• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2017 The Abseil Authors.
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 //      https://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 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
17 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
18 
19 #include <limits.h>
20 
21 #include <cstddef>
22 #include <cstring>
23 #include <ostream>
24 
25 #include "absl/base/config.h"
26 #include "absl/base/port.h"
27 #include "absl/meta/type_traits.h"
28 #include "absl/strings/internal/str_format/output.h"
29 #include "absl/strings/string_view.h"
30 
31 namespace absl {
32 ABSL_NAMESPACE_BEGIN
33 
34 enum class FormatConversionChar : uint8_t;
35 enum class FormatConversionCharSet : uint64_t;
36 
37 namespace str_format_internal {
38 
39 class FormatRawSinkImpl {
40  public:
41   // Implicitly convert from any type that provides the hook function as
42   // described above.
43   template <typename T, decltype(str_format_internal::InvokeFlush(
44                             std::declval<T*>(), string_view()))* = nullptr>
FormatRawSinkImpl(T * raw)45   FormatRawSinkImpl(T* raw)  // NOLINT
46       : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
47 
Write(string_view s)48   void Write(string_view s) { write_(sink_, s); }
49 
50   template <typename T>
Extract(T s)51   static FormatRawSinkImpl Extract(T s) {
52     return s.sink_;
53   }
54 
55  private:
56   template <typename T>
Flush(void * r,string_view s)57   static void Flush(void* r, string_view s) {
58     str_format_internal::InvokeFlush(static_cast<T*>(r), s);
59   }
60 
61   void* sink_;
62   void (*write_)(void*, string_view);
63 };
64 
65 // An abstraction to which conversions write their string data.
66 class FormatSinkImpl {
67  public:
FormatSinkImpl(FormatRawSinkImpl raw)68   explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
69 
~FormatSinkImpl()70   ~FormatSinkImpl() { Flush(); }
71 
Flush()72   void Flush() {
73     raw_.Write(string_view(buf_, pos_ - buf_));
74     pos_ = buf_;
75   }
76 
Append(size_t n,char c)77   void Append(size_t n, char c) {
78     if (n == 0) return;
79     size_ += n;
80     auto raw_append = [&](size_t count) {
81       memset(pos_, c, count);
82       pos_ += count;
83     };
84     while (n > Avail()) {
85       n -= Avail();
86       if (Avail() > 0) {
87         raw_append(Avail());
88       }
89       Flush();
90     }
91     raw_append(n);
92   }
93 
Append(string_view v)94   void Append(string_view v) {
95     size_t n = v.size();
96     if (n == 0) return;
97     size_ += n;
98     if (n >= Avail()) {
99       Flush();
100       raw_.Write(v);
101       return;
102     }
103     memcpy(pos_, v.data(), n);
104     pos_ += n;
105   }
106 
size()107   size_t size() const { return size_; }
108 
109   // Put 'v' to 'sink' with specified width, precision, and left flag.
110   bool PutPaddedString(string_view v, int width, int precision, bool left);
111 
112   template <typename T>
Wrap()113   T Wrap() {
114     return T(this);
115   }
116 
117   template <typename T>
Extract(T * s)118   static FormatSinkImpl* Extract(T* s) {
119     return s->sink_;
120   }
121 
122  private:
Avail()123   size_t Avail() const { return buf_ + sizeof(buf_) - pos_; }
124 
125   FormatRawSinkImpl raw_;
126   size_t size_ = 0;
127   char* pos_ = buf_;
128   char buf_[1024];
129 };
130 
131 struct Flags {
132   bool basic : 1;     // fastest conversion: no flags, width, or precision
133   bool left : 1;      // "-"
134   bool show_pos : 1;  // "+"
135   bool sign_col : 1;  // " "
136   bool alt : 1;       // "#"
137   bool zero : 1;      // "0"
138   std::string ToString() const;
139   friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
140     return os << v.ToString();
141   }
142 };
143 
144 // clang-format off
145 #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
146   /* text */ \
147   X_VAL(c) X_SEP X_VAL(s) X_SEP \
148   /* ints */ \
149   X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
150   X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
151   /* floats */ \
152   X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
153   X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
154   /* misc */ \
155   X_VAL(n) X_SEP X_VAL(p)
156 // clang-format on
157 
158 // This type should not be referenced, it exists only to provide labels
159 // internally that match the values declared in FormatConversionChar in
160 // str_format.h. This is meant to allow internal libraries to use the same
161 // declared interface type as the public interface
162 // (absl::StrFormatConversionChar) while keeping the definition in a public
163 // header.
164 // Internal libraries should use the form
165 // `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for
166 // comparisons.  Use in switch statements is not recommended due to a bug in how
167 // gcc 4.9 -Wswitch handles declared but undefined enums.
168 struct FormatConversionCharInternal {
169   FormatConversionCharInternal() = delete;
170 
171  private:
172   // clang-format off
173   enum class Enum : uint8_t {
174     c, s,                    // text
175     d, i, o, u, x, X,        // int
176     f, F, e, E, g, G, a, A,  // float
177     n, p,                    // misc
178     kNone
179   };
180   // clang-format on
181  public:
182 #define ABSL_INTERNAL_X_VAL(id)              \
183   static constexpr FormatConversionChar id = \
184       static_cast<FormatConversionChar>(Enum::id);
185   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
186 #undef ABSL_INTERNAL_X_VAL
187   static constexpr FormatConversionChar kNone =
188       static_cast<FormatConversionChar>(Enum::kNone);
189 };
190 // clang-format on
191 
FormatConversionCharFromChar(char c)192 inline FormatConversionChar FormatConversionCharFromChar(char c) {
193   switch (c) {
194 #define ABSL_INTERNAL_X_VAL(id) \
195   case #id[0]:                  \
196     return FormatConversionCharInternal::id;
197     ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
198 #undef ABSL_INTERNAL_X_VAL
199   }
200   return FormatConversionCharInternal::kNone;
201 }
202 
FormatConversionCharIsUpper(FormatConversionChar c)203 inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
204   if (c == FormatConversionCharInternal::X ||
205       c == FormatConversionCharInternal::F ||
206       c == FormatConversionCharInternal::E ||
207       c == FormatConversionCharInternal::G ||
208       c == FormatConversionCharInternal::A) {
209     return true;
210   } else {
211     return false;
212   }
213 }
214 
FormatConversionCharIsFloat(FormatConversionChar c)215 inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
216   if (c == FormatConversionCharInternal::a ||
217       c == FormatConversionCharInternal::e ||
218       c == FormatConversionCharInternal::f ||
219       c == FormatConversionCharInternal::g ||
220       c == FormatConversionCharInternal::A ||
221       c == FormatConversionCharInternal::E ||
222       c == FormatConversionCharInternal::F ||
223       c == FormatConversionCharInternal::G) {
224     return true;
225   } else {
226     return false;
227   }
228 }
229 
FormatConversionCharToChar(FormatConversionChar c)230 inline char FormatConversionCharToChar(FormatConversionChar c) {
231   if (c == FormatConversionCharInternal::kNone) {
232     return '\0';
233 
234 #define ABSL_INTERNAL_X_VAL(e)                       \
235   } else if (c == FormatConversionCharInternal::e) { \
236     return #e[0];
237 #define ABSL_INTERNAL_X_SEP
238   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL,
239                                          ABSL_INTERNAL_X_SEP)
240   } else {
241     return '\0';
242   }
243 
244 #undef ABSL_INTERNAL_X_VAL
245 #undef ABSL_INTERNAL_X_SEP
246 }
247 
248 // The associated char.
249 inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
250   char c = FormatConversionCharToChar(v);
251   if (!c) c = '?';
252   return os << c;
253 }
254 
255 struct FormatConversionSpecImplFriend;
256 
257 class FormatConversionSpecImpl {
258  public:
259   // Width and precison are not specified, no flags are set.
is_basic()260   bool is_basic() const { return flags_.basic; }
has_left_flag()261   bool has_left_flag() const { return flags_.left; }
has_show_pos_flag()262   bool has_show_pos_flag() const { return flags_.show_pos; }
has_sign_col_flag()263   bool has_sign_col_flag() const { return flags_.sign_col; }
has_alt_flag()264   bool has_alt_flag() const { return flags_.alt; }
has_zero_flag()265   bool has_zero_flag() const { return flags_.zero; }
266 
conversion_char()267   FormatConversionChar conversion_char() const {
268     // Keep this field first in the struct . It generates better code when
269     // accessing it when ConversionSpec is passed by value in registers.
270     static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, "");
271     return conv_;
272   }
273 
274   // Returns the specified width. If width is unspecfied, it returns a negative
275   // value.
width()276   int width() const { return width_; }
277   // Returns the specified precision. If precision is unspecfied, it returns a
278   // negative value.
precision()279   int precision() const { return precision_; }
280 
281   template <typename T>
Wrap()282   T Wrap() {
283     return T(*this);
284   }
285 
286  private:
287   friend struct str_format_internal::FormatConversionSpecImplFriend;
288   FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
289   Flags flags_;
290   int width_;
291   int precision_;
292 };
293 
294 struct FormatConversionSpecImplFriend final {
SetFlagsfinal295   static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
296     conv->flags_ = f;
297   }
SetConversionCharfinal298   static void SetConversionChar(FormatConversionChar c,
299                                 FormatConversionSpecImpl* conv) {
300     conv->conv_ = c;
301   }
SetWidthfinal302   static void SetWidth(int w, FormatConversionSpecImpl* conv) {
303     conv->width_ = w;
304   }
SetPrecisionfinal305   static void SetPrecision(int p, FormatConversionSpecImpl* conv) {
306     conv->precision_ = p;
307   }
FlagsToStringfinal308   static std::string FlagsToString(const FormatConversionSpecImpl& spec) {
309     return spec.flags_.ToString();
310   }
311 };
312 
313 // Type safe OR operator.
314 // We need this for two reasons:
315 //  1. operator| on enums makes them decay to integers and the result is an
316 //     integer. We need the result to stay as an enum.
317 //  2. We use "enum class" which would not work even if we accepted the decay.
FormatConversionCharSetUnion(FormatConversionCharSet a)318 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
319     FormatConversionCharSet a) {
320   return a;
321 }
322 
323 template <typename... CharSet>
FormatConversionCharSetUnion(FormatConversionCharSet a,CharSet...rest)324 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
325     FormatConversionCharSet a, CharSet... rest) {
326   return static_cast<FormatConversionCharSet>(
327       static_cast<uint64_t>(a) |
328       static_cast<uint64_t>(FormatConversionCharSetUnion(rest...)));
329 }
330 
FormatConversionCharToConvInt(FormatConversionChar c)331 constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) {
332   return uint64_t{1} << (1 + static_cast<uint8_t>(c));
333 }
334 
FormatConversionCharToConvInt(char conv)335 constexpr uint64_t FormatConversionCharToConvInt(char conv) {
336   return
337 #define ABSL_INTERNAL_CHAR_SET_CASE(c)                                 \
338   conv == #c[0]                                                        \
339       ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \
340       :
341       ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
342 #undef ABSL_INTERNAL_CHAR_SET_CASE
343                   conv == '*'
344           ? 1
345           : 0;
346 }
347 
FormatConversionCharToConvValue(char conv)348 constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) {
349   return static_cast<FormatConversionCharSet>(
350       FormatConversionCharToConvInt(conv));
351 }
352 
353 struct FormatConversionCharSetInternal {
354 #define ABSL_INTERNAL_CHAR_SET_CASE(c)         \
355   static constexpr FormatConversionCharSet c = \
356       FormatConversionCharToConvValue(#c[0]);
357   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
358 #undef ABSL_INTERNAL_CHAR_SET_CASE
359 
360   // Used for width/precision '*' specification.
361   static constexpr FormatConversionCharSet kStar =
362       FormatConversionCharToConvValue('*');
363 
364   static constexpr FormatConversionCharSet kIntegral =
365       FormatConversionCharSetUnion(d, i, u, o, x, X);
366   static constexpr FormatConversionCharSet kFloating =
367       FormatConversionCharSetUnion(a, e, f, g, A, E, F, G);
368   static constexpr FormatConversionCharSet kNumeric =
369       FormatConversionCharSetUnion(kIntegral, kFloating);
370   static constexpr FormatConversionCharSet kPointer = p;
371 };
372 
373 // Type safe OR operator.
374 // We need this for two reasons:
375 //  1. operator| on enums makes them decay to integers and the result is an
376 //     integer. We need the result to stay as an enum.
377 //  2. We use "enum class" which would not work even if we accepted the decay.
378 constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
379                                             FormatConversionCharSet b) {
380   return FormatConversionCharSetUnion(a, b);
381 }
382 
383 // Overloaded conversion functions to support absl::ParsedFormat.
384 // Get a conversion with a single character in it.
ToFormatConversionCharSet(char c)385 constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) {
386   return static_cast<FormatConversionCharSet>(
387       FormatConversionCharToConvValue(c));
388 }
389 
390 // Get a conversion with a single character in it.
ToFormatConversionCharSet(FormatConversionCharSet c)391 constexpr FormatConversionCharSet ToFormatConversionCharSet(
392     FormatConversionCharSet c) {
393   return c;
394 }
395 
396 template <typename T>
397 void ToFormatConversionCharSet(T) = delete;
398 
399 // Checks whether `c` exists in `set`.
Contains(FormatConversionCharSet set,char c)400 constexpr bool Contains(FormatConversionCharSet set, char c) {
401   return (static_cast<uint64_t>(set) &
402           static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0;
403 }
404 
405 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionCharSet c)406 constexpr bool Contains(FormatConversionCharSet set,
407                         FormatConversionCharSet c) {
408   return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
409          static_cast<uint64_t>(c);
410 }
411 
412 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionChar c)413 constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) {
414   return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0;
415 }
416 
417 // Return capacity - used, clipped to a minimum of 0.
Excess(size_t used,size_t capacity)418 inline size_t Excess(size_t used, size_t capacity) {
419   return used < capacity ? capacity - used : 0;
420 }
421 
422 }  // namespace str_format_internal
423 
424 ABSL_NAMESPACE_END
425 }  // namespace absl
426 
427 #endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
428