xref: /aosp_15_r20/external/abseil-cpp/absl/strings/internal/str_format/extension.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
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 
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <ostream>
24 #include <string>
25 
26 #include "absl/base/config.h"
27 #include "absl/strings/internal/str_format/output.h"
28 #include "absl/strings/string_view.h"
29 
30 namespace absl {
31 ABSL_NAMESPACE_BEGIN
32 
33 enum class FormatConversionChar : uint8_t;
34 enum class FormatConversionCharSet : uint64_t;
35 enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
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_, static_cast<size_t>(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 {
124     return static_cast<size_t>(buf_ + sizeof(buf_) - pos_);
125   }
126 
127   FormatRawSinkImpl raw_;
128   size_t size_ = 0;
129   char* pos_ = buf_;
130   char buf_[1024];
131 };
132 
133 enum class Flags : uint8_t {
134   kBasic = 0,
135   kLeft = 1 << 0,
136   kShowPos = 1 << 1,
137   kSignCol = 1 << 2,
138   kAlt = 1 << 3,
139   kZero = 1 << 4,
140   // This is not a real flag. It just exists to turn off kBasic when no other
141   // flags are set. This is for when width/precision are specified, or a length
142   // modifier affects the behavior ("%lc").
143   kNonBasic = 1 << 5,
144 };
145 
146 constexpr Flags operator|(Flags a, Flags b) {
147   return static_cast<Flags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
148 }
149 
FlagsContains(Flags haystack,Flags needle)150 constexpr bool FlagsContains(Flags haystack, Flags needle) {
151   return (static_cast<uint8_t>(haystack) & static_cast<uint8_t>(needle)) ==
152          static_cast<uint8_t>(needle);
153 }
154 
155 std::string FlagsToString(Flags v);
156 
157 inline std::ostream& operator<<(std::ostream& os, Flags v) {
158   return os << FlagsToString(v);
159 }
160 
161 // clang-format off
162 #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
163   /* text */ \
164   X_VAL(c) X_SEP X_VAL(s) X_SEP \
165   /* ints */ \
166   X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
167   X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
168   /* floats */ \
169   X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
170   X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
171   /* misc */ \
172   X_VAL(n) X_SEP X_VAL(p) X_SEP X_VAL(v)
173 // clang-format on
174 
175 // This type should not be referenced, it exists only to provide labels
176 // internally that match the values declared in FormatConversionChar in
177 // str_format.h. This is meant to allow internal libraries to use the same
178 // declared interface type as the public interface
179 // (absl::StrFormatConversionChar) while keeping the definition in a public
180 // header.
181 // Internal libraries should use the form
182 // `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for
183 // comparisons.  Use in switch statements is not recommended due to a bug in how
184 // gcc 4.9 -Wswitch handles declared but undefined enums.
185 struct FormatConversionCharInternal {
186   FormatConversionCharInternal() = delete;
187 
188  private:
189   // clang-format off
190   enum class Enum : uint8_t {
191     c, s,                    // text
192     d, i, o, u, x, X,        // int
193     f, F, e, E, g, G, a, A,  // float
194     n, p, v,                    // misc
195     kNone
196   };
197   // clang-format on
198  public:
199 #define ABSL_INTERNAL_X_VAL(id)              \
200   static constexpr FormatConversionChar id = \
201       static_cast<FormatConversionChar>(Enum::id);
202   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
203 #undef ABSL_INTERNAL_X_VAL
204   static constexpr FormatConversionChar kNone =
205       static_cast<FormatConversionChar>(Enum::kNone);
206 };
207 // clang-format on
208 
FormatConversionCharFromChar(char c)209 inline FormatConversionChar FormatConversionCharFromChar(char c) {
210   switch (c) {
211 #define ABSL_INTERNAL_X_VAL(id) \
212   case #id[0]:                  \
213     return FormatConversionCharInternal::id;
214     ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
215 #undef ABSL_INTERNAL_X_VAL
216   }
217   return FormatConversionCharInternal::kNone;
218 }
219 
FormatConversionCharIsUpper(FormatConversionChar c)220 inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
221   if (c == FormatConversionCharInternal::X ||
222       c == FormatConversionCharInternal::F ||
223       c == FormatConversionCharInternal::E ||
224       c == FormatConversionCharInternal::G ||
225       c == FormatConversionCharInternal::A) {
226     return true;
227   } else {
228     return false;
229   }
230 }
231 
FormatConversionCharIsFloat(FormatConversionChar c)232 inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
233   if (c == FormatConversionCharInternal::a ||
234       c == FormatConversionCharInternal::e ||
235       c == FormatConversionCharInternal::f ||
236       c == FormatConversionCharInternal::g ||
237       c == FormatConversionCharInternal::A ||
238       c == FormatConversionCharInternal::E ||
239       c == FormatConversionCharInternal::F ||
240       c == FormatConversionCharInternal::G) {
241     return true;
242   } else {
243     return false;
244   }
245 }
246 
FormatConversionCharToChar(FormatConversionChar c)247 inline char FormatConversionCharToChar(FormatConversionChar c) {
248   if (c == FormatConversionCharInternal::kNone) {
249     return '\0';
250 
251 #define ABSL_INTERNAL_X_VAL(e)                       \
252   } else if (c == FormatConversionCharInternal::e) { \
253     return #e[0];
254 #define ABSL_INTERNAL_X_SEP
255   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL,
256                                          ABSL_INTERNAL_X_SEP)
257   } else {
258     return '\0';
259   }
260 
261 #undef ABSL_INTERNAL_X_VAL
262 #undef ABSL_INTERNAL_X_SEP
263 }
264 
265 // The associated char.
266 inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
267   char c = FormatConversionCharToChar(v);
268   if (!c) c = '?';
269   return os << c;
270 }
271 
272 struct FormatConversionSpecImplFriend;
273 
274 class FormatConversionSpecImpl {
275  public:
276   // Width and precision are not specified, no flags are set.
is_basic()277   bool is_basic() const { return flags_ == Flags::kBasic; }
has_left_flag()278   bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); }
has_show_pos_flag()279   bool has_show_pos_flag() const {
280     return FlagsContains(flags_, Flags::kShowPos);
281   }
has_sign_col_flag()282   bool has_sign_col_flag() const {
283     return FlagsContains(flags_, Flags::kSignCol);
284   }
has_alt_flag()285   bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); }
has_zero_flag()286   bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); }
287 
length_mod()288   LengthMod length_mod() const { return length_mod_; }
289 
conversion_char()290   FormatConversionChar conversion_char() const {
291     // Keep this field first in the struct . It generates better code when
292     // accessing it when ConversionSpec is passed by value in registers.
293     static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, "");
294     return conv_;
295   }
296 
set_conversion_char(FormatConversionChar c)297   void set_conversion_char(FormatConversionChar c) { conv_ = c; }
298 
299   // Returns the specified width. If width is unspecfied, it returns a negative
300   // value.
width()301   int width() const { return width_; }
302   // Returns the specified precision. If precision is unspecfied, it returns a
303   // negative value.
precision()304   int precision() const { return precision_; }
305 
306   template <typename T>
Wrap()307   T Wrap() {
308     return T(*this);
309   }
310 
311  private:
312   friend struct str_format_internal::FormatConversionSpecImplFriend;
313   FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
314   Flags flags_;
315   LengthMod length_mod_ = LengthMod::none;
316   int width_;
317   int precision_;
318 };
319 
320 struct FormatConversionSpecImplFriend final {
SetFlagsfinal321   static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
322     conv->flags_ = f;
323   }
SetLengthModfinal324   static void SetLengthMod(LengthMod l, FormatConversionSpecImpl* conv) {
325     conv->length_mod_ = l;
326   }
SetConversionCharfinal327   static void SetConversionChar(FormatConversionChar c,
328                                 FormatConversionSpecImpl* conv) {
329     conv->conv_ = c;
330   }
SetWidthfinal331   static void SetWidth(int w, FormatConversionSpecImpl* conv) {
332     conv->width_ = w;
333   }
SetPrecisionfinal334   static void SetPrecision(int p, FormatConversionSpecImpl* conv) {
335     conv->precision_ = p;
336   }
FlagsToStringfinal337   static std::string FlagsToString(const FormatConversionSpecImpl& spec) {
338     return str_format_internal::FlagsToString(spec.flags_);
339   }
340 };
341 
342 // Type safe OR operator.
343 // We need this for two reasons:
344 //  1. operator| on enums makes them decay to integers and the result is an
345 //     integer. We need the result to stay as an enum.
346 //  2. We use "enum class" which would not work even if we accepted the decay.
FormatConversionCharSetUnion(FormatConversionCharSet a)347 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
348     FormatConversionCharSet a) {
349   return a;
350 }
351 
352 template <typename... CharSet>
FormatConversionCharSetUnion(FormatConversionCharSet a,CharSet...rest)353 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
354     FormatConversionCharSet a, CharSet... rest) {
355   return static_cast<FormatConversionCharSet>(
356       static_cast<uint64_t>(a) |
357       static_cast<uint64_t>(FormatConversionCharSetUnion(rest...)));
358 }
359 
FormatConversionCharToConvInt(FormatConversionChar c)360 constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) {
361   return uint64_t{1} << (1 + static_cast<uint8_t>(c));
362 }
363 
FormatConversionCharToConvInt(char conv)364 constexpr uint64_t FormatConversionCharToConvInt(char conv) {
365   return
366 #define ABSL_INTERNAL_CHAR_SET_CASE(c)                                 \
367   conv == #c[0]                                                        \
368       ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \
369       :
370       ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
371 #undef ABSL_INTERNAL_CHAR_SET_CASE
372                   conv == '*'
373           ? 1
374           : 0;
375 }
376 
FormatConversionCharToConvValue(char conv)377 constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) {
378   return static_cast<FormatConversionCharSet>(
379       FormatConversionCharToConvInt(conv));
380 }
381 
382 struct FormatConversionCharSetInternal {
383 #define ABSL_INTERNAL_CHAR_SET_CASE(c)         \
384   static constexpr FormatConversionCharSet c = \
385       FormatConversionCharToConvValue(#c[0]);
386   ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
387 #undef ABSL_INTERNAL_CHAR_SET_CASE
388 
389   // Used for width/precision '*' specification.
390   static constexpr FormatConversionCharSet kStar =
391       FormatConversionCharToConvValue('*');
392 
393   static constexpr FormatConversionCharSet kIntegral =
394       FormatConversionCharSetUnion(d, i, u, o, x, X);
395   static constexpr FormatConversionCharSet kFloating =
396       FormatConversionCharSetUnion(a, e, f, g, A, E, F, G);
397   static constexpr FormatConversionCharSet kNumeric =
398       FormatConversionCharSetUnion(kIntegral, kFloating);
399   static constexpr FormatConversionCharSet kPointer = p;
400 };
401 
402 // Type safe OR operator.
403 // We need this for two reasons:
404 //  1. operator| on enums makes them decay to integers and the result is an
405 //     integer. We need the result to stay as an enum.
406 //  2. We use "enum class" which would not work even if we accepted the decay.
407 constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
408                                             FormatConversionCharSet b) {
409   return FormatConversionCharSetUnion(a, b);
410 }
411 
412 // Overloaded conversion functions to support absl::ParsedFormat.
413 // Get a conversion with a single character in it.
ToFormatConversionCharSet(char c)414 constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) {
415   return static_cast<FormatConversionCharSet>(
416       FormatConversionCharToConvValue(c));
417 }
418 
419 // Get a conversion with a single character in it.
ToFormatConversionCharSet(FormatConversionCharSet c)420 constexpr FormatConversionCharSet ToFormatConversionCharSet(
421     FormatConversionCharSet c) {
422   return c;
423 }
424 
425 template <typename T>
426 void ToFormatConversionCharSet(T) = delete;
427 
428 // Checks whether `c` exists in `set`.
Contains(FormatConversionCharSet set,char c)429 constexpr bool Contains(FormatConversionCharSet set, char c) {
430   return (static_cast<uint64_t>(set) &
431           static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0;
432 }
433 
434 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionCharSet c)435 constexpr bool Contains(FormatConversionCharSet set,
436                         FormatConversionCharSet c) {
437   return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
438          static_cast<uint64_t>(c);
439 }
440 
441 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionChar c)442 constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) {
443   return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0;
444 }
445 
446 // Return capacity - used, clipped to a minimum of 0.
Excess(size_t used,size_t capacity)447 inline size_t Excess(size_t used, size_t capacity) {
448   return used < capacity ? capacity - used : 0;
449 }
450 
451 }  // namespace str_format_internal
452 
453 ABSL_NAMESPACE_END
454 }  // namespace absl
455 
456 #endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
457