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