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