Copyright 2014->2015
[folly.git] / folly / Format.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/Format.h>
18
19 namespace folly {
20 namespace detail {
21
22 extern const FormatArg::Align formatAlignTable[];
23 extern const FormatArg::Sign formatSignTable[];
24
25 }  // namespace detail
26
27 using namespace folly::detail;
28
29 void FormatArg::initSlow() {
30   auto b = fullArgString.begin();
31   auto end = fullArgString.end();
32
33   // Parse key
34   auto p = static_cast<const char*>(memchr(b, ':', end - b));
35   if (!p) {
36     key_ = StringPiece(b, end);
37     return;
38   }
39   key_ = StringPiece(b, p);
40
41   if (*p == ':') {
42     // parse format spec
43     if (++p == end) return;
44
45     // fill/align, or just align
46     Align a;
47     if (p + 1 != end &&
48         (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
49         Align::INVALID) {
50       fill = *p;
51       align = a;
52       p += 2;
53       if (p == end) return;
54     } else if ((a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
55                Align::INVALID) {
56       align = a;
57       if (++p == end) return;
58     }
59
60     Sign s;
61     unsigned char uSign = static_cast<unsigned char>(*p);
62     if ((s = formatSignTable[uSign]) != Sign::INVALID) {
63       sign = s;
64       if (++p == end) return;
65     }
66
67     if (*p == '#') {
68       basePrefix = true;
69       if (++p == end) return;
70     }
71
72     if (*p == '0') {
73       enforce(align == Align::DEFAULT, "alignment specified twice");
74       fill = '0';
75       align = Align::PAD_AFTER_SIGN;
76       if (++p == end) return;
77     }
78
79     if (*p >= '0' && *p <= '9') {
80       auto b = p;
81       do {
82         ++p;
83       } while (p != end && *p >= '0' && *p <= '9');
84       width = to<int>(StringPiece(b, p));
85
86       if (p == end) return;
87     }
88
89     if (*p == ',') {
90       thousandsSeparator = true;
91       if (++p == end) return;
92     }
93
94     if (*p == '.') {
95       auto b = ++p;
96       while (p != end && *p >= '0' && *p <= '9') {
97         ++p;
98       }
99       if (p != b) {
100         precision = to<int>(StringPiece(b, p));
101         if (p != end && *p == '.') {
102           trailingDot = true;
103           ++p;
104         }
105       } else {
106         trailingDot = true;
107       }
108
109       if (p == end) return;
110     }
111
112     presentation = *p;
113     if (++p == end) return;
114   }
115
116   error("extra characters in format string");
117 }
118
119 void FormatArg::validate(Type type) const {
120   enforce(keyEmpty(), "index not allowed");
121   switch (type) {
122   case Type::INTEGER:
123     enforce(precision == kDefaultPrecision,
124             "precision not allowed on integers");
125     break;
126   case Type::FLOAT:
127     enforce(!basePrefix,
128             "base prefix ('#') specifier only allowed on integers");
129     enforce(!thousandsSeparator,
130             "thousands separator (',') only allowed on integers");
131     break;
132   case Type::OTHER:
133     enforce(align != Align::PAD_AFTER_SIGN,
134             "'='alignment only allowed on numbers");
135     enforce(sign == Sign::DEFAULT,
136             "sign specifier only allowed on numbers");
137     enforce(!basePrefix,
138             "base prefix ('#') specifier only allowed on integers");
139     enforce(!thousandsSeparator,
140             "thousands separator (',') only allowed on integers");
141     break;
142   }
143 }
144
145 namespace detail {
146 void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
147   uint32_t remaining_digits = *end_buffer - start_buffer;
148   uint32_t separator_size = (remaining_digits - 1) / 3;
149   uint32_t result_size = remaining_digits + separator_size;
150   *end_buffer = *end_buffer + separator_size;
151
152   // get the end of the new string with the separators
153   uint32_t buffer_write_index = result_size - 1;
154   uint32_t buffer_read_index = remaining_digits - 1;
155   start_buffer[buffer_write_index + 1] = 0;
156
157   uint32_t count = 0;
158   bool done = false;
159   uint32_t next_group_size = 3;
160
161   while (!done) {
162     uint32_t current_group_size = std::max<uint32_t>(1,
163       std::min<uint32_t>(remaining_digits, next_group_size));
164
165     // write out the current group's digits to the buffer index
166     for (uint32_t i = 0; i < current_group_size; i++) {
167       start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
168     }
169
170     // if not finished, write the separator before the next group
171     if (buffer_write_index < buffer_write_index + 1) {
172       start_buffer[buffer_write_index--] = ',';
173     } else {
174       done = true;
175     }
176
177     remaining_digits -= current_group_size;
178   }
179 }
180 } // detail
181
182 }  // namespace folly