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