#include <algorithm>
#include <float.h>
#include <math.h>
#include <new>
#include <stdlib.h>
#include <GenericNumberFormat.h>
#include <String.h>
#include <UnicodeChar.h>
#if __GNUC__ > 2
using std::max;
using std::min;
using std::nothrow;
#endif
static const int kMaxIntDigitCount = 20;
static const int kMaxFloatDigitCount = DBL_DIG + 2;
BGenericNumberFormat::Symbol::Symbol(const char *symbol)
: symbol(NULL),
length(0),
char_count(0)
{
SetTo(symbol);
}
BGenericNumberFormat::Symbol::~Symbol()
{
Unset();
}
status_t
BGenericNumberFormat::Symbol::SetTo(const char *symbol)
{
if (this->symbol) {
free(this->symbol);
length = 0;
char_count = 0;
}
if (symbol) {
this->symbol = strdup(symbol);
if (!this->symbol)
return B_NO_MEMORY;
length = strlen(this->symbol);
char_count = BUnicodeChar::UTF8StringLength(this->symbol);
}
return B_OK;
}
struct BGenericNumberFormat::SpecialNumberSymbols {
const Symbol *nan;
const Symbol *infinity;
const Symbol *negative_infinity;
};
class BGenericNumberFormat::GroupingInfo {
public:
GroupingInfo()
: fSeparators(NULL),
fSeparatorCount(0),
fSizes(NULL),
fSizeCount(0),
fSumSizes(NULL),
fSumSeparators(NULL)
{
}
GroupingInfo(const char **separators, int32 separatorCount,
const size_t *sizes, int32 sizeCount)
: fSeparators(NULL),
fSeparatorCount(0),
fSizes(NULL),
fSizeCount(0),
fSumSizes(NULL),
fSumSeparators(NULL)
{
SetTo(separators, separatorCount, sizes, sizeCount);
}
~GroupingInfo()
{
Unset();
}
status_t SetTo(const char **separators, int32 separatorCount,
const size_t *sizes, int32 sizeCount)
{
Unset();
if ((!separators && separatorCount <= 0)
|| (!sizes && sizeCount <= 0))
return B_OK;
fSeparators = new(nothrow) Symbol[separatorCount];
fSizes = new(nothrow) int32[sizeCount];
fSumSizes = new(nothrow) int32[sizeCount];
fSumSeparators = new(nothrow) Symbol*[separatorCount];
if (!fSeparators || !fSizes || !fSumSizes || !fSumSeparators) {
Unset();
return B_NO_MEMORY;
}
fSeparatorCount = separatorCount;
fSizeCount = sizeCount;
for (int i = 0; i < separatorCount; i++) {
status_t error = fSeparators[i].SetTo(separators[i]);
if (error != B_OK) {
Unset();
return error;
}
}
int32 sumSize = -1;
for (int32 i = 0; i < sizeCount; i++) {
fSizes[i] = (int32)sizes[i];
sumSize += fSizes[i];
fSumSizes[i] = sumSize;
fSumSeparators[i] = &fSeparators[min(i, fSeparatorCount)];
}
return B_OK;
}
void Unset()
{
if (fSeparators) {
delete[] fSeparators;
fSeparators = NULL;
}
fSeparatorCount = 0;
if (fSizes) {
delete[] fSizes;
fSizes = NULL;
}
fSizeCount = 0;
if (fSumSizes) {
delete[] fSumSizes;
fSumSizes = NULL;
}
if (fSumSeparators) {
delete[] fSumSeparators;
fSumSeparators = NULL;
}
}
const Symbol *SeparatorForDigit(int32 position) const
{
for (int i = fSizeCount - 1; i >= 0; i--) {
if (fSumSizes[i] <= position) {
if (fSumSizes[i] == position
|| (i == fSizeCount - 1
&& (position - fSumSizes[i]) % fSizes[i] == 0)) {
return fSumSeparators[i];
}
return NULL;
}
}
return NULL;
}
private:
Symbol *fSeparators;
int32 fSeparatorCount;
int32 *fSizes;
int32 fSizeCount;
int32 *fSumSizes;
Symbol **fSumSeparators;
};
class BGenericNumberFormat::SignSymbols {
public:
SignSymbols()
: fPlusPrefix(),
fMinusPrefix(),
fPadPlusPrefix(),
fNoForcePlusPrefix(),
fPlusSuffix(),
fMinusSuffix(),
fPadPlusSuffix(),
fNoForcePlusSuffix()
{
}
SignSymbols(const char *plusPrefix, const char *minusPrefix,
const char *padPlusPrefix, const char *noForcePlusPrefix,
const char *plusSuffix, const char *minusSuffix,
const char *padPlusSuffix, const char *noForcePlusSuffix)
: fPlusPrefix(plusPrefix),
fMinusPrefix(minusPrefix),
fPadPlusPrefix(padPlusPrefix),
fNoForcePlusPrefix(noForcePlusPrefix),
fPlusSuffix(plusSuffix),
fMinusSuffix(minusSuffix),
fPadPlusSuffix(padPlusSuffix),
fNoForcePlusSuffix(noForcePlusSuffix)
{
}
~SignSymbols()
{
}
status_t SetTo(const char *plusPrefix, const char *minusPrefix,
const char *padPlusPrefix, const char *noForcePlusPrefix,
const char *plusSuffix, const char *minusSuffix,
const char *padPlusSuffix, const char *noForcePlusSuffix)
{
status_t error = B_OK;
if (error == B_OK)
error = fPlusPrefix.SetTo(plusPrefix);
if (error == B_OK)
error = fMinusPrefix.SetTo(minusPrefix);
if (error == B_OK)
error = fPadPlusPrefix.SetTo(noForcePlusPrefix);
if (error == B_OK)
error = fNoForcePlusPrefix.SetTo(noForcePlusPrefix);
if (error == B_OK)
error = fPlusSuffix.SetTo(plusSuffix);
if (error == B_OK)
error = fMinusSuffix.SetTo(minusSuffix);
if (error == B_OK)
error = fPadPlusSuffix.SetTo(noForcePlusSuffix);
if (error == B_OK)
error = fNoForcePlusSuffix.SetTo(noForcePlusSuffix);
if (error != B_OK)
Unset();
return error;
}
void Unset()
{
fPlusPrefix.Unset();
fMinusPrefix.Unset();
fNoForcePlusPrefix.Unset();
fPadPlusPrefix.Unset();
fPlusSuffix.Unset();
fMinusSuffix.Unset();
fNoForcePlusSuffix.Unset();
fPadPlusSuffix.Unset();
}
const Symbol *PlusPrefix() const
{
return &fPlusPrefix;
}
const Symbol *MinusPrefix() const
{
return &fMinusPrefix;
}
const Symbol *PadPlusPrefix() const
{
return &fPadPlusPrefix;
}
const Symbol *NoForcePlusPrefix() const
{
return &fNoForcePlusPrefix;
}
const Symbol *PlusSuffix() const
{
return &fPlusSuffix;
}
const Symbol *MinusSuffix() const
{
return &fMinusSuffix;
}
const Symbol *PadPlusSuffix() const
{
return &fPadPlusSuffix;
}
const Symbol *NoForcePlusSuffix() const
{
return &fNoForcePlusSuffix;
}
private:
Symbol fPlusPrefix;
Symbol fMinusPrefix;
Symbol fPadPlusPrefix;
Symbol fNoForcePlusPrefix;
Symbol fPlusSuffix;
Symbol fMinusSuffix;
Symbol fPadPlusSuffix;
Symbol fNoForcePlusSuffix;
};
class BGenericNumberFormat::BufferWriter {
public:
BufferWriter(char *buffer = NULL, int32 bufferSize = 0)
{
SetTo(buffer, bufferSize);
}
void SetTo(char *buffer = NULL, int32 bufferSize = 0)
{
fBuffer = buffer;
fBufferSize = bufferSize;
fPosition = 0;
fCharCount = 0;
fDryRun = (!fBuffer || (fBufferSize == 0));
if (!fDryRun)
fBuffer[0] = '\0';
}
int32 StringLength() const
{
return fPosition;
}
int32 CharCount() const
{
return fCharCount;
}
bool IsOverflow() const
{
return (fPosition >= fBufferSize);
}
void Append(const char *bytes, size_t length, size_t charCount)
{
int32 newPosition = fPosition + length;
fDryRun |= (newPosition >= fBufferSize);
if (!fDryRun && length > 0) {
memcpy(fBuffer + fPosition, bytes, length);
fBuffer[newPosition] = '\0';
}
fPosition = newPosition;
fCharCount += charCount;
}
void Append(const Symbol &symbol)
{
Append(symbol.symbol, symbol.length, symbol.char_count);
}
void Append(const Symbol *symbol)
{
if (symbol)
Append(*symbol);
}
void Append(char c, int32 count)
{
if (count <= 0)
return;
int32 newPosition = fPosition + count;
fDryRun |= (newPosition >= fBufferSize);
if (!fDryRun && count > 0) {
memset(fBuffer + fPosition, c, count);
fBuffer[newPosition] = '\0';
}
fPosition = newPosition;
fCharCount += count;
}
void Append(const Symbol &symbol, int32 count)
{
if (count <= 0)
return;
int32 bytes = count * symbol.length;
int32 newPosition = fPosition + bytes;
fDryRun |= (newPosition >= fBufferSize);
if (!fDryRun && count > 0) {
for (int i = 0; i < count * symbol.length; i++)
fBuffer[i] = symbol.symbol[i % symbol.length];
fBuffer[newPosition] = '\0';
}
fPosition = newPosition;
fCharCount += count * symbol.char_count;
}
void Append(const Symbol *symbol, int32 count)
{
if (symbol)
Append(*symbol, count);
}
private:
char *fBuffer;
int32 fBufferSize;
int32 fPosition;
int32 fCharCount;
bool fDryRun;
};
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultDigitSymbols[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
};
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultFractionSeparator = ".";
static const char *kDefaultGroupingSeparators[] = { "," };
static const int32 kDefaultGroupingSeparatorCount
= sizeof(kDefaultGroupingSeparators) / sizeof(const char*);
static const char *kNoGroupingSeparators[] = { NULL };
static const int32 kNoGroupingSeparatorCount = 0;
static const size_t kDefaultGroupingSizes[] = { 3 };
static const int32 kDefaultGroupingSizeCount
= sizeof(kDefaultGroupingSizes) / sizeof(size_t);
static const size_t kNoGroupingSizes[] = { 0 };
static const int32 kNoGroupingSizeCount = 0;
const BGenericNumberFormat::GroupingInfo
BGenericNumberFormat::kDefaultGroupingInfo(
kDefaultGroupingSeparators, kDefaultGroupingSeparatorCount,
kDefaultGroupingSizes, kDefaultGroupingSizeCount
);
const BGenericNumberFormat::GroupingInfo
BGenericNumberFormat::kNoGroupingInfo(
kNoGroupingSeparators, kNoGroupingSeparatorCount,
kNoGroupingSizes, kNoGroupingSizeCount
);
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultExponentSymbol = "e";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultUpperCaseExponentSymbol = "E";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultNaNSymbol = "NaN";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultUpperCaseNaNSymbol = "NaN";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultInfinitySymbol = "infinity";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultUpperCaseInfinitySymbol = "INFINITY";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultNegativeInfinitySymbol = "-infinity";
const BGenericNumberFormat::Symbol
BGenericNumberFormat::kDefaultUpperCaseNegativeInfinitySymbol = "-INFINITY";
const BGenericNumberFormat::SignSymbols
BGenericNumberFormat::kDefaultSignSymbols(
"+", "-", " ", "",
"", "", "", ""
);
const BGenericNumberFormat::SignSymbols
BGenericNumberFormat::kDefaultMantissaSignSymbols(
"", "", "", "",
"", "", "", ""
);
const BGenericNumberFormat::SignSymbols
BGenericNumberFormat::kDefaultExponentSignSymbols(
"+", "-", " ", "",
"", "", "", ""
);
class BGenericNumberFormat::Integer {
public:
Integer(int64 number)
: fDigitCount(0),
fNegative(number < 0)
{
if (fNegative)
Init(0ULL - (uint64)number);
else
Init(number);
}
Integer(uint64 number)
: fDigitCount(0),
fNegative(false)
{
Init(number);
}
int DigitCount() const
{
return fDigitCount;
}
bool IsNegative() const
{
return fNegative;
}
char *ToString(char *str) const
{
if (fDigitCount == 0) {
str[0] = '0';
str[1] = '\0';
} else if (fNegative) {
str[0] = '-';
for (int i = 0; i < fDigitCount; i++)
str[i + 1] = '0' + fDigits[fDigitCount - i - 1];
str[fDigitCount + 1] = '\0';
} else {
for (int i = 0; i < fDigitCount; i++)
str[i] = '0' + fDigits[fDigitCount - i - 1];
str[fDigitCount] = '\0';
}
return str;
}
void Format(BufferWriter &writer, const Symbol *digitSymbols,
const SignSymbols *signSymbols,
number_format_sign_policy signPolicy,
const GroupingInfo *groupingInfo, int32 minDigits) const
{
const Symbol *suffix = NULL;
if (fNegative) {
writer.Append(signSymbols->MinusPrefix());
suffix = signSymbols->MinusSuffix();
} else {
switch (signPolicy) {
case B_USE_NEGATIVE_SIGN_ONLY:
writer.Append(signSymbols->NoForcePlusPrefix());
suffix = signSymbols->NoForcePlusSuffix();
break;
case B_USE_SPACE_FOR_POSITIVE_SIGN:
writer.Append(signSymbols->PadPlusPrefix());
suffix = signSymbols->PadPlusSuffix();
break;
case B_USE_POSITIVE_SIGN:
writer.Append(signSymbols->PlusPrefix());
suffix = signSymbols->PlusSuffix();
break;
}
}
if (fDigitCount == 0 && minDigits < 1) {
writer.Append(digitSymbols[0]);
} else {
if (groupingInfo) {
int32 digitCount = max(fDigitCount, minDigits);
for (int i = minDigits - 1; i >= fDigitCount; i--) {
if (i != digitCount - 1)
writer.Append(groupingInfo->SeparatorForDigit(i));
writer.Append(digitSymbols[0]);
}
for (int i = fDigitCount - 1; i >= 0; i--) {
if (i != digitCount - 1)
writer.Append(groupingInfo->SeparatorForDigit(i));
writer.Append(digitSymbols[fDigits[i]]);
}
} else {
if (fDigitCount < minDigits)
writer.Append(digitSymbols, minDigits - fDigitCount);
for (int i = fDigitCount - 1; i >= 0; i--)
writer.Append(digitSymbols[fDigits[i]]);
}
}
writer.Append(suffix);
}
private:
void Init(uint64 number)
{
fDigitCount = 0;
while (number) {
fDigits[fDigitCount] = number % 10;
number /= 10;
fDigitCount++;
}
}
private:
uchar fDigits[kMaxIntDigitCount];
int32 fDigitCount;
bool fNegative;
};
class BGenericNumberFormat::Float {
public:
Float(double number)
: fNegative(signbit(number)),
fClass(fpclassify(number)),
fExponent(0),
fDigitCount(0)
{
switch (fClass) {
case FP_NAN:
case FP_INFINITE:
return;
case FP_ZERO:
fDigits[0] = 0;
fDigitCount = 1;
return;
case FP_NORMAL:
case FP_SUBNORMAL:
break;
}
if (fNegative)
number = -number;
fExponent = (int)ceil(log10(number));
int shiftBy = kMaxFloatDigitCount - fExponent - 1;
double mantissa = number * pow(10, shiftBy / 2);
mantissa *= pow(10, shiftBy - shiftBy / 2);
int32 firstNonNull = -1;
for (int i = 0; i < kMaxFloatDigitCount; i++) {
char digit = (char)fmod(mantissa, 10);
if (firstNonNull < 0 && digit > 0)
firstNonNull = i;
if (firstNonNull >= 0)
fDigits[i - firstNonNull] = digit;
mantissa /= 10;
}
if (firstNonNull >= 0)
fDigitCount = kMaxFloatDigitCount - firstNonNull;
else
fDigitCount = 0;
while (fDigitCount > 0 && fDigits[fDigitCount - 1] == 0) {
fDigitCount--;
fExponent--;
}
if (fDigitCount == 0) {
fExponent = 0;
fDigits[0] = 0;
fDigitCount = 1;
}
}
void Format(BufferWriter &writer, const Symbol *digitSymbols,
const SpecialNumberSymbols *specialNumbers,
const Symbol *fractionSeparator,
const Symbol *exponentSymbol,
const SignSymbols *signSymbols,
const SignSymbols *mantissaSignSymbols,
const SignSymbols *exponentSignSymbols,
float_format_type formatType,
number_format_sign_policy signPolicy,
const GroupingInfo *groupingInfo,
int32 minIntegerDigits, int32 minFractionDigits,
int32 maxFractionDigits, bool forceFractionSeparator,
bool keepTrailingFractionZeros) const
{
switch (fClass) {
case FP_NAN:
writer.Append(specialNumbers->nan);
return;
case FP_INFINITE:
if (fNegative)
writer.Append(specialNumbers->negative_infinity);
else
writer.Append(specialNumbers->infinity);
return;
case FP_ZERO:
case FP_NORMAL:
case FP_SUBNORMAL:
break;
}
bool scientific = false;
switch (formatType) {
case B_FIXED_POINT_FLOAT_FORMAT:
break;
case B_SCIENTIFIC_FLOAT_FORMAT:
scientific = true;
break;
case B_AUTO_FLOAT_FORMAT:
scientific = (fExponent >= maxFractionDigits
|| fExponent < -4);
break;
}
if (scientific) {
FormatScientific(writer, digitSymbols, fractionSeparator,
exponentSymbol, signSymbols, mantissaSignSymbols,
exponentSignSymbols, signPolicy, minIntegerDigits,
minFractionDigits, maxFractionDigits,
forceFractionSeparator, keepTrailingFractionZeros);
} else {
FormatFixedPoint(writer, digitSymbols, fractionSeparator,
signSymbols, mantissaSignSymbols, signPolicy, groupingInfo,
minIntegerDigits, minFractionDigits, maxFractionDigits,
forceFractionSeparator, keepTrailingFractionZeros);
}
}
void FormatScientific(BufferWriter &writer, const Symbol *digitSymbols,
const Symbol *fractionSeparator,
const Symbol *exponentSymbol,
const SignSymbols *signSymbols,
const SignSymbols *mantissaSignSymbols,
const SignSymbols *exponentSignSymbols,
number_format_sign_policy signPolicy,
int32 minIntegerDigits, int32 minFractionDigits,
int32 maxFractionDigits, bool forceFractionSeparator,
bool keepTrailingFractionZeros) const
{
const Symbol *suffix = NULL;
const Symbol *mantissaSuffix = NULL;
if (fNegative) {
writer.Append(signSymbols->MinusPrefix());
writer.Append(mantissaSignSymbols->MinusPrefix());
suffix = signSymbols->MinusSuffix();
mantissaSuffix = mantissaSignSymbols->MinusSuffix();
} else {
switch (signPolicy) {
case B_USE_NEGATIVE_SIGN_ONLY:
writer.Append(signSymbols->NoForcePlusPrefix());
writer.Append(mantissaSignSymbols->NoForcePlusPrefix());
suffix = signSymbols->NoForcePlusSuffix();
mantissaSuffix
= mantissaSignSymbols->NoForcePlusSuffix();
break;
case B_USE_SPACE_FOR_POSITIVE_SIGN:
writer.Append(signSymbols->PadPlusPrefix());
writer.Append(mantissaSignSymbols->PadPlusPrefix());
suffix = signSymbols->PadPlusSuffix();
mantissaSuffix = mantissaSignSymbols->PadPlusSuffix();
break;
case B_USE_POSITIVE_SIGN:
writer.Append(signSymbols->PlusPrefix());
writer.Append(mantissaSignSymbols->PlusPrefix());
suffix = signSymbols->PlusSuffix();
mantissaSuffix = mantissaSignSymbols->PlusSuffix();
break;
}
}
int32 exponent = fExponent;
char digits[kMaxFloatDigitCount];
int32 integerDigits = max(minIntegerDigits, 1L);
int32 fractionDigits
= max(fDigitCount - integerDigits, minFractionDigits);
fractionDigits = min(fractionDigits, maxFractionDigits);
int32 digitCount
= _Round(digits, integerDigits + fractionDigits, exponent);
fractionDigits = digitCount - integerDigits;
if (keepTrailingFractionZeros)
fractionDigits = max(fractionDigits, minFractionDigits);
fractionDigits = min(fractionDigits, maxFractionDigits);
int32 existingIntegerDigits = min(integerDigits, digitCount);
for (int i = 0; i < existingIntegerDigits; i++)
writer.Append(digitSymbols[(int)digits[digitCount - i - 1]]);
if (existingIntegerDigits < integerDigits) {
writer.Append(digitSymbols,
integerDigits - existingIntegerDigits);
}
if (!_IsZero(digits, digitCount))
exponent -= integerDigits - 1;
if (fractionDigits > 0 || forceFractionSeparator)
writer.Append(fractionSeparator);
int32 existingFractionDigits
= min(digitCount - integerDigits, fractionDigits);
for (int i = existingFractionDigits - 1; i >= 0; i--)
writer.Append(digitSymbols[(int)digits[i]]);
if (fractionDigits > existingFractionDigits) {
writer.Append(digitSymbols,
fractionDigits - existingFractionDigits);
}
writer.Append(mantissaSuffix);
writer.Append(exponentSymbol);
Integer(static_cast<int64>(exponent)).Format(writer, digitSymbols,
exponentSignSymbols, B_USE_POSITIVE_SIGN, &kNoGroupingInfo, 2);
writer.Append(suffix);
}
void FormatFixedPoint(BufferWriter &writer, const Symbol *digitSymbols,
const Symbol *fractionSeparator,
const SignSymbols *signSymbols,
const SignSymbols *mantissaSignSymbols,
number_format_sign_policy signPolicy,
const GroupingInfo *groupingInfo,
int32 minIntegerDigits, int32 minFractionDigits,
int32 maxFractionDigits, bool forceFractionSeparator,
bool keepTrailingFractionZeros) const
{
const Symbol *suffix = NULL;
const Symbol *mantissaSuffix = NULL;
if (fNegative) {
writer.Append(signSymbols->MinusPrefix());
writer.Append(mantissaSignSymbols->MinusPrefix());
suffix = signSymbols->MinusSuffix();
mantissaSuffix = mantissaSignSymbols->MinusSuffix();
} else {
switch (signPolicy) {
case B_USE_NEGATIVE_SIGN_ONLY:
writer.Append(signSymbols->NoForcePlusPrefix());
writer.Append(mantissaSignSymbols->NoForcePlusPrefix());
suffix = signSymbols->NoForcePlusSuffix();
mantissaSuffix
= mantissaSignSymbols->NoForcePlusSuffix();
break;
case B_USE_SPACE_FOR_POSITIVE_SIGN:
writer.Append(signSymbols->PadPlusPrefix());
writer.Append(mantissaSignSymbols->PadPlusPrefix());
suffix = signSymbols->PadPlusSuffix();
mantissaSuffix = mantissaSignSymbols->PadPlusSuffix();
break;
case B_USE_POSITIVE_SIGN:
writer.Append(signSymbols->PlusPrefix());
writer.Append(mantissaSignSymbols->PlusPrefix());
suffix = signSymbols->PlusSuffix();
mantissaSuffix = mantissaSignSymbols->PlusSuffix();
break;
}
}
int32 exponent = fExponent;
char digits[kMaxFloatDigitCount];
int32 integerDigits = max(minIntegerDigits, exponent + 1L);
int32 fractionDigits
= max(fDigitCount - integerDigits, minFractionDigits);
fractionDigits = min(fractionDigits, maxFractionDigits);
int32 digitCount
= _Round(digits, integerDigits + fractionDigits, exponent);
fractionDigits = digitCount - integerDigits;
if (keepTrailingFractionZeros)
fractionDigits = max(fractionDigits, minFractionDigits);
fractionDigits = min(fractionDigits, maxFractionDigits);
int32 existingIntegerDigits = min(integerDigits, exponent + 1);
existingIntegerDigits = max(existingIntegerDigits, 0L);
if (groupingInfo) {
for (int i = integerDigits - 1;
i >= existingIntegerDigits;
i--) {
if (i != integerDigits - 1)
writer.Append(groupingInfo->SeparatorForDigit(i));
writer.Append(digitSymbols[0]);
}
for (int i = existingIntegerDigits - 1; i >= 0; i--) {
if (i != integerDigits - 1)
writer.Append(groupingInfo->SeparatorForDigit(i));
writer.Append(digitSymbols[(int)digits[
digitCount - existingIntegerDigits + i]]);
}
} else {
if (existingIntegerDigits < integerDigits) {
writer.Append(digitSymbols[0],
integerDigits - existingIntegerDigits);
}
for (int i = 0; i < existingIntegerDigits; i++) {
writer.Append(
digitSymbols[(int)digits[digitCount - i - 1]]);
}
}
if (fractionDigits > 0 || forceFractionSeparator)
writer.Append(fractionSeparator);
int32 existingFractionDigits
= min(digitCount - existingIntegerDigits, fractionDigits);
for (int i = existingFractionDigits - 1; i >= 0; i--)
writer.Append(digitSymbols[(int)digits[i]]);
if (fractionDigits > existingFractionDigits) {
writer.Append(digitSymbols,
fractionDigits - existingFractionDigits);
}
writer.Append(mantissaSuffix);
writer.Append(suffix);
}
private:
int32 _Round(char *digits, int32 count, int32 &exponent) const
{
if (count > fDigitCount)
count = fDigitCount;
int firstNonNull = -1;
bool carry = false;
if (count < fDigitCount)
carry = (fDigits[fDigitCount - count - 1] >= 5);
for (int i = 0; i < count; i++) {
char digit = fDigits[fDigitCount - count + i];
if (carry) {
digit++;
if (digit == 10)
digit = 0;
else
carry = false;
}
if (firstNonNull < 0 && digit > 0)
firstNonNull = i;
if (firstNonNull >= 0)
digits[i - firstNonNull] = digit;
}
if (firstNonNull < 0) {
if (carry) {
digits[0] = 1;
exponent++;
} else {
exponent = 0;
digits[0] = 0;
}
count = 1;
} else
count -= firstNonNull;
return count;
}
static bool _IsZero(const char *digits, int32 digitCount)
{
return (digitCount == 1 && digits[0] == 0);
}
private:
bool fNegative;
int fClass;
int32 fExponent;
char fDigits[kMaxFloatDigitCount];
int32 fDigitCount;
};
BGenericNumberFormat::BGenericNumberFormat()
: fIntegerParameters(),
fFloatParameters(),
fDigitSymbols(NULL),
fFractionSeparator(NULL),
fGroupingInfo(NULL),
fExponentSymbol(NULL),
fUpperCaseExponentSymbol(NULL),
fNaNSymbol(NULL),
fUpperCaseNaNSymbol(NULL),
fInfinitySymbol(NULL),
fUpperCaseInfinitySymbol(NULL),
fNegativeInfinitySymbol(NULL),
fUpperCaseNegativeInfinitySymbol(NULL),
fSignSymbols(NULL),
fMantissaSignSymbols(NULL),
fExponentSignSymbols(NULL)
{
}
BGenericNumberFormat::~BGenericNumberFormat()
{
delete fSignSymbols;
delete fMantissaSignSymbols;
delete fExponentSignSymbols;
}
status_t
BGenericNumberFormat::FormatInteger(
const BIntegerFormatParameters *parameters, int64 number, BString *buffer,
format_field_position *positions, int32 positionCount, int32 *fieldCount,
bool allFieldPositions) const
{
if (!buffer)
return B_BAD_VALUE;
char localBuffer[1024];
status_t error = FormatInteger(parameters, number, localBuffer,
sizeof(localBuffer), positions, positionCount, fieldCount,
allFieldPositions);
if (error == B_OK) {
buffer->Append(localBuffer);
}
return error;
}
status_t
BGenericNumberFormat::FormatInteger(
const BIntegerFormatParameters *parameters, uint64 number, BString *buffer,
format_field_position *positions, int32 positionCount, int32 *fieldCount,
bool allFieldPositions) const
{
if (!buffer)
return B_BAD_VALUE;
char localBuffer[1024];
status_t error = FormatInteger(parameters, number, localBuffer,
sizeof(localBuffer), positions, positionCount, fieldCount,
allFieldPositions);
if (error == B_OK) {
buffer->Append(localBuffer);
}
return error;
}
status_t
BGenericNumberFormat::FormatInteger(
const BIntegerFormatParameters *parameters, int64 number, char *buffer,
size_t bufferSize, format_field_position *positions, int32 positionCount,
int32 *fieldCount, bool allFieldPositions) const
{
Integer integer(number);
return FormatInteger(parameters, integer, buffer, bufferSize, positions,
positionCount, fieldCount, allFieldPositions);
}
status_t
BGenericNumberFormat::FormatInteger(
const BIntegerFormatParameters *parameters, uint64 number, char *buffer,
size_t bufferSize, format_field_position *positions, int32 positionCount,
int32 *fieldCount, bool allFieldPositions) const
{
Integer integer(number);
return FormatInteger(parameters, integer, buffer, bufferSize, positions,
positionCount, fieldCount, allFieldPositions);
}
status_t
BGenericNumberFormat::FormatFloat(const BFloatFormatParameters *parameters,
double number, BString *buffer, format_field_position *positions,
int32 positionCount, int32 *fieldCount, bool allFieldPositions) const
{
if (!buffer)
return B_BAD_VALUE;
char localBuffer[1024];
status_t error = FormatFloat(parameters, number, localBuffer,
sizeof(localBuffer), positions, positionCount, fieldCount,
allFieldPositions);
if (error == B_OK) {
buffer->Append(localBuffer);
}
return error;
}
status_t
BGenericNumberFormat::FormatFloat(const BFloatFormatParameters *parameters,
double number, char *buffer, size_t bufferSize,
format_field_position *positions, int32 positionCount, int32 *fieldCount,
bool allFieldPositions) const
{
if (!parameters)
parameters = DefaultFloatFormatParameters();
if (bufferSize <= parameters->FormatWidth())
return EOVERFLOW;
Float floatNumber(number);
const GroupingInfo *groupingInfo = NULL;
if (parameters->UseGrouping())
groupingInfo = GetGroupingInfo();
bool upperCase = parameters->UseUpperCase();
SpecialNumberSymbols specialSymbols;
GetSpecialNumberSymbols(upperCase, &specialSymbols);
BufferWriter writer;
floatNumber.Format(writer, DigitSymbols(), &specialSymbols,
FractionSeparator(), ExponentSymbol(), GetSignSymbols(),
MantissaSignSymbols(), ExponentSignSymbols(),
parameters->FloatFormatType(), parameters->SignPolicy(), groupingInfo,
parameters->MinimalIntegerDigits(), parameters->MinimalFractionDigits(),
parameters->MaximalFractionDigits(),
parameters->AlwaysUseFractionSeparator(),
parameters->KeepTrailingFractionZeros());
int32 stringLength = writer.StringLength();
int32 charCount = writer.CharCount();
int32 padding = max(0L, (int32)parameters->FormatWidth() - charCount);
if ((int32)bufferSize <= stringLength + padding)
return EOVERFLOW;
writer.SetTo(buffer, bufferSize);
if (parameters->Alignment() == B_ALIGN_FORMAT_RIGHT && padding > 0)
writer.Append(' ', padding);
floatNumber.Format(writer, DigitSymbols(), &specialSymbols,
FractionSeparator(), ExponentSymbol(), GetSignSymbols(),
MantissaSignSymbols(), ExponentSignSymbols(),
parameters->FloatFormatType(), parameters->SignPolicy(), groupingInfo,
parameters->MinimalIntegerDigits(), parameters->MinimalFractionDigits(),
parameters->MaximalFractionDigits(),
parameters->AlwaysUseFractionSeparator(),
parameters->KeepTrailingFractionZeros());
if (parameters->Alignment() == B_ALIGN_FORMAT_LEFT && padding > 0)
writer.Append(' ', padding);
return B_OK;
}
status_t
BGenericNumberFormat::SetDefaultIntegerFormatParameters(
const BIntegerFormatParameters *parameters)
{
if (!parameters)
return B_BAD_VALUE;
fIntegerParameters = *parameters;
return B_OK;
}
BIntegerFormatParameters *
BGenericNumberFormat::DefaultIntegerFormatParameters()
{
return &fIntegerParameters;
}
const BIntegerFormatParameters *
BGenericNumberFormat::DefaultIntegerFormatParameters() const
{
return &fIntegerParameters;
}
status_t
BGenericNumberFormat::SetDefaultFloatFormatParameters(
const BFloatFormatParameters *parameters)
{
if (!parameters)
return B_BAD_VALUE;
fFloatParameters = *parameters;
return B_OK;
}
BFloatFormatParameters *
BGenericNumberFormat::DefaultFloatFormatParameters()
{
return &fFloatParameters;
}
const BFloatFormatParameters *
BGenericNumberFormat::DefaultFloatFormatParameters() const
{
return &fFloatParameters;
}
status_t
BGenericNumberFormat::SetDigitSymbols(const char **digits)
{
if (digits) {
for (int i = 0; i < 10; i++) {
if (!digits[i])
return B_BAD_VALUE;
}
}
if (fDigitSymbols) {
delete[] fDigitSymbols;
fDigitSymbols = NULL;
}
if (digits) {
fDigitSymbols = new(nothrow) Symbol[10];
if (!fDigitSymbols)
return B_NO_MEMORY;
for (int i = 0; i < 10; i++) {
status_t error = fDigitSymbols[i].SetTo(digits[i]);
if (error != B_OK) {
SetDigitSymbols(NULL);
return error;
}
}
}
return B_OK;
}
status_t
BGenericNumberFormat::SetFractionSeparator(const char *decimalSeparator)
{
return _SetSymbol(&fFractionSeparator, decimalSeparator);
}
status_t
BGenericNumberFormat::SetGroupingInfo(const char **groupingSeparators,
size_t separatorCount, size_t *groupingSizes, size_t sizeCount)
{
if (groupingSeparators && separatorCount > 0 && groupingSizes
&& sizeCount) {
for (int i = 0; i < (int)separatorCount; i++) {
if (!groupingSeparators[i])
return B_BAD_VALUE;
}
}
if (fGroupingInfo) {
delete fGroupingInfo;
fGroupingInfo = NULL;
}
if (groupingSeparators && separatorCount > 0 && groupingSizes
&& sizeCount) {
fGroupingInfo = new GroupingInfo;
if (!fGroupingInfo)
return B_NO_MEMORY;
status_t error = fGroupingInfo->SetTo(groupingSeparators,
separatorCount, groupingSizes, sizeCount);
if (error != B_OK) {
delete fGroupingInfo;
fGroupingInfo = NULL;
return error;
}
}
return B_OK;
}
status_t
BGenericNumberFormat::SetExponentSymbol(const char *exponentSymbol,
const char *upperCaseExponentSymbol)
{
status_t error = _SetSymbol(&fExponentSymbol, exponentSymbol);
if (error == B_OK)
error = _SetSymbol(&fUpperCaseExponentSymbol, upperCaseExponentSymbol);
if (error != B_OK)
SetExponentSymbol(NULL, NULL);
return error;
}
status_t
BGenericNumberFormat::SetSpecialNumberSymbols(const char *nan,
const char *infinity, const char *negativeInfinity,
const char *upperCaseNaN, const char *upperCaseInfinity,
const char *upperCaseNegativeInfinity)
{
status_t error = _SetSymbol(&fNaNSymbol, nan);
if (error == B_OK)
error = _SetSymbol(&fInfinitySymbol, infinity);
if (error == B_OK)
error = _SetSymbol(&fNegativeInfinitySymbol, negativeInfinity);
if (error == B_OK)
error = _SetSymbol(&fUpperCaseNaNSymbol, upperCaseNaN);
if (error == B_OK)
error = _SetSymbol(&fUpperCaseInfinitySymbol, upperCaseInfinity);
if (error == B_OK) {
error = _SetSymbol(&fUpperCaseNegativeInfinitySymbol,
upperCaseNegativeInfinity);
}
if (error != B_OK)
SetSpecialNumberSymbols(NULL, NULL, NULL, NULL, NULL, NULL);
return error;
}
status_t
BGenericNumberFormat::SetSignSymbols(const char *plusPrefix,
const char *minusPrefix, const char *padPlusPrefix,
const char *noForcePlusPrefix, const char *plusSuffix,
const char *minusSuffix, const char *padPlusSuffix,
const char *noForcePlusSuffix)
{
if (!fSignSymbols) {
fSignSymbols = new(nothrow) SignSymbols;
if (!fSignSymbols)
return B_NO_MEMORY;
}
return fSignSymbols->SetTo(plusPrefix, minusPrefix, padPlusPrefix,
noForcePlusPrefix, plusSuffix, minusSuffix, padPlusSuffix,
noForcePlusSuffix);
}
status_t
BGenericNumberFormat::SetMantissaSignSymbols(const char *plusPrefix,
const char *minusPrefix, const char *padPlusPrefix,
const char *noForcePlusPrefix, const char *plusSuffix,
const char *minusSuffix, const char *padPlusSuffix,
const char *noForcePlusSuffix)
{
if (!fMantissaSignSymbols) {
fMantissaSignSymbols = new(nothrow) SignSymbols;
if (!fMantissaSignSymbols)
return B_NO_MEMORY;
}
return fMantissaSignSymbols->SetTo(plusPrefix, minusPrefix, padPlusPrefix,
noForcePlusPrefix, plusSuffix, minusSuffix, padPlusSuffix,
noForcePlusSuffix);
}
status_t
BGenericNumberFormat::SetExponentSignSymbols(const char *plusPrefix,
const char *minusPrefix, const char *plusSuffix, const char *minusSuffix)
{
if (!fExponentSignSymbols) {
fExponentSignSymbols = new(nothrow) SignSymbols;
if (!fExponentSignSymbols)
return B_NO_MEMORY;
}
return fExponentSignSymbols->SetTo(plusPrefix, minusPrefix, plusPrefix,
plusPrefix, plusSuffix, minusSuffix, plusSuffix, plusSuffix);
}
status_t
BGenericNumberFormat::FormatInteger(
const BIntegerFormatParameters *parameters, const Integer &integer,
char *buffer, size_t bufferSize, format_field_position *positions,
int32 positionCount, int32 *fieldCount, bool allFieldPositions) const
{
if (!parameters)
parameters = DefaultIntegerFormatParameters();
if (bufferSize <= parameters->FormatWidth())
return EOVERFLOW;
const GroupingInfo *groupingInfo = NULL;
if (parameters->UseGrouping())
groupingInfo = GetGroupingInfo();
BufferWriter writer;
integer.Format(writer, DigitSymbols(),
GetSignSymbols(), parameters->SignPolicy(), groupingInfo,
parameters->MinimalIntegerDigits());
int32 stringLength = writer.StringLength();
int32 charCount = writer.CharCount();
int32 padding = max(0L, (int32)parameters->FormatWidth() - charCount);
if ((int32)bufferSize <= stringLength + padding)
return EOVERFLOW;
writer.SetTo(buffer, bufferSize);
if (parameters->Alignment() == B_ALIGN_FORMAT_RIGHT && padding > 0)
writer.Append(' ', padding);
integer.Format(writer, DigitSymbols(),
GetSignSymbols(), parameters->SignPolicy(), groupingInfo,
parameters->MinimalIntegerDigits());
if (parameters->Alignment() == B_ALIGN_FORMAT_LEFT && padding > 0)
writer.Append(' ', padding);
return B_OK;
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::DigitSymbols() const
{
return (fDigitSymbols ? fDigitSymbols : kDefaultDigitSymbols);
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::FractionSeparator() const
{
return (fFractionSeparator ? fFractionSeparator
: &kDefaultFractionSeparator);
}
const BGenericNumberFormat::GroupingInfo *
BGenericNumberFormat::GetGroupingInfo() const
{
return (fGroupingInfo ? fGroupingInfo : &kDefaultGroupingInfo);
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::ExponentSymbol(bool upperCase) const
{
if (fExponentSymbol) {
return (upperCase && fUpperCaseExponentSymbol ? fUpperCaseExponentSymbol
: fExponentSymbol);
}
return (upperCase ? &kDefaultUpperCaseExponentSymbol
: &kDefaultExponentSymbol);
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::NaNSymbol(bool upperCase) const
{
if (fNaNSymbol) {
return (upperCase && fUpperCaseNaNSymbol ? fUpperCaseNaNSymbol
: fNaNSymbol);
}
return (upperCase ? &kDefaultUpperCaseNaNSymbol
: &kDefaultNaNSymbol);
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::InfinitySymbol(bool upperCase) const
{
if (fInfinitySymbol) {
return (upperCase && fUpperCaseInfinitySymbol ? fUpperCaseInfinitySymbol
: fInfinitySymbol);
}
return (upperCase ? &kDefaultUpperCaseInfinitySymbol
: &kDefaultInfinitySymbol);
}
const BGenericNumberFormat::Symbol *
BGenericNumberFormat::NegativeInfinitySymbol(bool upperCase) const
{
if (fNegativeInfinitySymbol) {
return (upperCase && fUpperCaseNegativeInfinitySymbol
? fUpperCaseNegativeInfinitySymbol : fNegativeInfinitySymbol);
}
return (upperCase ? &kDefaultUpperCaseNegativeInfinitySymbol
: &kDefaultNegativeInfinitySymbol);
}
void
BGenericNumberFormat::GetSpecialNumberSymbols(bool upperCase,
SpecialNumberSymbols *symbols) const
{
symbols->nan = NaNSymbol(upperCase);
symbols->infinity = InfinitySymbol(upperCase);
symbols->negative_infinity = NegativeInfinitySymbol(upperCase);
}
const BGenericNumberFormat::SignSymbols *
BGenericNumberFormat::GetSignSymbols() const
{
return (fSignSymbols ? fSignSymbols : &kDefaultSignSymbols);
}
const BGenericNumberFormat::SignSymbols *
BGenericNumberFormat::MantissaSignSymbols() const
{
return (fMantissaSignSymbols ? fMantissaSignSymbols
: &kDefaultMantissaSignSymbols);
}
const BGenericNumberFormat::SignSymbols *
BGenericNumberFormat::ExponentSignSymbols() const
{
return (fExponentSignSymbols ? fExponentSignSymbols
: &kDefaultExponentSignSymbols);
}
status_t
BGenericNumberFormat::_SetSymbol(Symbol **symbol, const char *str)
{
if (!str) {
if (*symbol) {
delete *symbol;
symbol = NULL;
}
} else {
if (!*symbol) {
*symbol = new(nothrow) Symbol;
if (!*symbol)
return B_NO_MEMORY;
}
status_t error = (*symbol)->SetTo(str);
if (error != B_OK) {
delete *symbol;
*symbol = NULL;
return B_NO_MEMORY;
}
}
return B_OK;
}