FOLLY_NORETURN
[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   void error(Args&&... args) const FOLLY_NORETURN;
76   /**
77    * Full argument string, as passed in to the constructor.
78    */
79   StringPiece fullArgString;
80
81   /**
82    * Fill
83    */
84   static constexpr char kDefaultFill = '\0';
85   char fill;
86
87   /**
88    * Alignment
89    */
90   enum class Align : uint8_t {
91     DEFAULT,
92     LEFT,
93     RIGHT,
94     PAD_AFTER_SIGN,
95     CENTER,
96     INVALID
97   };
98   Align align;
99
100   /**
101    * Sign
102    */
103   enum class Sign : uint8_t {
104     DEFAULT,
105     PLUS_OR_MINUS,
106     MINUS,
107     SPACE_OR_MINUS,
108     INVALID
109   };
110   Sign sign;
111
112   /**
113    * Output base prefix (0 for octal, 0x for hex)
114    */
115   bool basePrefix;
116
117   /**
118    * Output thousands separator (comma)
119    */
120   bool thousandsSeparator;
121
122   /**
123    * Field width
124    */
125   static constexpr int kDefaultWidth = -1;
126   int width;
127
128   /**
129    * Precision
130    */
131   static constexpr int kDefaultPrecision = -1;
132   int precision;
133
134   /**
135    * Presentation
136    */
137   static constexpr char kDefaultPresentation = '\0';
138   char presentation;
139
140   /**
141    * Split a key component from "key", which must be non-empty (an exception
142    * is thrown otherwise).
143    */
144   template <bool emptyOk=false>
145   StringPiece splitKey();
146
147   /**
148    * Is the entire key empty?
149    */
150   bool keyEmpty() const {
151     return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
152   }
153
154   /**
155    * Split an key component from "key", which must be non-empty and a valid
156    * integer (an exception is thrown otherwise).
157    */
158   int splitIntKey();
159
160   void setNextIntKey(int val) {
161     assert(nextKeyMode_ == NextKeyMode::NONE);
162     nextKeyMode_ = NextKeyMode::INT;
163     nextIntKey_ = val;
164   }
165
166   void setNextKey(StringPiece val) {
167     assert(nextKeyMode_ == NextKeyMode::NONE);
168     nextKeyMode_ = NextKeyMode::STRING;
169     nextKey_ = val;
170   }
171
172  private:
173   void initSlow();
174   template <bool emptyOk>
175   StringPiece doSplitKey();
176
177   StringPiece key_;
178   int nextIntKey_;
179   StringPiece nextKey_;
180   enum class NextKeyMode {
181     NONE,
182     INT,
183     STRING,
184   };
185   NextKeyMode nextKeyMode_;
186 };
187
188 template <typename... Args>
189 inline void FormatArg::error(Args&&... args) const {
190   throw std::invalid_argument(to<std::string>(
191       "folly::format: invalid format argument {", fullArgString, "}: ",
192       std::forward<Args>(args)...));
193 }
194
195 template <bool emptyOk>
196 inline StringPiece FormatArg::splitKey() {
197   enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
198   return doSplitKey<emptyOk>();
199 }
200
201 template <bool emptyOk>
202 inline StringPiece FormatArg::doSplitKey() {
203   if (nextKeyMode_ == NextKeyMode::STRING) {
204     nextKeyMode_ = NextKeyMode::NONE;
205     if (!emptyOk) {  // static
206       enforce(!nextKey_.empty(), "non-empty key required");
207     }
208     return nextKey_;
209   }
210
211   if (key_.empty()) {
212     if (!emptyOk) {  // static
213       error("non-empty key required");
214     }
215     return StringPiece();
216   }
217
218   const char* b = key_.begin();
219   const char* e = key_.end();
220   const char* p;
221   if (e[-1] == ']') {
222     --e;
223     p = static_cast<const char*>(memchr(b, '[', e - b));
224     enforce(p, "unmatched ']'");
225   } else {
226     p = static_cast<const char*>(memchr(b, '.', e - b));
227   }
228   if (p) {
229     key_.assign(p + 1, e);
230   } else {
231     p = e;
232     key_.clear();
233   }
234   if (!emptyOk) {  // static
235     enforce(b != p, "non-empty key required");
236   }
237   return StringPiece(b, p);
238 }
239
240 inline int FormatArg::splitIntKey() {
241   if (nextKeyMode_ == NextKeyMode::INT) {
242     nextKeyMode_ = NextKeyMode::NONE;
243     return nextIntKey_;
244   }
245   try {
246     return to<int>(doSplitKey<true>());
247   } catch (const std::out_of_range& e) {
248     error("integer key required");
249     return 0;  // unreached
250   }
251 }
252
253 }  // namespace folly
254
255 #endif /* FOLLY_FORMATARG_H_ */
256