blob: 38ef44bb20ba7b8a787880c8764edbfabffa57f0 [file] [log] [blame]
Victor Zverovichb076df42012-12-07 08:31:09 -08001/*
2 Small, safe and fast printf-like formatting library for C++
3 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 Zverovich5f3ed202012-12-07 17:48:10 -080026 while (*s && *s != '}')
27 ++s;
Victor Zverovichb98b2e92012-12-10 11:08:16 -080028 throw fmt::FormatError(*s ? message : std::string("unmatched '{' in format"));
Victor Zverovich5f3ed202012-12-07 17:48:10 -080029}
30
Victor Zverovichd599e3b2012-12-10 13:30:06 -080031void ReportUnknownType(char code, const char *type) {
32 if (std::isprint(code)) {
33 throw fmt::FormatError(
34 str(fmt::Format("unknown format code '{0}' for {1}") << code << type));
35 }
36 throw fmt::FormatError(
37 str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
38 << static_cast<unsigned>(code) << type));
39}
40
Victor Zverovich8b1c7092012-12-08 18:45:35 -080041// Parses an unsigned integer advancing s to the end of the parsed input.
42// This function assumes that the first character of s is a digit.
43unsigned ParseUInt(const char *&s) {
44 assert('0' <= *s && *s <= '9');
45 unsigned value = 0;
46 do {
47 unsigned new_value = value * 10 + (*s++ - '0');
48 if (new_value < value) // Check if value wrapped around.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080049 ReportError(s, "number is too big in format");
Victor Zverovich8b1c7092012-12-08 18:45:35 -080050 value = new_value;
51 } while ('0' <= *s && *s <= '9');
52 return value;
53}
Victor Zverovichb98b2e92012-12-10 11:08:16 -080054
55// Maps an integer type T to its unsigned counterpart.
56template <typename T>
57struct GetUnsigned;
58
59template <>
60struct GetUnsigned<int> {
61 typedef unsigned Type;
62};
63
64template <>
65struct GetUnsigned<unsigned> {
66 typedef unsigned Type;
67};
68
69template <>
70struct GetUnsigned<long> {
71 typedef unsigned long Type;
72};
73
74template <>
75struct GetUnsigned<unsigned long> {
76 typedef unsigned long Type;
77};
78
79template <typename T>
80struct IsLongDouble { enum {VALUE = 0}; };
81
82template <>
83struct IsLongDouble<long double> { enum {VALUE = 1}; };
Victor Zverovich8b1c7092012-12-08 18:45:35 -080084}
85
Victor Zverovichb076df42012-12-07 08:31:09 -080086template <typename T>
Victor Zverovichb98b2e92012-12-10 11:08:16 -080087void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) {
88 int size = 0;
89 char sign = 0;
90 typedef typename GetUnsigned<T>::Type UnsignedType;
91 UnsignedType abs_value = value;
92 if (value < 0) {
93 sign = '-';
94 ++size;
95 abs_value = -value;
96 } else if ((flags & PLUS_FLAG) != 0) {
97 sign = '+';
98 ++size;
99 }
100 char fill = (flags & ZERO_FLAG) != 0 ? '0' : ' ';
101 size_t start = buffer_.size();
102 char *p = 0;
103 switch (type) {
104 case 0: case 'd': {
105 UnsignedType n = abs_value;
106 do {
107 ++size;
108 } while ((n /= 10) != 0);
109 width = std::max(width, size);
110 buffer_.resize(buffer_.size() + width, fill);
111 p = &buffer_.back();
112 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);
126 buffer_.resize(buffer_.size() + width, fill);
127 p = &buffer_.back();
128 n = abs_value;
129 const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF";
130 do {
131 *p-- = digits[n & 0xf];
132 } while ((n >>= 4) != 0);
133 if (print_prefix) {
134 *p-- = type;
135 *p-- = '0';
136 }
137 break;
138 }
139 case 'o': {
140 UnsignedType n = abs_value;
141 do {
142 ++size;
143 } while ((n >>= 3) != 0);
144 width = std::max(width, size);
145 buffer_.resize(buffer_.size() + width, fill);
146 p = &buffer_.back();
147 n = abs_value;
148 do {
149 *p-- = '0' + (n & 7);
150 } while ((n >>= 3) != 0);
151 break;
152 }
153 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800154 ReportUnknownType(type, "integer");
155 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800156 }
157 if (sign) {
158 if ((flags & ZERO_FLAG) != 0)
159 buffer_[start] = sign;
160 else
161 *p = sign;
162 }
163}
164
165template <typename T>
166void fmt::Formatter::FormatDouble(
167 T value, unsigned flags, int width, int precision, char type) {
168 // Check type.
169 switch (type) {
Victor Zverovichd73306b2012-12-10 12:16:02 -0800170 case 0:
171 type = 'g';
172 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800173 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
174 break;
175 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800176 ReportUnknownType(type, "double");
177 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800178 }
179
180 // Build format string.
181 enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
182 char format[MAX_FORMAT_SIZE];
183 char *format_ptr = format;
184 *format_ptr++ = '%';
185 if ((flags & PLUS_FLAG) != 0)
186 *format_ptr++ = '+';
187 if ((flags & ZERO_FLAG) != 0)
188 *format_ptr++ = '0';
189 if (width > 0)
190 *format_ptr++ = '*';
191 if (precision >= 0) {
192 *format_ptr++ = '.';
193 *format_ptr++ = '*';
194 }
195 if (IsLongDouble<T>::VALUE)
196 *format_ptr++ = 'L';
Victor Zverovichd73306b2012-12-10 12:16:02 -0800197 *format_ptr++ = type;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800198 *format_ptr = '\0';
199
200 // Format using snprintf.
Victor Zverovichb076df42012-12-07 08:31:09 -0800201 size_t offset = buffer_.size();
202 buffer_.resize(buffer_.capacity());
203 for (;;) {
204 size_t size = buffer_.size() - offset;
205 int n = 0;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800206 if (width <= 0) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800207 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800208 snprintf(&buffer_[offset], size, format, value) :
209 snprintf(&buffer_[offset], size, format, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800210 } else {
211 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800212 snprintf(&buffer_[offset], size, format, width, value) :
213 snprintf(&buffer_[offset], size, format, width, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800214 }
215 if (n >= 0 && offset + n < buffer_.size()) {
216 buffer_.resize(offset + n);
217 return;
218 }
219 buffer_.resize(n >= 0 ? offset + n + 1 : 2 * buffer_.size());
220 }
221}
222
223void fmt::Formatter::Format() {
224 buffer_.reserve(500);
225 const char *start = format_;
226 const char *s = start;
Victor Zverovich5f3ed202012-12-07 17:48:10 -0800227 while (*s) {
228 if (*s++ != '{') continue;
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800229 // TODO: handle escape sequence
Victor Zverovich5f3ed202012-12-07 17:48:10 -0800230 buffer_.insert(buffer_.end(), start, s - 1);
Victor Zverovichb076df42012-12-07 08:31:09 -0800231
232 // Parse argument index.
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800233 if (*s < '0' || *s > '9')
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800234 ReportError(s, "missing argument index in format string");
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800235 unsigned arg_index = ParseUInt(s);
Victor Zverovich63539c02012-12-08 08:17:12 -0800236 if (arg_index >= args_.size())
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800237 ReportError(s, "argument index is out of range in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800238 Arg &arg = args_[arg_index];
Victor Zverovichb076df42012-12-07 08:31:09 -0800239
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800240 unsigned flags = 0;
241 int width = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800242 int precision = -1;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800243 char type = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800244 if (*s == ':') {
245 ++s;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800246 if (*s == '+') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800247 ++s;
248 if (arg.type > LAST_NUMERIC_TYPE)
249 ReportError(s, "format specifier '+' requires numeric argument");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800250 if (arg.type == UINT || arg.type == ULONG) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800251 ReportError(s,
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800252 "format specifier '+' requires signed argument");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800253 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800254 flags |= PLUS_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800255 }
256 if (*s == '0') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800257 ++s;
258 if (arg.type > LAST_NUMERIC_TYPE)
259 ReportError(s, "format specifier '0' requires numeric argument");
260 flags |= ZERO_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800261 }
Victor Zverovichb076df42012-12-07 08:31:09 -0800262
263 // Parse width.
264 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800265 unsigned value = ParseUInt(s);
266 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800267 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800268 width = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800269 }
270
271 // Parse precision.
272 if (*s == '.') {
Victor Zverovichb076df42012-12-07 08:31:09 -0800273 ++s;
274 precision = 0;
275 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800276 unsigned value = ParseUInt(s);
277 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800278 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800279 precision = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800280 } else {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800281 ReportError(s, "missing precision in format");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800282 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800283 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
284 ReportError(s,
285 "precision specifier requires floating-point argument");
Victor Zverovichb076df42012-12-07 08:31:09 -0800286 }
287 }
288
289 // Parse type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800290 if (*s != '}' && *s)
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800291 type = *s++;
Victor Zverovichb076df42012-12-07 08:31:09 -0800292 }
293
294 if (*s++ != '}')
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800295 throw FormatError("unmatched '{' in format");
Victor Zverovichb076df42012-12-07 08:31:09 -0800296 start = s;
297
298 // Format argument.
Victor Zverovichb076df42012-12-07 08:31:09 -0800299 switch (arg.type) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800300 case INT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800301 FormatInt(arg.int_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800302 break;
303 case UINT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800304 FormatInt(arg.uint_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800305 break;
306 case LONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800307 FormatInt(arg.long_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800308 break;
309 case ULONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800310 FormatInt(arg.ulong_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800311 break;
312 case DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800313 FormatDouble(arg.double_value, flags, width, precision, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800314 break;
315 case LONG_DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800316 FormatDouble(arg.long_double_value, flags, width, precision, type);
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800317 break;
318 case CHAR:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800319 if (type && type != 'c')
320 ReportUnknownType(type, "char");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800321 buffer_.reserve(std::max(width, 1));
322 buffer_.push_back(arg.int_value);
323 if (width > 1)
324 buffer_.resize(buffer_.size() + width - 1, ' ');
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800325 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800326 case STRING: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800327 if (type && type != 's')
328 ReportUnknownType(type, "string");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800329 const char *str = arg.string_value;
330 size_t size = arg.size;
331 if (size == 0 && *str)
332 size = std::strlen(str);
333 buffer_.reserve(buffer_.size() + std::max<size_t>(width, size));
334 buffer_.insert(buffer_.end(), str, str + size);
335 if (width > size)
336 buffer_.resize(buffer_.size() + width - size, ' ');
Victor Zverovichb076df42012-12-07 08:31:09 -0800337 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800338 }
339 case POINTER:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800340 if (type && type != 'p')
341 ReportUnknownType(type, "pointer");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800342 FormatInt(reinterpret_cast<uintptr_t>(
343 arg.pointer_value), HEX_PREFIX_FLAG, width, 'x');
Victor Zverovichb076df42012-12-07 08:31:09 -0800344 break;
Victor Zverovich280ea8b2012-12-09 09:03:47 -0800345 case CUSTOM:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800346 if (type)
347 ReportUnknownType(type, "object");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800348 (this->*arg.format)(arg.custom_value, width);
Victor Zverovich63539c02012-12-08 08:17:12 -0800349 break;
Victor Zverovichb076df42012-12-07 08:31:09 -0800350 default:
351 assert(false);
352 break;
353 }
354 }
355 buffer_.insert(buffer_.end(), start, s + 1);
356}
357
358fmt::ArgFormatter::~ArgFormatter() {
359 if (!formatter_) return;
360 FinishFormatting();
361}