d48aad7a05b277f5b3c1866d7399a49f71b83e8a
[folly.git] / folly / FormatArg.h
1 /*
2  * Copyright 2013 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   CHECK(nextKeyMode_ != NextKeyMode::INT) << errorStr("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     CHECK(emptyOk || !nextKey_.empty()) << errorStr("non-empty key required");
214     return nextKey_;
215   }
216
217   if (key_.empty()) {
218     CHECK(emptyOk) << errorStr("non-empty key required");
219     return StringPiece();
220   }
221
222   const char* b = key_.begin();
223   const char* e = key_.end();
224   const char* p;
225   if (e[-1] == ']') {
226     --e;
227     p = static_cast<const char*>(memchr(b, '[', e - b));
228     CHECK(p) << errorStr("unmatched ']'");
229   } else {
230     p = static_cast<const char*>(memchr(b, '.', e - b));
231   }
232   if (p) {
233     key_.assign(p + 1, e);
234   } else {
235     p = e;
236     key_.clear();
237   }
238   CHECK(emptyOk || b != p) << errorStr("non-empty key required");
239
240   return StringPiece(b, p);
241 }
242
243 inline int FormatArg::splitIntKey() {
244   if (nextKeyMode_ == NextKeyMode::INT) {
245     nextKeyMode_ = NextKeyMode::NONE;
246     return nextIntKey_;
247   }
248   try {
249     return to<int>(doSplitKey<true>());
250   } catch (const std::out_of_range& e) {
251     LOG(FATAL) << errorStr("integer key required");
252     return 0;  // unreached
253   }
254 }
255
256 }  // namespace folly
257
258 #endif /* FOLLY_FORMATARG_H_ */
259