blob: 468dd13393900dbde7e6b0496ed9b1e83618aea8 [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
61// Maps an integer type T to its unsigned counterpart.
62template <typename T>
63struct GetUnsigned;
64
65template <>
66struct GetUnsigned<int> {
67 typedef unsigned Type;
68};
69
70template <>
71struct GetUnsigned<unsigned> {
72 typedef unsigned Type;
73};
74
75template <>
76struct GetUnsigned<long> {
77 typedef unsigned long Type;
78};
79
80template <>
81struct GetUnsigned<unsigned long> {
82 typedef unsigned long Type;
83};
84
85template <typename T>
86struct IsLongDouble { enum {VALUE = 0}; };
87
88template <>
89struct IsLongDouble<long double> { enum {VALUE = 1}; };
Victor Zverovich8b1c7092012-12-08 18:45:35 -080090}
91
Victor Zverovichb076df42012-12-07 08:31:09 -080092template <typename T>
Victor Zverovichb98b2e92012-12-10 11:08:16 -080093void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) {
94 int size = 0;
95 char sign = 0;
96 typedef typename GetUnsigned<T>::Type UnsignedType;
97 UnsignedType abs_value = value;
98 if (value < 0) {
99 sign = '-';
100 ++size;
101 abs_value = -value;
102 } else if ((flags & PLUS_FLAG) != 0) {
103 sign = '+';
104 ++size;
105 }
106 char fill = (flags & ZERO_FLAG) != 0 ? '0' : ' ';
107 size_t start = buffer_.size();
108 char *p = 0;
109 switch (type) {
110 case 0: case 'd': {
111 UnsignedType n = abs_value;
112 do {
113 ++size;
114 } while ((n /= 10) != 0);
115 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800116 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800117 n = abs_value;
118 do {
119 *p-- = '0' + (n % 10);
120 } while ((n /= 10) != 0);
121 break;
122 }
123 case 'x': case 'X': {
124 UnsignedType n = abs_value;
125 bool print_prefix = (flags & HEX_PREFIX_FLAG) != 0;
126 if (print_prefix) size += 2;
127 do {
128 ++size;
129 } while ((n >>= 4) != 0);
130 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800131 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800132 n = abs_value;
133 const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF";
134 do {
135 *p-- = digits[n & 0xf];
136 } while ((n >>= 4) != 0);
137 if (print_prefix) {
138 *p-- = type;
139 *p-- = '0';
140 }
141 break;
142 }
143 case 'o': {
144 UnsignedType n = abs_value;
145 do {
146 ++size;
147 } while ((n >>= 3) != 0);
148 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800149 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800150 n = abs_value;
151 do {
152 *p-- = '0' + (n & 7);
153 } while ((n >>= 3) != 0);
154 break;
155 }
156 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800157 ReportUnknownType(type, "integer");
158 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800159 }
160 if (sign) {
161 if ((flags & ZERO_FLAG) != 0)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800162 buffer_[start++] = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800163 else
Victor Zverovich198ebe92012-12-10 17:16:08 -0800164 *p-- = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800165 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800166 std::fill(&buffer_[start], p + 1, fill);
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800167}
168
169template <typename T>
170void fmt::Formatter::FormatDouble(
171 T value, unsigned flags, int width, int precision, char type) {
172 // Check type.
173 switch (type) {
Victor Zverovichd73306b2012-12-10 12:16:02 -0800174 case 0:
175 type = 'g';
176 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800177 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
178 break;
179 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800180 ReportUnknownType(type, "double");
181 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800182 }
183
184 // Build format string.
185 enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
186 char format[MAX_FORMAT_SIZE];
187 char *format_ptr = format;
188 *format_ptr++ = '%';
189 if ((flags & PLUS_FLAG) != 0)
190 *format_ptr++ = '+';
191 if ((flags & ZERO_FLAG) != 0)
192 *format_ptr++ = '0';
193 if (width > 0)
194 *format_ptr++ = '*';
195 if (precision >= 0) {
196 *format_ptr++ = '.';
197 *format_ptr++ = '*';
198 }
199 if (IsLongDouble<T>::VALUE)
200 *format_ptr++ = 'L';
Victor Zverovichd73306b2012-12-10 12:16:02 -0800201 *format_ptr++ = type;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800202 *format_ptr = '\0';
203
204 // Format using snprintf.
Victor Zverovichb076df42012-12-07 08:31:09 -0800205 size_t offset = buffer_.size();
Victor Zverovichb076df42012-12-07 08:31:09 -0800206 for (;;) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800207 size_t size = buffer_.capacity() - offset;
Victor Zverovichb076df42012-12-07 08:31:09 -0800208 int n = 0;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800209 if (width <= 0) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800210 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800211 snprintf(&buffer_[offset], size, format, value) :
212 snprintf(&buffer_[offset], size, format, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800213 } else {
214 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800215 snprintf(&buffer_[offset], size, format, width, value) :
216 snprintf(&buffer_[offset], size, format, width, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800217 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800218 if (n >= 0 && offset + n < buffer_.capacity()) {
Victor Zverovich14e0f872012-12-10 18:08:04 -0800219 GrowBuffer(n);
Victor Zverovichb076df42012-12-07 08:31:09 -0800220 return;
221 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800222 buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
Victor Zverovichb076df42012-12-07 08:31:09 -0800223 }
224}
225
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800226void fmt::Formatter::DoFormat() {
Victor Zverovichb076df42012-12-07 08:31:09 -0800227 const char *start = format_;
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800228 format_ = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800229 const char *s = start;
Victor Zverovich5f3ed202012-12-07 17:48:10 -0800230 while (*s) {
Victor Zverovich31a50702012-12-10 15:04:55 -0800231 char c = *s++;
232 if (c != '{' && c != '}') continue;
233 if (*s == c) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800234 buffer_.append(start, s);
Victor Zverovich31a50702012-12-10 15:04:55 -0800235 start = ++s;
236 continue;
237 }
238 if (c == '}')
239 throw FormatError("unmatched '}' in format");
Victor Zverovich198ebe92012-12-10 17:16:08 -0800240 buffer_.append(start, s - 1);
Victor Zverovichb076df42012-12-07 08:31:09 -0800241
242 // Parse argument index.
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800243 if (*s < '0' || *s > '9')
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800244 ReportError(s, "missing argument index in format string");
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800245 unsigned arg_index = ParseUInt(s);
Victor Zverovich63539c02012-12-08 08:17:12 -0800246 if (arg_index >= args_.size())
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800247 ReportError(s, "argument index is out of range in format");
Victor Zverovich4db5a662012-12-11 10:27:13 -0800248 const Arg &arg = *args_[arg_index];
Victor Zverovichb076df42012-12-07 08:31:09 -0800249
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800250 unsigned flags = 0;
251 int width = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800252 int precision = -1;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800253 char type = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800254 if (*s == ':') {
255 ++s;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800256 if (*s == '+') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800257 ++s;
258 if (arg.type > LAST_NUMERIC_TYPE)
259 ReportError(s, "format specifier '+' requires numeric argument");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800260 if (arg.type == UINT || arg.type == ULONG) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800261 ReportError(s,
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800262 "format specifier '+' requires signed argument");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800263 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800264 flags |= PLUS_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800265 }
266 if (*s == '0') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800267 ++s;
268 if (arg.type > LAST_NUMERIC_TYPE)
269 ReportError(s, "format specifier '0' requires numeric argument");
270 flags |= ZERO_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800271 }
Victor Zverovichb076df42012-12-07 08:31:09 -0800272
273 // Parse width.
274 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800275 unsigned value = ParseUInt(s);
276 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800277 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800278 width = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800279 }
280
281 // Parse precision.
282 if (*s == '.') {
Victor Zverovichb076df42012-12-07 08:31:09 -0800283 ++s;
284 precision = 0;
285 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800286 unsigned value = ParseUInt(s);
287 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800288 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800289 precision = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800290 } else {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800291 ReportError(s, "missing precision in format");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800292 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800293 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
294 ReportError(s,
295 "precision specifier requires floating-point argument");
Victor Zverovichb076df42012-12-07 08:31:09 -0800296 }
297 }
298
299 // Parse type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800300 if (*s != '}' && *s)
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800301 type = *s++;
Victor Zverovichb076df42012-12-07 08:31:09 -0800302 }
303
304 if (*s++ != '}')
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800305 throw FormatError("unmatched '{' in format");
Victor Zverovichb076df42012-12-07 08:31:09 -0800306 start = s;
307
308 // Format argument.
Victor Zverovichb076df42012-12-07 08:31:09 -0800309 switch (arg.type) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800310 case INT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800311 FormatInt(arg.int_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800312 break;
313 case UINT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800314 FormatInt(arg.uint_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800315 break;
316 case LONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800317 FormatInt(arg.long_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800318 break;
319 case ULONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800320 FormatInt(arg.ulong_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800321 break;
322 case DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800323 FormatDouble(arg.double_value, flags, width, precision, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800324 break;
325 case LONG_DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800326 FormatDouble(arg.long_double_value, flags, width, precision, type);
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800327 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800328 case CHAR: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800329 if (type && type != 'c')
330 ReportUnknownType(type, "char");
Victor Zverovich14e0f872012-12-10 18:08:04 -0800331 char *out = GrowBuffer(std::max(width, 1));
Victor Zverovich198ebe92012-12-10 17:16:08 -0800332 *out++ = arg.int_value;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800333 if (width > 1)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800334 std::fill_n(out, width - 1, ' ');
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800335 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800336 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800337 case STRING: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800338 if (type && type != 's')
339 ReportUnknownType(type, "string");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800340 const char *str = arg.string_value;
341 size_t size = arg.size;
342 if (size == 0 && *str)
343 size = std::strlen(str);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800344 char *out = GrowBuffer(std::max<size_t>(width, size));
345 out = std::copy(str, str + size, out);
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800346 if (width > size)
Victor Zverovich14e0f872012-12-10 18:08:04 -0800347 std::fill_n(out, width - size, ' ');
Victor Zverovichb076df42012-12-07 08:31:09 -0800348 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800349 }
350 case POINTER:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800351 if (type && type != 'p')
352 ReportUnknownType(type, "pointer");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800353 FormatInt(reinterpret_cast<uintptr_t>(
354 arg.pointer_value), HEX_PREFIX_FLAG, width, 'x');
Victor Zverovichb076df42012-12-07 08:31:09 -0800355 break;
Victor Zverovich280ea8b2012-12-09 09:03:47 -0800356 case CUSTOM:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800357 if (type)
358 ReportUnknownType(type, "object");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800359 (this->*arg.format)(arg.custom_value, width);
Victor Zverovich63539c02012-12-08 08:17:12 -0800360 break;
Victor Zverovichb076df42012-12-07 08:31:09 -0800361 default:
362 assert(false);
363 break;
364 }
365 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800366 buffer_.append(start, s + 1);
Victor Zverovich0996e982012-12-10 20:37:35 -0800367 buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero.
Victor Zverovichb076df42012-12-07 08:31:09 -0800368}