diff --git a/format.cc b/format.cc index 03c10e40..13f9cf23 100644 --- a/format.cc +++ b/format.cc @@ -51,3 +51,561 @@ void fmt::internal::ReportUnknownType(char code, const char *type) { fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}") << static_cast(code) << type)); } + + +// Fills the padding around the content and returns the pointer to the +// content area. +template +typename fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding( + CharPtr buffer, unsigned total_size, std::size_t content_size, char fill) { + std::size_t padding = total_size - content_size; + std::size_t left_padding = padding / 2; + std::fill_n(buffer, left_padding, fill); + buffer += left_padding; + CharPtr content = buffer; + std::fill_n(buffer + content_size, padding - left_padding, fill); + return content; +} + +template +void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, uint64_t value, unsigned num_digits) { + --num_digits; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + buffer[num_digits] = internal::DIGITS[index + 1]; + buffer[num_digits - 1] = internal::DIGITS[index]; + num_digits -= 2; + } + if (value < 10) { + *buffer = static_cast('0' + value); + return; + } + unsigned index = static_cast(value * 2); + buffer[1] = internal::DIGITS[index + 1]; + buffer[0] = internal::DIGITS[index]; +} + +template +typename fmt::BasicWriter::CharPtr + fmt::BasicWriter::PrepareFilledBuffer( + unsigned size, const AlignSpec &spec, char sign) { + unsigned width = spec.width(); + if (width <= size) { + CharPtr p = GrowBuffer(size); + *p = sign; + return p + size - 1; + } + CharPtr p = GrowBuffer(width); + CharPtr end = p + width; + Alignment align = spec.align(); + if (align == ALIGN_LEFT) { + *p = sign; + p += size; + std::fill(p, end, spec.fill()); + } else if (align == ALIGN_CENTER) { + p = FillPadding(p, width, size, spec.fill()); + *p = sign; + p += size; + } else { + if (align == ALIGN_NUMERIC) { + if (sign) { + *p++ = sign; + --size; + } + } else { + *(end - size) = sign; + } + std::fill(p, end - size, spec.fill()); + p = end; + } + return p - 1; +} + +template +template +void fmt::BasicWriter::FormatDouble( + T value, const FormatSpec &spec, int precision) { + // Check type. + char type = spec.type(); + bool upper = false; + switch (type) { + case 0: + type = 'g'; + break; + case 'e': case 'f': case 'g': + break; + case 'F': +#ifdef _MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + // Fall through. + case 'E': case 'G': + upper = true; + break; + default: + internal::ReportUnknownType(type, "double"); + break; + } + + char sign = 0; + // Use SignBit instead of value < 0 because the latter is always + // false for NaN. + if (internal::SignBit(value)) { + sign = '-'; + value = -value; + } else if (spec.sign_flag()) { + sign = spec.plus_flag() ? '+' : ' '; + } + + if (value != value) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --size; + ++nan; + } + CharPtr out = FormatString(nan, size, spec); + if (sign) + *out = sign; + return; + } + + if (internal::IsInf(value)) { + // Format infinity ourselves because sprintf's output is not consistent + // across platforms. + std::size_t size = 4; + const char *inf = upper ? " INF" : " inf"; + if (!sign) { + --size; + ++inf; + } + CharPtr out = FormatString(inf, size, spec); + if (sign) + *out = sign; + return; + } + + std::size_t offset = buffer_.size(); + unsigned width = spec.width(); + if (sign) { + buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); + if (width > 0) + --width; + ++offset; + } + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + Char format[MAX_FORMAT_SIZE]; + Char *format_ptr = format; + *format_ptr++ = '%'; + unsigned width_for_sprintf = width; + if (spec.hash_flag()) + *format_ptr++ = '#'; + if (spec.align() == ALIGN_CENTER) { + width_for_sprintf = 0; + } else { + if (spec.align() == ALIGN_LEFT) + *format_ptr++ = '-'; + if (width != 0) + *format_ptr++ = '*'; + } + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (internal::IsLongDouble::VALUE) + *format_ptr++ = 'L'; + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + for (;;) { + std::size_t size = buffer_.capacity() - offset; + Char *start = &buffer_[offset]; + int n = internal::CharTraits::FormatFloat( + start, size, format, width_for_sprintf, precision, value); + if (n >= 0 && offset + n < buffer_.capacity()) { + if (sign) { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') { + *(start - 1) = sign; + sign = 0; + } else { + *(start - 1) = spec.fill(); + } + ++n; + } + if (spec.align() == ALIGN_CENTER && + spec.width() > static_cast(n)) { + unsigned width = spec.width(); + CharPtr p = GrowBuffer(width); + std::copy(p, p + n, p + (width - n) / 2); + FillPadding(p, spec.width(), n, spec.fill()); + return; + } + if (spec.fill() != ' ' || sign) { + while (*start == ' ') + *start++ = spec.fill(); + if (sign) + *(start - 1) = sign; + } + GrowBuffer(n); + return; + } + buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); + } +} + +// Throws Exception(message) if format contains '}', otherwise throws +// FormatError reporting unmatched '{'. The idea is that unmatched '{' +// should override other errors. +template +void fmt::BasicFormatter::ReportError( + const Char *s, StringRef message) const { + for (int num_open_braces = num_open_braces_; *s; ++s) { + if (*s == '{') { + ++num_open_braces; + } else if (*s == '}') { + if (--num_open_braces == 0) + throw fmt::FormatError(message); + } + } + throw fmt::FormatError("unmatched '{' in format"); +} + +// Parses an unsigned integer advancing s to the end of the parsed input. +// This function assumes that the first character of s is a digit. +template +unsigned fmt::BasicFormatter::ParseUInt(const Char *&s) const { + assert('0' <= *s && *s <= '9'); + unsigned value = 0; + do { + unsigned new_value = value * 10 + (*s++ - '0'); + if (new_value < value) // Check if value wrapped around. + ReportError(s, "number is too big in format"); + value = new_value; + } while ('0' <= *s && *s <= '9'); + return value; +} + +template +inline const typename fmt::BasicFormatter::Arg + &fmt::BasicFormatter::ParseArgIndex(const Char *&s) { + unsigned arg_index = 0; + if (*s < '0' || *s > '9') { + if (*s != '}' && *s != ':') + ReportError(s, "invalid argument index in format string"); + if (next_arg_index_ < 0) { + ReportError(s, + "cannot switch from manual to automatic argument indexing"); + } + arg_index = next_arg_index_++; + } else { + if (next_arg_index_ > 0) { + ReportError(s, + "cannot switch from automatic to manual argument indexing"); + } + next_arg_index_ = -1; + arg_index = ParseUInt(s); + } + if (arg_index >= args_.size()) + ReportError(s, "argument index is out of range in format"); + return *args_[arg_index]; +} + +template +void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { + if (arg.type > LAST_NUMERIC_TYPE) { + ReportError(s, + Format("format specifier '{0}' requires numeric argument") << *s); + } + if (arg.type == UINT || arg.type == ULONG) { + ReportError(s, + Format("format specifier '{0}' requires signed argument") << *s); + } + ++s; +} + +template +void fmt::BasicFormatter::DoFormat() { + const Char *start = format_; + format_ = 0; + next_arg_index_ = 0; + const Char *s = start; + typedef internal::Array::INLINE_BUFFER_SIZE> Buffer; + BasicWriter &writer = *writer_; + while (*s) { + char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + writer.buffer_.append(start, s); + start = ++s; + continue; + } + if (c == '}') + throw FormatError("unmatched '}' in format"); + num_open_braces_= 1; + writer.buffer_.append(start, s - 1); + + const Arg &arg = ParseArgIndex(s); + + FormatSpec spec; + int precision = -1; + if (*s == ':') { + ++s; + + // Parse fill and alignment. + if (char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + ReportError(s, "invalid fill character '{'"); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '=' requires numeric argument"); + break; + } + } while (--p >= s); + } + + // Parse sign. + switch (*s) { + case '+': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + CheckSign(s, arg); + break; + case ' ': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. + if ('0' <= *s && *s <= '9') { + if (*s == '0') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '0' requires numeric argument"); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. + unsigned value = ParseUInt(s); + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + spec.width_ = value; + } + + // Parse precision. + if (*s == '.') { + ++s; + precision = 0; + if ('0' <= *s && *s <= '9') { + unsigned value = ParseUInt(s); + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = value; + } else if (*s == '{') { + ++s; + ++num_open_braces_; + const Arg &precision_arg = ParseArgIndex(s); + unsigned long value = 0; + switch (precision_arg.type) { + case INT: + if (precision_arg.int_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.int_value; + break; + case UINT: + value = precision_arg.uint_value; + break; + case LONG: + if (precision_arg.long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_value; + break; + case ULONG: + value = precision_arg.ulong_value; + break; + default: + ReportError(s, "precision is not integer"); + } + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = value; + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + --num_open_braces_; + } else { + ReportError(s, "missing precision in format"); + } + if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { + ReportError(s, + "precision specifier requires floating-point argument"); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = *s++; + } + + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + start = s; + + // Format argument. + switch (arg.type) { + case INT: + writer.FormatInt(arg.int_value, spec); + break; + case UINT: + writer.FormatInt(arg.uint_value, spec); + break; + case LONG: + writer.FormatInt(arg.long_value, spec); + break; + case ULONG: + writer.FormatInt(arg.ulong_value, spec); + break; + case DOUBLE: + writer.FormatDouble(arg.double_value, spec, precision); + break; + case LONG_DOUBLE: + writer.FormatDouble(arg.long_double_value, spec, precision); + break; + case CHAR: { + if (spec.type_ && spec.type_ != 'c') + internal::ReportUnknownType(spec.type_, "char"); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + out = writer.GrowBuffer(spec.width_); + if (spec.align_ == ALIGN_RIGHT) { + std::fill_n(out, spec.width_ - 1, spec.fill_); + out += spec.width_ - 1; + } else if (spec.align_ == ALIGN_CENTER) { + out = writer.FillPadding(out, spec.width_, 1, spec.fill_); + } else { + std::fill_n(out + 1, spec.width_ - 1, spec.fill_); + } + } else { + out = writer.GrowBuffer(1); + } + *out = arg.int_value; + break; + } + case STRING: { + if (spec.type_ && spec.type_ != 's') + internal::ReportUnknownType(spec.type_, "string"); + const Char *str = arg.string.value; + std::size_t size = arg.string.size; + if (size == 0) { + if (!str) + throw FormatError("string pointer is null"); + if (*str) + size = std::char_traits::length(str); + } + writer.FormatString(str, size, spec); + break; + } + case POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::ReportUnknownType(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); + break; + case CUSTOM: + if (spec.type_) + internal::ReportUnknownType(spec.type_, "object"); + arg.custom.format(writer, arg.custom.value, spec); + break; + default: + assert(false); + break; + } + } + writer.buffer_.append(start, s); +} + +template void fmt::BasicWriter::FormatDouble( + double value, const FormatSpec &spec, int precision); +template void fmt::BasicWriter::FormatDouble( + long double value, const FormatSpec &spec, int precision); +template fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding( + CharPtr buffer, unsigned total_size, std::size_t content_size, char fill); +template void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, uint64_t value, unsigned num_digits); +template fmt::BasicWriter::CharPtr + fmt::BasicWriter::PrepareFilledBuffer( + unsigned size, const AlignSpec &spec, char sign); +template void fmt::BasicFormatter::ReportError( + const char *s, StringRef message) const; +template unsigned fmt::BasicFormatter::ParseUInt(const char *&s) const; +template const fmt::BasicFormatter::Arg + &fmt::BasicFormatter::ParseArgIndex(const char *&s); +template void fmt::BasicFormatter::CheckSign( + const char *&s, const Arg &arg); +template void fmt::BasicFormatter::DoFormat(); + +template void fmt::BasicWriter::FormatDouble( + double value, const FormatSpec &spec, int precision); +template void fmt::BasicWriter::FormatDouble( + long double value, const FormatSpec &spec, int precision); +template fmt::BasicWriter::CharPtr + fmt::BasicWriter::FillPadding( + CharPtr buffer, unsigned total_size, std::size_t content_size, char fill); +template void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, uint64_t value, unsigned num_digits); +template fmt::BasicWriter::CharPtr + fmt::BasicWriter::PrepareFilledBuffer( + unsigned size, const AlignSpec &spec, char sign); +template void fmt::BasicFormatter::ReportError( + const wchar_t *s, StringRef message) const; +template unsigned fmt::BasicFormatter::ParseUInt( + const wchar_t *&s) const; +template const fmt::BasicFormatter::Arg + &fmt::BasicFormatter::ParseArgIndex(const wchar_t *&s); +template void fmt::BasicFormatter::CheckSign( + const wchar_t *&s, const Arg &arg); +template void fmt::BasicFormatter::DoFormat(); diff --git a/format.h b/format.h index 542f7b67..7b934007 100644 --- a/format.h +++ b/format.h @@ -623,217 +623,6 @@ class BasicWriter { } }; -// Fills the padding around the content and returns the pointer to the -// content area. -template -typename BasicWriter::CharPtr BasicWriter::FillPadding( - CharPtr buffer, unsigned total_size, std::size_t content_size, char fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - std::fill_n(buffer, left_padding, fill); - buffer += left_padding; - CharPtr content = buffer; - std::fill_n(buffer + content_size, padding - left_padding, fill); - return content; -} - -template -void BasicWriter::FormatDecimal( - CharPtr buffer, uint64_t value, unsigned num_digits) { - --num_digits; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - buffer[num_digits] = internal::DIGITS[index + 1]; - buffer[num_digits - 1] = internal::DIGITS[index]; - num_digits -= 2; - } - if (value < 10) { - *buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - buffer[1] = internal::DIGITS[index + 1]; - buffer[0] = internal::DIGITS[index]; -} - -template -typename BasicWriter::CharPtr BasicWriter::PrepareFilledBuffer( - unsigned size, const AlignSpec &spec, char sign) { - unsigned width = spec.width(); - if (width <= size) { - CharPtr p = GrowBuffer(size); - *p = sign; - return p + size - 1; - } - CharPtr p = GrowBuffer(width); - CharPtr end = p + width; - Alignment align = spec.align(); - if (align == ALIGN_LEFT) { - *p = sign; - p += size; - std::fill(p, end, spec.fill()); - } else if (align == ALIGN_CENTER) { - p = FillPadding(p, width, size, spec.fill()); - *p = sign; - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (sign) { - *p++ = sign; - --size; - } - } else { - *(end - size) = sign; - } - std::fill(p, end - size, spec.fill()); - p = end; - } - return p - 1; -} - -template -template -void BasicWriter::FormatDouble( - T value, const FormatSpec &spec, int precision) { - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) { - case 0: - type = 'g'; - break; - case 'e': case 'f': case 'g': - break; - case 'F': -#ifdef _MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': case 'G': - upper = true; - break; - default: - internal::ReportUnknownType(type, "double"); - break; - } - - char sign = 0; - // Use SignBit instead of value < 0 because the latter is always - // false for NaN. - if (internal::SignBit(value)) { - sign = '-'; - value = -value; - } else if (spec.sign_flag()) { - sign = spec.plus_flag() ? '+' : ' '; - } - - if (value != value) { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) { - --size; - ++nan; - } - CharPtr out = FormatString(nan, size, spec); - if (sign) - *out = sign; - return; - } - - if (internal::IsInf(value)) { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) { - --size; - ++inf; - } - CharPtr out = FormatString(inf, size, spec); - if (sign) - *out = sign; - return; - } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) { - buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.hash_flag()) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) { - width_for_sprintf = 0; - } else { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (internal::IsLongDouble::VALUE) - *format_ptr++ = 'L'; - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - for (;;) { - std::size_t size = buffer_.capacity() - offset; - Char *start = &buffer_[offset]; - int n = internal::CharTraits::FormatFloat( - start, size, format, width_for_sprintf, precision, value); - if (n >= 0 && offset + n < buffer_.capacity()) { - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = spec.fill(); - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) { - unsigned width = spec.width(); - CharPtr p = GrowBuffer(width); - std::copy(p, p + n, p + (width - n) / 2); - FillPadding(p, spec.width(), n, spec.fill()); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = spec.fill(); - if (sign) - *(start - 1) = sign; - } - GrowBuffer(n); - return; - } - buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); - } -} - template template typename BasicWriter::CharPtr BasicWriter::FormatString( @@ -1335,308 +1124,6 @@ class Write { inline Formatter Print(StringRef format) { return Formatter(format); } - -// Throws Exception(message) if format contains '}', otherwise throws -// FormatError reporting unmatched '{'. The idea is that unmatched '{' -// should override other errors. -template -void BasicFormatter::ReportError(const Char *s, StringRef message) const { - for (int num_open_braces = num_open_braces_; *s; ++s) { - if (*s == '{') { - ++num_open_braces; - } else if (*s == '}') { - if (--num_open_braces == 0) - throw fmt::FormatError(message); - } - } - throw fmt::FormatError("unmatched '{' in format"); -} - -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -unsigned BasicFormatter::ParseUInt(const Char *&s) const { - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - do { - unsigned new_value = value * 10 + (*s++ - '0'); - if (new_value < value) // Check if value wrapped around. - ReportError(s, "number is too big in format"); - value = new_value; - } while ('0' <= *s && *s <= '9'); - return value; -} - -template -inline const typename BasicFormatter::Arg - &BasicFormatter::ParseArgIndex(const Char *&s) { - unsigned arg_index = 0; - if (*s < '0' || *s > '9') { - if (*s != '}' && *s != ':') - ReportError(s, "invalid argument index in format string"); - if (next_arg_index_ < 0) { - ReportError(s, - "cannot switch from manual to automatic argument indexing"); - } - arg_index = next_arg_index_++; - } else { - if (next_arg_index_ > 0) { - ReportError(s, - "cannot switch from automatic to manual argument indexing"); - } - next_arg_index_ = -1; - arg_index = ParseUInt(s); - } - if (arg_index >= args_.size()) - ReportError(s, "argument index is out of range in format"); - return *args_[arg_index]; -} - -template -void BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { - if (arg.type > LAST_NUMERIC_TYPE) { - ReportError(s, - Format("format specifier '{0}' requires numeric argument") << *s); - } - if (arg.type == UINT || arg.type == ULONG) { - ReportError(s, - Format("format specifier '{0}' requires signed argument") << *s); - } - ++s; -} - -template -void BasicFormatter::DoFormat() { - const Char *start = format_; - format_ = 0; - next_arg_index_ = 0; - const Char *s = start; - typedef internal::Array::INLINE_BUFFER_SIZE> Buffer; - BasicWriter &writer = *writer_; - while (*s) { - char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - writer.buffer_.append(start, s); - start = ++s; - continue; - } - if (c == '}') - throw FormatError("unmatched '}' in format"); - num_open_braces_= 1; - writer.buffer_.append(start, s - 1); - - const Arg &arg = ParseArgIndex(s); - - FormatSpec spec; - int precision = -1; - if (*s == ':') { - ++s; - - // Parse fill and alignment. - if (char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - ReportError(s, "invalid fill character '{'"); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '=' requires numeric argument"); - break; - } - } while (--p >= s); - } - - // Parse sign. - switch (*s) { - case '+': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - CheckSign(s, arg); - break; - case ' ': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } - - if (*s == '#') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '#' requires numeric argument"); - spec.flags_ |= HASH_FLAG; - ++s; - } - - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '0' requires numeric argument"); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - unsigned value = ParseUInt(s); - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - spec.width_ = value; - } - - // Parse precision. - if (*s == '.') { - ++s; - precision = 0; - if ('0' <= *s && *s <= '9') { - unsigned value = ParseUInt(s); - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - precision = value; - } else if (*s == '{') { - ++s; - ++num_open_braces_; - const Arg &precision_arg = ParseArgIndex(s); - unsigned long value = 0; - switch (precision_arg.type) { - case INT: - if (precision_arg.int_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.int_value; - break; - case UINT: - value = precision_arg.uint_value; - break; - case LONG: - if (precision_arg.long_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.long_value; - break; - case ULONG: - value = precision_arg.ulong_value; - break; - default: - ReportError(s, "precision is not integer"); - } - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - precision = value; - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - --num_open_braces_; - } else { - ReportError(s, "missing precision in format"); - } - if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { - ReportError(s, - "precision specifier requires floating-point argument"); - } - } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = *s++; - } - - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - start = s; - - // Format argument. - switch (arg.type) { - case INT: - writer.FormatInt(arg.int_value, spec); - break; - case UINT: - writer.FormatInt(arg.uint_value, spec); - break; - case LONG: - writer.FormatInt(arg.long_value, spec); - break; - case ULONG: - writer.FormatInt(arg.ulong_value, spec); - break; - case DOUBLE: - writer.FormatDouble(arg.double_value, spec, precision); - break; - case LONG_DOUBLE: - writer.FormatDouble(arg.long_double_value, spec, precision); - break; - case CHAR: { - if (spec.type_ && spec.type_ != 'c') - internal::ReportUnknownType(spec.type_, "char"); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - out = writer.GrowBuffer(spec.width_); - if (spec.align_ == ALIGN_RIGHT) { - std::fill_n(out, spec.width_ - 1, spec.fill_); - out += spec.width_ - 1; - } else if (spec.align_ == ALIGN_CENTER) { - out = writer.FillPadding(out, spec.width_, 1, spec.fill_); - } else { - std::fill_n(out + 1, spec.width_ - 1, spec.fill_); - } - } else { - out = writer.GrowBuffer(1); - } - *out = arg.int_value; - break; - } - case STRING: { - if (spec.type_ && spec.type_ != 's') - internal::ReportUnknownType(spec.type_, "string"); - const Char *str = arg.string.value; - std::size_t size = arg.string.size; - if (size == 0) { - if (!str) - throw FormatError("string pointer is null"); - if (*str) - size = std::char_traits::length(str); - } - writer.FormatString(str, size, spec); - break; - } - case POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::ReportUnknownType(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); - break; - case CUSTOM: - if (spec.type_) - internal::ReportUnknownType(spec.type_, "object"); - arg.custom.format(writer, arg.custom.value, spec); - break; - default: - assert(false); - break; - } - } - writer.buffer_.append(start, s); -} } #endif // FORMAT_H_