| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2012-12-10 20:37:35 -08:00
										 |  |  |  Small, safe and fast string formatting library for C++ | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |  Author: Victor Zverovich | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "format.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | #include <cassert>
 | 
					
						
							| 
									
										
										
										
											2012-12-08 08:17:12 -08:00
										 |  |  | #include <climits>
 | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | #include <cstring>
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using std::size_t; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  | // Flags.
 | 
					
						
							|  |  |  | enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  | // Throws Exception(message) if format contains '}', otherwise throws
 | 
					
						
							|  |  |  | // FormatError reporting unmatched '{'. The idea is that unmatched '{'
 | 
					
						
							|  |  |  | // should override other errors.
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  | void ReportError(const char *s, const std::string &message) { | 
					
						
							| 
									
										
										
										
											2012-12-10 15:04:55 -08:00
										 |  |  |   for (int num_open_braces = 1; *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"); | 
					
						
							| 
									
										
										
										
											2012-12-07 17:48:10 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  | void ReportUnknownType(char code, const char *type) { | 
					
						
							|  |  |  |   if (std::isprint(code)) { | 
					
						
							|  |  |  |     throw fmt::FormatError( | 
					
						
							|  |  |  |         str(fmt::Format("unknown format code '{0}' for {1}") << code << type)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   throw fmt::FormatError( | 
					
						
							|  |  |  |       str(fmt::Format("unknown format code '\\x{0:02x}' for {1}") | 
					
						
							|  |  |  |         << static_cast<unsigned>(code) << type)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  | // 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.
 | 
					
						
							|  |  |  | unsigned ParseUInt(const char *&s) { | 
					
						
							|  |  |  |   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.
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       ReportError(s, "number is too big in format"); | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |     value = new_value; | 
					
						
							|  |  |  |   } while ('0' <= *s && *s <= '9'); | 
					
						
							|  |  |  |   return value; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Maps an integer type T to its unsigned counterpart.
 | 
					
						
							|  |  |  | template <typename T> | 
					
						
							|  |  |  | struct GetUnsigned; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct GetUnsigned<int> { | 
					
						
							|  |  |  |   typedef unsigned Type; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct GetUnsigned<unsigned> { | 
					
						
							|  |  |  |   typedef unsigned Type; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct GetUnsigned<long> { | 
					
						
							|  |  |  |   typedef unsigned long Type; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct GetUnsigned<unsigned long> { | 
					
						
							|  |  |  |   typedef unsigned long Type; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T> | 
					
						
							|  |  |  | struct IsLongDouble { enum {VALUE = 0}; }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct IsLongDouble<long double> { enum {VALUE = 1}; }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T> | 
					
						
							|  |  |  | void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) { | 
					
						
							|  |  |  |   int size = 0; | 
					
						
							|  |  |  |   char sign = 0; | 
					
						
							|  |  |  |   typedef typename GetUnsigned<T>::Type UnsignedType; | 
					
						
							|  |  |  |   UnsignedType abs_value = value; | 
					
						
							|  |  |  |   if (value < 0) { | 
					
						
							|  |  |  |     sign = '-'; | 
					
						
							|  |  |  |     ++size; | 
					
						
							|  |  |  |     abs_value = -value; | 
					
						
							|  |  |  |   } else if ((flags & PLUS_FLAG) != 0) { | 
					
						
							|  |  |  |     sign = '+'; | 
					
						
							|  |  |  |     ++size; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   char fill = (flags & ZERO_FLAG) != 0 ? '0' : ' '; | 
					
						
							|  |  |  |   size_t start = buffer_.size(); | 
					
						
							|  |  |  |   char *p = 0; | 
					
						
							|  |  |  |   switch (type) { | 
					
						
							|  |  |  |   case 0: case 'd': { | 
					
						
							|  |  |  |     UnsignedType n = abs_value; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       ++size; | 
					
						
							|  |  |  |     } while ((n /= 10) != 0); | 
					
						
							|  |  |  |     width = std::max(width, size); | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |     p = GrowBuffer(width) + width - 1; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     n = abs_value; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       *p-- = '0' + (n % 10); | 
					
						
							|  |  |  |     } while ((n /= 10) != 0); | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   case 'x': case 'X': { | 
					
						
							|  |  |  |     UnsignedType n = abs_value; | 
					
						
							|  |  |  |     bool print_prefix = (flags & HEX_PREFIX_FLAG) != 0; | 
					
						
							|  |  |  |     if (print_prefix) size += 2; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       ++size; | 
					
						
							|  |  |  |     } while ((n >>= 4) != 0); | 
					
						
							|  |  |  |     width = std::max(width, size); | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |     p = GrowBuffer(width) + width - 1; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     n = abs_value; | 
					
						
							|  |  |  |     const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       *p-- = digits[n & 0xf]; | 
					
						
							|  |  |  |     } while ((n >>= 4) != 0); | 
					
						
							|  |  |  |     if (print_prefix) { | 
					
						
							|  |  |  |       *p-- = type; | 
					
						
							|  |  |  |       *p-- = '0'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   case 'o': { | 
					
						
							|  |  |  |     UnsignedType n = abs_value; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       ++size; | 
					
						
							|  |  |  |     } while ((n >>= 3) != 0); | 
					
						
							|  |  |  |     width = std::max(width, size); | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |     p = GrowBuffer(width) + width - 1; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     n = abs_value; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |       *p-- = '0' + (n & 7); | 
					
						
							|  |  |  |     } while ((n >>= 3) != 0); | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   default: | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |     ReportUnknownType(type, "integer"); | 
					
						
							|  |  |  |     break; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |   } | 
					
						
							|  |  |  |   if (sign) { | 
					
						
							|  |  |  |     if ((flags & ZERO_FLAG) != 0) | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |       buffer_[start++] = sign; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |       *p-- = sign; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |   std::fill(&buffer_[start], p + 1, fill); | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | template <typename T> | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  | void fmt::Formatter::FormatDouble( | 
					
						
							|  |  |  |     T value, unsigned flags, int width, int precision, char type) { | 
					
						
							|  |  |  |   // Check type.
 | 
					
						
							|  |  |  |   switch (type) { | 
					
						
							| 
									
										
										
										
											2012-12-10 12:16:02 -08:00
										 |  |  |   case 0: | 
					
						
							|  |  |  |     type = 'g'; | 
					
						
							|  |  |  |     break; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |   case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   default: | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |     ReportUnknownType(type, "double"); | 
					
						
							|  |  |  |     break; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Build format string.
 | 
					
						
							|  |  |  |   enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
 | 
					
						
							|  |  |  |   char format[MAX_FORMAT_SIZE]; | 
					
						
							|  |  |  |   char *format_ptr = format; | 
					
						
							|  |  |  |   *format_ptr++ = '%'; | 
					
						
							|  |  |  |   if ((flags & PLUS_FLAG) != 0) | 
					
						
							|  |  |  |     *format_ptr++ = '+'; | 
					
						
							|  |  |  |   if ((flags & ZERO_FLAG) != 0) | 
					
						
							|  |  |  |     *format_ptr++ = '0'; | 
					
						
							|  |  |  |   if (width > 0) | 
					
						
							|  |  |  |     *format_ptr++ = '*'; | 
					
						
							|  |  |  |   if (precision >= 0) { | 
					
						
							|  |  |  |     *format_ptr++ = '.'; | 
					
						
							|  |  |  |     *format_ptr++ = '*'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (IsLongDouble<T>::VALUE) | 
					
						
							|  |  |  |     *format_ptr++ = 'L'; | 
					
						
							| 
									
										
										
										
											2012-12-10 12:16:02 -08:00
										 |  |  |   *format_ptr++ = type; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |   *format_ptr = '\0'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Format using snprintf.
 | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |   size_t offset = buffer_.size(); | 
					
						
							|  |  |  |   for (;;) { | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     size_t size = buffer_.capacity() - offset; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     int n = 0; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     if (width <= 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       n = precision < 0 ? | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |           snprintf(&buffer_[offset], size, format, value) : | 
					
						
							|  |  |  |           snprintf(&buffer_[offset], size, format, precision, value); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       n = precision < 0 ? | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |           snprintf(&buffer_[offset], size, format, width, value) : | 
					
						
							|  |  |  |           snprintf(&buffer_[offset], size, format, width, precision, value); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     if (n >= 0 && offset + n < buffer_.capacity()) { | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |       GrowBuffer(n); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-11 12:23:52 -08:00
										 |  |  | void fmt::Formatter::DoFormat() { | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |   const char *start = format_; | 
					
						
							| 
									
										
										
										
											2012-12-11 12:23:52 -08:00
										 |  |  |   format_ = 0; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |   const char *s = start; | 
					
						
							| 
									
										
										
										
											2012-12-07 17:48:10 -08:00
										 |  |  |   while (*s) { | 
					
						
							| 
									
										
										
										
											2012-12-10 15:04:55 -08:00
										 |  |  |     char c = *s++; | 
					
						
							|  |  |  |     if (c != '{' && c != '}') continue; | 
					
						
							|  |  |  |     if (*s == c) { | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |       buffer_.append(start, s); | 
					
						
							| 
									
										
										
										
											2012-12-10 15:04:55 -08:00
										 |  |  |       start = ++s; | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (c == '}') | 
					
						
							|  |  |  |       throw FormatError("unmatched '}' in format"); | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     buffer_.append(start, s - 1); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Parse argument index.
 | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |     if (*s < '0' || *s > '9') | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       ReportError(s, "missing argument index in format string"); | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |     unsigned arg_index = ParseUInt(s); | 
					
						
							| 
									
										
										
										
											2012-12-08 08:17:12 -08:00
										 |  |  |     if (arg_index >= args_.size()) | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       ReportError(s, "argument index is out of range in format"); | 
					
						
							| 
									
										
										
										
											2012-12-11 10:27:13 -08:00
										 |  |  |     const Arg &arg = *args_[arg_index]; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     unsigned flags = 0; | 
					
						
							|  |  |  |     int width = 0; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     int precision = -1; | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |     char type = 0; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     if (*s == ':') { | 
					
						
							|  |  |  |       ++s; | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |       if (*s == '+') { | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |         ++s; | 
					
						
							|  |  |  |         if (arg.type > LAST_NUMERIC_TYPE) | 
					
						
							|  |  |  |           ReportError(s, "format specifier '+' requires numeric argument"); | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |         if (arg.type == UINT || arg.type == ULONG) { | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |           ReportError(s, | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |               "format specifier '+' requires signed argument"); | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |         flags |= PLUS_FLAG; | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |       if (*s == '0') { | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |         ++s; | 
					
						
							|  |  |  |         if (arg.type > LAST_NUMERIC_TYPE) | 
					
						
							|  |  |  |           ReportError(s, "format specifier '0' requires numeric argument"); | 
					
						
							|  |  |  |         flags |= ZERO_FLAG; | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Parse width.
 | 
					
						
							|  |  |  |       if ('0' <= *s && *s <= '9') { | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |         unsigned value = ParseUInt(s); | 
					
						
							|  |  |  |         if (value > INT_MAX) | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |           ReportError(s, "number is too big in format"); | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |         width = value; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Parse precision.
 | 
					
						
							|  |  |  |       if (*s == '.') { | 
					
						
							|  |  |  |         ++s; | 
					
						
							|  |  |  |         precision = 0; | 
					
						
							|  |  |  |         if ('0' <= *s && *s <= '9') { | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |           unsigned value = ParseUInt(s); | 
					
						
							|  |  |  |           if (value > INT_MAX) | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |             ReportError(s, "number is too big in format"); | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |           precision = value; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |           ReportError(s, "missing precision in format"); | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |         if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { | 
					
						
							|  |  |  |           ReportError(s, | 
					
						
							|  |  |  |               "precision specifier requires floating-point argument"); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Parse type.
 | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       if (*s != '}' && *s) | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |         type = *s++; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (*s++ != '}') | 
					
						
							| 
									
										
										
										
											2012-12-08 18:45:35 -08:00
										 |  |  |       throw FormatError("unmatched '{' in format"); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     start = s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Format argument.
 | 
					
						
							|  |  |  |     switch (arg.type) { | 
					
						
							|  |  |  |     case INT: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatInt(arg.int_value, flags, width, type); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     case UINT: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatInt(arg.uint_value, flags, width, type); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     case LONG: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatInt(arg.long_value, flags, width, type); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     case ULONG: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatInt(arg.ulong_value, flags, width, type); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     case DOUBLE: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatDouble(arg.double_value, flags, width, precision, type); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     case LONG_DOUBLE: | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatDouble(arg.long_double_value, flags, width, precision, type); | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     case CHAR: { | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |       if (type && type != 'c') | 
					
						
							|  |  |  |         ReportUnknownType(type, "char"); | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |       char *out = GrowBuffer(std::max(width, 1)); | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |       *out++ = arg.int_value; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       if (width > 1) | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |         std::fill_n(out, width - 1, ' '); | 
					
						
							| 
									
										
										
										
											2012-12-09 14:13:23 -08:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     case STRING: { | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |       if (type && type != 's') | 
					
						
							|  |  |  |         ReportUnknownType(type, "string"); | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       const char *str = arg.string_value; | 
					
						
							|  |  |  |       size_t size = arg.size; | 
					
						
							|  |  |  |       if (size == 0 && *str) | 
					
						
							|  |  |  |         size = std::strlen(str); | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |       char *out = GrowBuffer(std::max<size_t>(width, size)); | 
					
						
							|  |  |  |       out = std::copy(str, str + size, out); | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       if (width > size) | 
					
						
							| 
									
										
										
										
											2012-12-10 18:08:04 -08:00
										 |  |  |         std::fill_n(out, width - size, ' '); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |     case POINTER: | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |       if (type && type != 'p') | 
					
						
							|  |  |  |         ReportUnknownType(type, "pointer"); | 
					
						
							| 
									
										
										
										
											2012-12-10 11:08:16 -08:00
										 |  |  |       FormatInt(reinterpret_cast<uintptr_t>( | 
					
						
							|  |  |  |           arg.pointer_value), HEX_PREFIX_FLAG, width, 'x'); | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-12-09 09:03:47 -08:00
										 |  |  |     case CUSTOM: | 
					
						
							| 
									
										
										
										
											2012-12-10 13:30:06 -08:00
										 |  |  |       if (type) | 
					
						
							|  |  |  |         ReportUnknownType(type, "object"); | 
					
						
							| 
									
										
										
										
											2012-12-09 11:32:39 -08:00
										 |  |  |       (this->*arg.format)(arg.custom_value, width); | 
					
						
							| 
									
										
										
										
											2012-12-08 08:17:12 -08:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  |     default: | 
					
						
							|  |  |  |       assert(false); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-12-10 17:16:08 -08:00
										 |  |  |   buffer_.append(start, s + 1); | 
					
						
							| 
									
										
										
										
											2012-12-10 20:37:35 -08:00
										 |  |  |   buffer_.resize(buffer_.size() - 1);  // Don't count the terminating zero.
 | 
					
						
							| 
									
										
										
										
											2012-12-07 08:31:09 -08:00
										 |  |  | } |