blob: cc9d8ec1c9ed2154928912944e31e1041fc0390d [file] [log] [blame]
Victor Zverovichb076df42012-12-07 08:31:09 -08001/*
Victor Zverovich0996e982012-12-10 20:37:35 -08002 Small, safe and fast string formatting library for C++
Victor Zverovichb076df42012-12-07 08:31:09 -08003 Author: Victor Zverovich
4 */
5
6#include "format.h"
7
Victor Zverovichb98b2e92012-12-10 11:08:16 -08008#include <stdint.h>
9
Victor Zverovichb076df42012-12-07 08:31:09 -080010#include <cassert>
Victor Zverovich63539c02012-12-08 08:17:12 -080011#include <climits>
Victor Zverovichb076df42012-12-07 08:31:09 -080012#include <cstring>
13#include <algorithm>
14
15using std::size_t;
16
Victor Zverovich8b1c7092012-12-08 18:45:35 -080017namespace {
18
Victor Zverovichb98b2e92012-12-10 11:08:16 -080019// Flags.
20enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 };
21
Victor Zverovich8b1c7092012-12-08 18:45:35 -080022// Throws Exception(message) if format contains '}', otherwise throws
23// FormatError reporting unmatched '{'. The idea is that unmatched '{'
24// should override other errors.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080025void ReportError(const char *s, const std::string &message) {
Victor Zverovich31a50702012-12-10 15:04:55 -080026 for (int num_open_braces = 1; *s; ++s) {
27 if (*s == '{') {
28 ++num_open_braces;
29 } else if (*s == '}') {
30 if (--num_open_braces == 0)
31 throw fmt::FormatError(message);
32 }
33 }
34 throw fmt::FormatError("unmatched '{' in format");
Victor Zverovich5f3ed202012-12-07 17:48:10 -080035}
36
Victor Zverovichd599e3b2012-12-10 13:30:06 -080037void ReportUnknownType(char code, const char *type) {
38 if (std::isprint(code)) {
39 throw fmt::FormatError(
40 str(fmt::Format("unknown format code '{0}' for {1}") << code << type));
41 }
42 throw fmt::FormatError(
43 str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
44 << static_cast<unsigned>(code) << type));
45}
46
Victor Zverovich8b1c7092012-12-08 18:45:35 -080047// Parses an unsigned integer advancing s to the end of the parsed input.
48// This function assumes that the first character of s is a digit.
49unsigned ParseUInt(const char *&s) {
50 assert('0' <= *s && *s <= '9');
51 unsigned value = 0;
52 do {
53 unsigned new_value = value * 10 + (*s++ - '0');
54 if (new_value < value) // Check if value wrapped around.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080055 ReportError(s, "number is too big in format");
Victor Zverovich8b1c7092012-12-08 18:45:35 -080056 value = new_value;
57 } while ('0' <= *s && *s <= '9');
58 return value;
59}
Victor Zverovichb98b2e92012-12-10 11:08:16 -080060
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080061// Information about an integer type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080062template <typename T>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080063struct IntTraits {
64 typedef T UnsignedType;
65 static bool IsNegative(T) { return false; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -080066};
67
68template <>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080069struct IntTraits<int> {
70 typedef unsigned UnsignedType;
71 static bool IsNegative(int value) { return value < 0; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -080072};
73
74template <>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080075struct IntTraits<long> {
76 typedef unsigned long UnsignedType;
77 static bool IsNegative(long value) { return value < 0; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -080078};
79
80template <typename T>
81struct IsLongDouble { enum {VALUE = 0}; };
82
83template <>
84struct IsLongDouble<long double> { enum {VALUE = 1}; };
Victor Zverovich8b1c7092012-12-08 18:45:35 -080085}
86
Victor Zverovichb076df42012-12-07 08:31:09 -080087template <typename T>
Victor Zverovichb98b2e92012-12-10 11:08:16 -080088void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) {
89 int size = 0;
90 char sign = 0;
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080091 typedef typename IntTraits<T>::UnsignedType UnsignedType;
Victor Zverovichb98b2e92012-12-10 11:08:16 -080092 UnsignedType abs_value = value;
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080093 if (IntTraits<T>::IsNegative(value)) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -080094 sign = '-';
95 ++size;
96 abs_value = -value;
97 } else if ((flags & PLUS_FLAG) != 0) {
98 sign = '+';
99 ++size;
100 }
101 char fill = (flags & ZERO_FLAG) != 0 ? '0' : ' ';
102 size_t start = buffer_.size();
103 char *p = 0;
104 switch (type) {
105 case 0: case 'd': {
106 UnsignedType n = abs_value;
107 do {
108 ++size;
109 } while ((n /= 10) != 0);
110 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800111 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800112 n = abs_value;
113 do {
114 *p-- = '0' + (n % 10);
115 } while ((n /= 10) != 0);
116 break;
117 }
118 case 'x': case 'X': {
119 UnsignedType n = abs_value;
120 bool print_prefix = (flags & HEX_PREFIX_FLAG) != 0;
121 if (print_prefix) size += 2;
122 do {
123 ++size;
124 } while ((n >>= 4) != 0);
125 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800126 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800127 n = abs_value;
128 const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF";
129 do {
130 *p-- = digits[n & 0xf];
131 } while ((n >>= 4) != 0);
132 if (print_prefix) {
133 *p-- = type;
134 *p-- = '0';
135 }
136 break;
137 }
138 case 'o': {
139 UnsignedType n = abs_value;
140 do {
141 ++size;
142 } while ((n >>= 3) != 0);
143 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800144 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800145 n = abs_value;
146 do {
147 *p-- = '0' + (n & 7);
148 } while ((n >>= 3) != 0);
149 break;
150 }
151 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800152 ReportUnknownType(type, "integer");
153 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800154 }
155 if (sign) {
156 if ((flags & ZERO_FLAG) != 0)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800157 buffer_[start++] = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800158 else
Victor Zverovich198ebe92012-12-10 17:16:08 -0800159 *p-- = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800160 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800161 std::fill(&buffer_[start], p + 1, fill);
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800162}
163
164template <typename T>
165void fmt::Formatter::FormatDouble(
166 T value, unsigned flags, int width, int precision, char type) {
167 // Check type.
168 switch (type) {
Victor Zverovichd73306b2012-12-10 12:16:02 -0800169 case 0:
170 type = 'g';
171 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800172 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
173 break;
174 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800175 ReportUnknownType(type, "double");
176 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800177 }
178
179 // Build format string.
180 enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
181 char format[MAX_FORMAT_SIZE];
182 char *format_ptr = format;
183 *format_ptr++ = '%';
184 if ((flags & PLUS_FLAG) != 0)
185 *format_ptr++ = '+';
186 if ((flags & ZERO_FLAG) != 0)
187 *format_ptr++ = '0';
188 if (width > 0)
189 *format_ptr++ = '*';
190 if (precision >= 0) {
191 *format_ptr++ = '.';
192 *format_ptr++ = '*';
193 }
194 if (IsLongDouble<T>::VALUE)
195 *format_ptr++ = 'L';
Victor Zverovichd73306b2012-12-10 12:16:02 -0800196 *format_ptr++ = type;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800197 *format_ptr = '\0';
198
199 // Format using snprintf.
Victor Zverovichb076df42012-12-07 08:31:09 -0800200 size_t offset = buffer_.size();
Victor Zverovichb076df42012-12-07 08:31:09 -0800201 for (;;) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800202 size_t size = buffer_.capacity() - offset;
Victor Zverovichb076df42012-12-07 08:31:09 -0800203 int n = 0;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800204 if (width <= 0) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800205 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800206 snprintf(&buffer_[offset], size, format, value) :
207 snprintf(&buffer_[offset], size, format, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800208 } else {
209 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800210 snprintf(&buffer_[offset], size, format, width, value) :
211 snprintf(&buffer_[offset], size, format, width, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800212 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800213 if (n >= 0 && offset + n < buffer_.capacity()) {
Victor Zverovich14e0f872012-12-10 18:08:04 -0800214 GrowBuffer(n);
Victor Zverovichb076df42012-12-07 08:31:09 -0800215 return;
216 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800217 buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
Victor Zverovichb076df42012-12-07 08:31:09 -0800218 }
219}
220
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800221void fmt::Formatter::DoFormat() {
Victor Zverovichb076df42012-12-07 08:31:09 -0800222 const char *start = format_;
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800223 format_ = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800224 const char *s = start;
Victor Zverovich5f3ed202012-12-07 17:48:10 -0800225 while (*s) {
Victor Zverovich31a50702012-12-10 15:04:55 -0800226 char c = *s++;
227 if (c != '{' && c != '}') continue;
228 if (*s == c) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800229 buffer_.append(start, s);
Victor Zverovich31a50702012-12-10 15:04:55 -0800230 start = ++s;
231 continue;
232 }
233 if (c == '}')
234 throw FormatError("unmatched '}' in format");
Victor Zverovich198ebe92012-12-10 17:16:08 -0800235 buffer_.append(start, s - 1);
Victor Zverovichb076df42012-12-07 08:31:09 -0800236
237 // Parse argument index.
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800238 if (*s < '0' || *s > '9')
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800239 ReportError(s, "missing argument index in format string");
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800240 unsigned arg_index = ParseUInt(s);
Victor Zverovich63539c02012-12-08 08:17:12 -0800241 if (arg_index >= args_.size())
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800242 ReportError(s, "argument index is out of range in format");
Victor Zverovich4db5a662012-12-11 10:27:13 -0800243 const Arg &arg = *args_[arg_index];
Victor Zverovichb076df42012-12-07 08:31:09 -0800244
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800245 unsigned flags = 0;
246 int width = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800247 int precision = -1;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800248 char type = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800249 if (*s == ':') {
250 ++s;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800251 if (*s == '+') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800252 ++s;
253 if (arg.type > LAST_NUMERIC_TYPE)
254 ReportError(s, "format specifier '+' requires numeric argument");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800255 if (arg.type == UINT || arg.type == ULONG) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800256 ReportError(s,
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800257 "format specifier '+' requires signed argument");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800258 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800259 flags |= PLUS_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800260 }
261 if (*s == '0') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800262 ++s;
263 if (arg.type > LAST_NUMERIC_TYPE)
264 ReportError(s, "format specifier '0' requires numeric argument");
265 flags |= ZERO_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800266 }
Victor Zverovichb076df42012-12-07 08:31:09 -0800267
268 // Parse width.
269 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800270 unsigned value = ParseUInt(s);
271 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800272 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800273 width = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800274 }
275
276 // Parse precision.
277 if (*s == '.') {
Victor Zverovichb076df42012-12-07 08:31:09 -0800278 ++s;
279 precision = 0;
280 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800281 unsigned value = ParseUInt(s);
282 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800283 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800284 precision = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800285 } else {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800286 ReportError(s, "missing precision in format");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800287 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800288 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
289 ReportError(s,
290 "precision specifier requires floating-point argument");
Victor Zverovichb076df42012-12-07 08:31:09 -0800291 }
292 }
293
294 // Parse type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800295 if (*s != '}' && *s)
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800296 type = *s++;
Victor Zverovichb076df42012-12-07 08:31:09 -0800297 }
298
299 if (*s++ != '}')
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800300 throw FormatError("unmatched '{' in format");
Victor Zverovichb076df42012-12-07 08:31:09 -0800301 start = s;
302
303 // Format argument.
Victor Zverovichb076df42012-12-07 08:31:09 -0800304 switch (arg.type) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800305 case INT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800306 FormatInt(arg.int_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800307 break;
308 case UINT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800309 FormatInt(arg.uint_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800310 break;
311 case LONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800312 FormatInt(arg.long_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800313 break;
314 case ULONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800315 FormatInt(arg.ulong_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800316 break;
317 case DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800318 FormatDouble(arg.double_value, flags, width, precision, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800319 break;
320 case LONG_DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800321 FormatDouble(arg.long_double_value, flags, width, precision, type);
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800322 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800323 case CHAR: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800324 if (type && type != 'c')
325 ReportUnknownType(type, "char");
Victor Zverovich14e0f872012-12-10 18:08:04 -0800326 char *out = GrowBuffer(std::max(width, 1));
Victor Zverovich198ebe92012-12-10 17:16:08 -0800327 *out++ = arg.int_value;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800328 if (width > 1)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800329 std::fill_n(out, width - 1, ' ');
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800330 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800331 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800332 case STRING: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800333 if (type && type != 's')
334 ReportUnknownType(type, "string");
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800335 const char *str = arg.string.value;
336 size_t size = arg.string.size;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800337 if (size == 0 && *str)
338 size = std::strlen(str);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800339 char *out = GrowBuffer(std::max<size_t>(width, size));
340 out = std::copy(str, str + size, out);
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800341 if (static_cast<unsigned>(width) > size)
Victor Zverovich14e0f872012-12-10 18:08:04 -0800342 std::fill_n(out, width - size, ' ');
Victor Zverovichb076df42012-12-07 08:31:09 -0800343 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800344 }
345 case POINTER:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800346 if (type && type != 'p')
347 ReportUnknownType(type, "pointer");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800348 FormatInt(reinterpret_cast<uintptr_t>(
349 arg.pointer_value), HEX_PREFIX_FLAG, width, 'x');
Victor Zverovichb076df42012-12-07 08:31:09 -0800350 break;
Victor Zverovich280ea8b2012-12-09 09:03:47 -0800351 case CUSTOM:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800352 if (type)
353 ReportUnknownType(type, "object");
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800354 (this->*arg.custom.format)(arg.custom.value, width);
Victor Zverovich63539c02012-12-08 08:17:12 -0800355 break;
Victor Zverovichb076df42012-12-07 08:31:09 -0800356 default:
357 assert(false);
358 break;
359 }
360 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800361 buffer_.append(start, s + 1);
Victor Zverovich0996e982012-12-10 20:37:35 -0800362 buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero.
Victor Zverovichb076df42012-12-07 08:31:09 -0800363}