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