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