blob: 5f07c548466fa33c11106622d89739bb43492ab7 [file] [log] [blame]
Victor Zverovichb076df42012-12-07 08:31:09 -08001/*
Victor Zverovichfaccb4c2012-12-12 07:44:41 -08002 String formatting library for C++
3
4 Copyright (c) 2012, Victor Zverovich
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Victor Zverovichb076df42012-12-07 08:31:09 -080026 */
27
28#include "format.h"
29
Victor Zverovichb98b2e92012-12-10 11:08:16 -080030#include <stdint.h>
31
Victor Zverovichb076df42012-12-07 08:31:09 -080032#include <cassert>
Victor Zverovich72f896d2012-12-12 09:17:28 -080033#include <cctype>
Victor Zverovich63539c02012-12-08 08:17:12 -080034#include <climits>
Victor Zverovichb076df42012-12-07 08:31:09 -080035#include <cstring>
36#include <algorithm>
37
38using std::size_t;
39
Victor Zverovich8b1c7092012-12-08 18:45:35 -080040namespace {
41
Victor Zverovichb98b2e92012-12-10 11:08:16 -080042// Flags.
43enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 };
44
Victor Zverovich8b1c7092012-12-08 18:45:35 -080045// Throws Exception(message) if format contains '}', otherwise throws
46// FormatError reporting unmatched '{'. The idea is that unmatched '{'
47// should override other errors.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080048void ReportError(const char *s, const std::string &message) {
Victor Zverovich31a50702012-12-10 15:04:55 -080049 for (int num_open_braces = 1; *s; ++s) {
50 if (*s == '{') {
51 ++num_open_braces;
52 } else if (*s == '}') {
53 if (--num_open_braces == 0)
54 throw fmt::FormatError(message);
55 }
56 }
57 throw fmt::FormatError("unmatched '{' in format");
Victor Zverovich5f3ed202012-12-07 17:48:10 -080058}
59
Victor Zverovichd599e3b2012-12-10 13:30:06 -080060void ReportUnknownType(char code, const char *type) {
61 if (std::isprint(code)) {
62 throw fmt::FormatError(
63 str(fmt::Format("unknown format code '{0}' for {1}") << code << type));
64 }
65 throw fmt::FormatError(
66 str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
67 << static_cast<unsigned>(code) << type));
68}
69
Victor Zverovich8b1c7092012-12-08 18:45:35 -080070// Parses an unsigned integer advancing s to the end of the parsed input.
71// This function assumes that the first character of s is a digit.
72unsigned ParseUInt(const char *&s) {
73 assert('0' <= *s && *s <= '9');
74 unsigned value = 0;
75 do {
76 unsigned new_value = value * 10 + (*s++ - '0');
77 if (new_value < value) // Check if value wrapped around.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080078 ReportError(s, "number is too big in format");
Victor Zverovich8b1c7092012-12-08 18:45:35 -080079 value = new_value;
80 } while ('0' <= *s && *s <= '9');
81 return value;
82}
Victor Zverovichb98b2e92012-12-10 11:08:16 -080083
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080084// Information about an integer type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -080085template <typename T>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080086struct IntTraits {
87 typedef T UnsignedType;
88 static bool IsNegative(T) { return false; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -080089};
90
91template <>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080092struct IntTraits<int> {
93 typedef unsigned UnsignedType;
94 static bool IsNegative(int value) { return value < 0; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -080095};
96
97template <>
Victor Zverovich33bb6ee2012-12-11 21:47:05 -080098struct IntTraits<long> {
99 typedef unsigned long UnsignedType;
100 static bool IsNegative(long value) { return value < 0; }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800101};
102
103template <typename T>
104struct IsLongDouble { enum {VALUE = 0}; };
105
106template <>
107struct IsLongDouble<long double> { enum {VALUE = 1}; };
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800108}
109
Victor Zverovichb076df42012-12-07 08:31:09 -0800110template <typename T>
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800111void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) {
112 int size = 0;
113 char sign = 0;
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800114 typedef typename IntTraits<T>::UnsignedType UnsignedType;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800115 UnsignedType abs_value = value;
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800116 if (IntTraits<T>::IsNegative(value)) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800117 sign = '-';
118 ++size;
119 abs_value = -value;
120 } else if ((flags & PLUS_FLAG) != 0) {
121 sign = '+';
122 ++size;
123 }
124 char fill = (flags & ZERO_FLAG) != 0 ? '0' : ' ';
125 size_t start = buffer_.size();
126 char *p = 0;
127 switch (type) {
128 case 0: case 'd': {
129 UnsignedType n = abs_value;
130 do {
131 ++size;
132 } while ((n /= 10) != 0);
133 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800134 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800135 n = abs_value;
136 do {
137 *p-- = '0' + (n % 10);
138 } while ((n /= 10) != 0);
139 break;
140 }
141 case 'x': case 'X': {
142 UnsignedType n = abs_value;
143 bool print_prefix = (flags & HEX_PREFIX_FLAG) != 0;
144 if (print_prefix) size += 2;
145 do {
146 ++size;
147 } while ((n >>= 4) != 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 const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF";
152 do {
153 *p-- = digits[n & 0xf];
154 } while ((n >>= 4) != 0);
155 if (print_prefix) {
156 *p-- = type;
157 *p-- = '0';
158 }
159 break;
160 }
161 case 'o': {
162 UnsignedType n = abs_value;
163 do {
164 ++size;
165 } while ((n >>= 3) != 0);
166 width = std::max(width, size);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800167 p = GrowBuffer(width) + width - 1;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800168 n = abs_value;
169 do {
170 *p-- = '0' + (n & 7);
171 } while ((n >>= 3) != 0);
172 break;
173 }
174 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800175 ReportUnknownType(type, "integer");
176 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800177 }
178 if (sign) {
179 if ((flags & ZERO_FLAG) != 0)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800180 buffer_[start++] = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800181 else
Victor Zverovich198ebe92012-12-10 17:16:08 -0800182 *p-- = sign;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800183 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800184 std::fill(&buffer_[start], p + 1, fill);
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800185}
186
187template <typename T>
188void fmt::Formatter::FormatDouble(
189 T value, unsigned flags, int width, int precision, char type) {
190 // Check type.
191 switch (type) {
Victor Zverovichd73306b2012-12-10 12:16:02 -0800192 case 0:
193 type = 'g';
194 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800195 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
196 break;
197 default:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800198 ReportUnknownType(type, "double");
199 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800200 }
201
202 // Build format string.
203 enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
204 char format[MAX_FORMAT_SIZE];
205 char *format_ptr = format;
206 *format_ptr++ = '%';
207 if ((flags & PLUS_FLAG) != 0)
208 *format_ptr++ = '+';
209 if ((flags & ZERO_FLAG) != 0)
210 *format_ptr++ = '0';
211 if (width > 0)
212 *format_ptr++ = '*';
213 if (precision >= 0) {
214 *format_ptr++ = '.';
215 *format_ptr++ = '*';
216 }
217 if (IsLongDouble<T>::VALUE)
218 *format_ptr++ = 'L';
Victor Zverovichd73306b2012-12-10 12:16:02 -0800219 *format_ptr++ = type;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800220 *format_ptr = '\0';
221
222 // Format using snprintf.
Victor Zverovichb076df42012-12-07 08:31:09 -0800223 size_t offset = buffer_.size();
Victor Zverovichb076df42012-12-07 08:31:09 -0800224 for (;;) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800225 size_t size = buffer_.capacity() - offset;
Victor Zverovichb076df42012-12-07 08:31:09 -0800226 int n = 0;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800227 if (width <= 0) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800228 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800229 snprintf(&buffer_[offset], size, format, value) :
230 snprintf(&buffer_[offset], size, format, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800231 } else {
232 n = precision < 0 ?
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800233 snprintf(&buffer_[offset], size, format, width, value) :
234 snprintf(&buffer_[offset], size, format, width, precision, value);
Victor Zverovichb076df42012-12-07 08:31:09 -0800235 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800236 if (n >= 0 && offset + n < buffer_.capacity()) {
Victor Zverovich14e0f872012-12-10 18:08:04 -0800237 GrowBuffer(n);
Victor Zverovichb076df42012-12-07 08:31:09 -0800238 return;
239 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800240 buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
Victor Zverovichb076df42012-12-07 08:31:09 -0800241 }
242}
243
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800244void fmt::Formatter::DoFormat() {
Victor Zverovichb076df42012-12-07 08:31:09 -0800245 const char *start = format_;
Victor Zverovich57dbd2c2012-12-11 12:23:52 -0800246 format_ = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800247 const char *s = start;
Victor Zverovich5f3ed202012-12-07 17:48:10 -0800248 while (*s) {
Victor Zverovich31a50702012-12-10 15:04:55 -0800249 char c = *s++;
250 if (c != '{' && c != '}') continue;
251 if (*s == c) {
Victor Zverovich198ebe92012-12-10 17:16:08 -0800252 buffer_.append(start, s);
Victor Zverovich31a50702012-12-10 15:04:55 -0800253 start = ++s;
254 continue;
255 }
256 if (c == '}')
257 throw FormatError("unmatched '}' in format");
Victor Zverovich198ebe92012-12-10 17:16:08 -0800258 buffer_.append(start, s - 1);
Victor Zverovichb076df42012-12-07 08:31:09 -0800259
260 // Parse argument index.
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800261 if (*s < '0' || *s > '9')
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800262 ReportError(s, "missing argument index in format string");
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800263 unsigned arg_index = ParseUInt(s);
Victor Zverovich63539c02012-12-08 08:17:12 -0800264 if (arg_index >= args_.size())
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800265 ReportError(s, "argument index is out of range in format");
Victor Zverovich4db5a662012-12-11 10:27:13 -0800266 const Arg &arg = *args_[arg_index];
Victor Zverovichb076df42012-12-07 08:31:09 -0800267
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800268 unsigned flags = 0;
269 int width = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800270 int precision = -1;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800271 char type = 0;
Victor Zverovichb076df42012-12-07 08:31:09 -0800272 if (*s == ':') {
273 ++s;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800274 if (*s == '+') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800275 ++s;
276 if (arg.type > LAST_NUMERIC_TYPE)
277 ReportError(s, "format specifier '+' requires numeric argument");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800278 if (arg.type == UINT || arg.type == ULONG) {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800279 ReportError(s,
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800280 "format specifier '+' requires signed argument");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800281 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800282 flags |= PLUS_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800283 }
284 if (*s == '0') {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800285 ++s;
286 if (arg.type > LAST_NUMERIC_TYPE)
287 ReportError(s, "format specifier '0' requires numeric argument");
288 flags |= ZERO_FLAG;
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800289 }
Victor Zverovichb076df42012-12-07 08:31:09 -0800290
291 // Parse width.
292 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800293 unsigned value = ParseUInt(s);
294 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800295 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800296 width = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800297 }
298
299 // Parse precision.
300 if (*s == '.') {
Victor Zverovichb076df42012-12-07 08:31:09 -0800301 ++s;
302 precision = 0;
303 if ('0' <= *s && *s <= '9') {
Victor Zverovich095b43a2012-12-09 11:32:39 -0800304 unsigned value = ParseUInt(s);
305 if (value > INT_MAX)
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800306 ReportError(s, "number is too big in format");
Victor Zverovich095b43a2012-12-09 11:32:39 -0800307 precision = value;
Victor Zverovichb076df42012-12-07 08:31:09 -0800308 } else {
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800309 ReportError(s, "missing precision in format");
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800310 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800311 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
312 ReportError(s,
313 "precision specifier requires floating-point argument");
Victor Zverovichb076df42012-12-07 08:31:09 -0800314 }
315 }
316
317 // Parse type.
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800318 if (*s != '}' && *s)
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800319 type = *s++;
Victor Zverovichb076df42012-12-07 08:31:09 -0800320 }
321
322 if (*s++ != '}')
Victor Zverovich8b1c7092012-12-08 18:45:35 -0800323 throw FormatError("unmatched '{' in format");
Victor Zverovichb076df42012-12-07 08:31:09 -0800324 start = s;
325
326 // Format argument.
Victor Zverovichb076df42012-12-07 08:31:09 -0800327 switch (arg.type) {
Victor Zverovichb076df42012-12-07 08:31:09 -0800328 case INT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800329 FormatInt(arg.int_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800330 break;
331 case UINT:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800332 FormatInt(arg.uint_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800333 break;
334 case LONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800335 FormatInt(arg.long_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800336 break;
337 case ULONG:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800338 FormatInt(arg.ulong_value, flags, width, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800339 break;
340 case DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800341 FormatDouble(arg.double_value, flags, width, precision, type);
Victor Zverovichb076df42012-12-07 08:31:09 -0800342 break;
343 case LONG_DOUBLE:
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800344 FormatDouble(arg.long_double_value, flags, width, precision, type);
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800345 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800346 case CHAR: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800347 if (type && type != 'c')
348 ReportUnknownType(type, "char");
Victor Zverovich14e0f872012-12-10 18:08:04 -0800349 char *out = GrowBuffer(std::max(width, 1));
Victor Zverovich198ebe92012-12-10 17:16:08 -0800350 *out++ = arg.int_value;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800351 if (width > 1)
Victor Zverovich198ebe92012-12-10 17:16:08 -0800352 std::fill_n(out, width - 1, ' ');
Victor Zverovichbbd13a42012-12-09 14:13:23 -0800353 break;
Victor Zverovich198ebe92012-12-10 17:16:08 -0800354 }
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800355 case STRING: {
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800356 if (type && type != 's')
357 ReportUnknownType(type, "string");
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800358 const char *str = arg.string.value;
359 size_t size = arg.string.size;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800360 if (size == 0 && *str)
361 size = std::strlen(str);
Victor Zverovich14e0f872012-12-10 18:08:04 -0800362 char *out = GrowBuffer(std::max<size_t>(width, size));
363 out = std::copy(str, str + size, out);
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800364 if (static_cast<unsigned>(width) > size)
Victor Zverovich14e0f872012-12-10 18:08:04 -0800365 std::fill_n(out, width - size, ' ');
Victor Zverovichb076df42012-12-07 08:31:09 -0800366 break;
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800367 }
368 case POINTER:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800369 if (type && type != 'p')
370 ReportUnknownType(type, "pointer");
Victor Zverovichb98b2e92012-12-10 11:08:16 -0800371 FormatInt(reinterpret_cast<uintptr_t>(
372 arg.pointer_value), HEX_PREFIX_FLAG, width, 'x');
Victor Zverovichb076df42012-12-07 08:31:09 -0800373 break;
Victor Zverovich280ea8b2012-12-09 09:03:47 -0800374 case CUSTOM:
Victor Zverovichd599e3b2012-12-10 13:30:06 -0800375 if (type)
376 ReportUnknownType(type, "object");
Victor Zverovich33bb6ee2012-12-11 21:47:05 -0800377 (this->*arg.custom.format)(arg.custom.value, width);
Victor Zverovich63539c02012-12-08 08:17:12 -0800378 break;
Victor Zverovichb076df42012-12-07 08:31:09 -0800379 default:
380 assert(false);
381 break;
382 }
383 }
Victor Zverovich198ebe92012-12-10 17:16:08 -0800384 buffer_.append(start, s + 1);
Victor Zverovich0996e982012-12-10 20:37:35 -0800385 buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero.
Victor Zverovichb076df42012-12-07 08:31:09 -0800386}