folly/wangle -> wangle cutover
[folly.git] / folly / FormatArg.h
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 #ifndef FOLLY_FORMATARG_H_
18 #define FOLLY_FORMATARG_H_
19
20 #include <stdexcept>
21 #include <folly/Conv.h>
22 #include <folly/Likely.h>
23 #include <folly/Portability.h>
24 #include <folly/Range.h>
25
26 namespace folly {
27
28 class BadFormatArg : public std::invalid_argument {
29  public:
30   explicit BadFormatArg(const std::string& msg)
31     : std::invalid_argument(msg) {}
32 };
33
34 /**
35  * Parsed format argument.
36  */
37 struct FormatArg {
38   /**
39    * Parse a format argument from a string.  Keeps a reference to the
40    * passed-in string -- does not copy the given characters.
41    */
42   explicit FormatArg(StringPiece sp)
43     : fullArgString(sp),
44       fill(kDefaultFill),
45       align(Align::DEFAULT),
46       sign(Sign::DEFAULT),
47       basePrefix(false),
48       thousandsSeparator(false),
49       trailingDot(false),
50       width(kDefaultWidth),
51       precision(kDefaultPrecision),
52       presentation(kDefaultPresentation),
53       nextKeyMode_(NextKeyMode::NONE) {
54     if (!sp.empty()) {
55       initSlow();
56     }
57   }
58
59   enum class Type {
60     INTEGER,
61     FLOAT,
62     OTHER
63   };
64   /**
65    * Validate the argument for the given type; throws on error.
66    */
67   void validate(Type type) const;
68
69   /**
70    * Throw an exception if the first argument is false.  The exception
71    * message will contain the argument string as well as any passed-in
72    * arguments to enforce, formatted using folly::to<std::string>.
73    */
74   template <typename... Args>
75   void enforce(bool v, Args&&... args) const {
76     if (UNLIKELY(!v)) {
77       error(std::forward<Args>(args)...);
78     }
79   }
80
81   template <typename... Args>
82   std::string errorStr(Args&&... args) const;
83   template <typename... Args>
84   FOLLY_NORETURN void error(Args&&... args) const;
85
86   /**
87    * Full argument string, as passed in to the constructor.
88    */
89   StringPiece fullArgString;
90
91   /**
92    * Fill
93    */
94   static constexpr char kDefaultFill = '\0';
95   char fill;
96
97   /**
98    * Alignment
99    */
100   enum class Align : uint8_t {
101     DEFAULT,
102     LEFT,
103     RIGHT,
104     PAD_AFTER_SIGN,
105     CENTER,
106     INVALID
107   };
108   Align align;
109
110   /**
111    * Sign
112    */
113   enum class Sign : uint8_t {
114     DEFAULT,
115     PLUS_OR_MINUS,
116     MINUS,
117     SPACE_OR_MINUS,
118     INVALID
119   };
120   Sign sign;
121
122   /**
123    * Output base prefix (0 for octal, 0x for hex)
124    */
125   bool basePrefix;
126
127   /**
128    * Output thousands separator (comma)
129    */
130   bool thousandsSeparator;
131
132   /**
133    * Force a trailing decimal on doubles which could be rendered as ints
134    */
135   bool trailingDot;
136
137   /**
138    * Field width
139    */
140   static constexpr int kDefaultWidth = -1;
141   int width;
142
143   /**
144    * Precision
145    */
146   static constexpr int kDefaultPrecision = -1;
147   int precision;
148
149   /**
150    * Presentation
151    */
152   static constexpr char kDefaultPresentation = '\0';
153   char presentation;
154
155   /**
156    * Split a key component from "key", which must be non-empty (an exception
157    * is thrown otherwise).
158    */
159   template <bool emptyOk=false>
160   StringPiece splitKey();
161
162   /**
163    * Is the entire key empty?
164    */
165   bool keyEmpty() const {
166     return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
167   }
168
169   /**
170    * Split an key component from "key", which must be non-empty and a valid
171    * integer (an exception is thrown otherwise).
172    */
173   int splitIntKey();
174
175   void setNextIntKey(int val) {
176     assert(nextKeyMode_ == NextKeyMode::NONE);
177     nextKeyMode_ = NextKeyMode::INT;
178     nextIntKey_ = val;
179   }
180
181   void setNextKey(StringPiece val) {
182     assert(nextKeyMode_ == NextKeyMode::NONE);
183     nextKeyMode_ = NextKeyMode::STRING;
184     nextKey_ = val;
185   }
186
187  private:
188   void initSlow();
189   template <bool emptyOk>
190   StringPiece doSplitKey();
191
192   StringPiece key_;
193   int nextIntKey_;
194   StringPiece nextKey_;
195   enum class NextKeyMode {
196     NONE,
197     INT,
198     STRING,
199   };
200   NextKeyMode nextKeyMode_;
201 };
202
203 template <typename... Args>
204 inline std::string FormatArg::errorStr(Args&&... args) const {
205   return to<std::string>(
206     "invalid format argument {", fullArgString, "}: ",
207     std::forward<Args>(args)...);
208 }
209
210 template <typename... Args>
211 inline void FormatArg::error(Args&&... args) const {
212   throw BadFormatArg(errorStr(std::forward<Args>(args)...));
213 }
214
215 template <bool emptyOk>
216 inline StringPiece FormatArg::splitKey() {
217   enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
218   return doSplitKey<emptyOk>();
219 }
220
221 template <bool emptyOk>
222 inline StringPiece FormatArg::doSplitKey() {
223   if (nextKeyMode_ == NextKeyMode::STRING) {
224     nextKeyMode_ = NextKeyMode::NONE;
225     if (!emptyOk) {  // static
226       enforce(!nextKey_.empty(), "non-empty key required");
227     }
228     return nextKey_;
229   }
230
231   if (key_.empty()) {
232     if (!emptyOk) {  // static
233       error("non-empty key required");
234     }
235     return StringPiece();
236   }
237
238   const char* b = key_.begin();
239   const char* e = key_.end();
240   const char* p;
241   if (e[-1] == ']') {
242     --e;
243     p = static_cast<const char*>(memchr(b, '[', e - b));
244     enforce(p, "unmatched ']'");
245   } else {
246     p = static_cast<const char*>(memchr(b, '.', e - b));
247   }
248   if (p) {
249     key_.assign(p + 1, e);
250   } else {
251     p = e;
252     key_.clear();
253   }
254   if (!emptyOk) {  // static
255     enforce(b != p, "non-empty key required");
256   }
257   return StringPiece(b, p);
258 }
259
260 inline int FormatArg::splitIntKey() {
261   if (nextKeyMode_ == NextKeyMode::INT) {
262     nextKeyMode_ = NextKeyMode::NONE;
263     return nextIntKey_;
264   }
265   try {
266     return to<int>(doSplitKey<true>());
267   } catch (const std::out_of_range& e) {
268     error("integer key required");
269     return 0;  // unreached
270   }
271 }
272
273 }  // namespace folly
274
275 #endif /* FOLLY_FORMATARG_H_ */