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 <cstdint>
23 #include <cstring>
24 #include <ostream>
25
26 #include "absl/base/config.h"
27 #include "absl/base/port.h"
28 #include "absl/meta/type_traits.h"
29 #include "absl/strings/internal/str_format/output.h"
30 #include "absl/strings/string_view.h"
31
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34
35 enum class FormatConversionChar : uint8_t;
36 enum class FormatConversionCharSet : uint64_t;
37
38 namespace str_format_internal {
39
40 class FormatRawSinkImpl {
41 public:
42 // Implicitly convert from any type that provides the hook function as
43 // described above.
44 template <typename T, decltype(str_format_internal::InvokeFlush(
45 std::declval<T*>(), string_view()))* = nullptr>
FormatRawSinkImpl(T * raw)46 FormatRawSinkImpl(T* raw) // NOLINT
47 : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
48
Write(string_view s)49 void Write(string_view s) { write_(sink_, s); }
50
51 template <typename T>
Extract(T s)52 static FormatRawSinkImpl Extract(T s) {
53 return s.sink_;
54 }
55
56 private:
57 template <typename T>
Flush(void * r,string_view s)58 static void Flush(void* r, string_view s) {
59 str_format_internal::InvokeFlush(static_cast<T*>(r), s);
60 }
61
62 void* sink_;
63 void (*write_)(void*, string_view);
64 };
65
66 // An abstraction to which conversions write their string data.
67 class FormatSinkImpl {
68 public:
FormatSinkImpl(FormatRawSinkImpl raw)69 explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
70
~FormatSinkImpl()71 ~FormatSinkImpl() { Flush(); }
72
Flush()73 void Flush() {
74 raw_.Write(string_view(buf_, static_cast<size_t>(pos_ - buf_)));
75 pos_ = buf_;
76 }
77
Append(size_t n,char c)78 void Append(size_t n, char c) {
79 if (n == 0) return;
80 size_ += n;
81 auto raw_append = [&](size_t count) {
82 memset(pos_, c, count);
83 pos_ += count;
84 };
85 while (n > Avail()) {
86 n -= Avail();
87 if (Avail() > 0) {
88 raw_append(Avail());
89 }
90 Flush();
91 }
92 raw_append(n);
93 }
94
Append(string_view v)95 void Append(string_view v) {
96 size_t n = v.size();
97 if (n == 0) return;
98 size_ += n;
99 if (n >= Avail()) {
100 Flush();
101 raw_.Write(v);
102 return;
103 }
104 memcpy(pos_, v.data(), n);
105 pos_ += n;
106 }
107
size()108 size_t size() const { return size_; }
109
110 // Put 'v' to 'sink' with specified width, precision, and left flag.
111 bool PutPaddedString(string_view v, int width, int precision, bool left);
112
113 template <typename T>
Wrap()114 T Wrap() {
115 return T(this);
116 }
117
118 template <typename T>
Extract(T * s)119 static FormatSinkImpl* Extract(T* s) {
120 return s->sink_;
121 }
122
123 private:
Avail()124 size_t Avail() const {
125 return static_cast<size_t>(buf_ + sizeof(buf_) - pos_);
126 }
127
128 FormatRawSinkImpl raw_;
129 size_t size_ = 0;
130 char* pos_ = buf_;
131 char buf_[1024];
132 };
133
134 enum class Flags : uint8_t {
135 kBasic = 0,
136 kLeft = 1 << 0,
137 kShowPos = 1 << 1,
138 kSignCol = 1 << 2,
139 kAlt = 1 << 3,
140 kZero = 1 << 4,
141 // This is not a real flag. It just exists to turn off kBasic when no other
142 // flags are set. This is for when width/precision are specified.
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 precison 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
conversion_char()288 FormatConversionChar conversion_char() const {
289 // Keep this field first in the struct . It generates better code when
290 // accessing it when ConversionSpec is passed by value in registers.
291 static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, "");
292 return conv_;
293 }
294
set_conversion_char(FormatConversionChar c)295 void set_conversion_char(FormatConversionChar c) { conv_ = c; }
296
297 // Returns the specified width. If width is unspecfied, it returns a negative
298 // value.
width()299 int width() const { return width_; }
300 // Returns the specified precision. If precision is unspecfied, it returns a
301 // negative value.
precision()302 int precision() const { return precision_; }
303
304 template <typename T>
Wrap()305 T Wrap() {
306 return T(*this);
307 }
308
309 private:
310 friend struct str_format_internal::FormatConversionSpecImplFriend;
311 FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
312 Flags flags_;
313 int width_;
314 int precision_;
315 };
316
317 struct FormatConversionSpecImplFriend final {
SetFlagsfinal318 static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
319 conv->flags_ = f;
320 }
SetConversionCharfinal321 static void SetConversionChar(FormatConversionChar c,
322 FormatConversionSpecImpl* conv) {
323 conv->conv_ = c;
324 }
SetWidthfinal325 static void SetWidth(int w, FormatConversionSpecImpl* conv) {
326 conv->width_ = w;
327 }
SetPrecisionfinal328 static void SetPrecision(int p, FormatConversionSpecImpl* conv) {
329 conv->precision_ = p;
330 }
FlagsToStringfinal331 static std::string FlagsToString(const FormatConversionSpecImpl& spec) {
332 return str_format_internal::FlagsToString(spec.flags_);
333 }
334 };
335
336 // Type safe OR operator.
337 // We need this for two reasons:
338 // 1. operator| on enums makes them decay to integers and the result is an
339 // integer. We need the result to stay as an enum.
340 // 2. We use "enum class" which would not work even if we accepted the decay.
FormatConversionCharSetUnion(FormatConversionCharSet a)341 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
342 FormatConversionCharSet a) {
343 return a;
344 }
345
346 template <typename... CharSet>
FormatConversionCharSetUnion(FormatConversionCharSet a,CharSet...rest)347 constexpr FormatConversionCharSet FormatConversionCharSetUnion(
348 FormatConversionCharSet a, CharSet... rest) {
349 return static_cast<FormatConversionCharSet>(
350 static_cast<uint64_t>(a) |
351 static_cast<uint64_t>(FormatConversionCharSetUnion(rest...)));
352 }
353
FormatConversionCharToConvInt(FormatConversionChar c)354 constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) {
355 return uint64_t{1} << (1 + static_cast<uint8_t>(c));
356 }
357
FormatConversionCharToConvInt(char conv)358 constexpr uint64_t FormatConversionCharToConvInt(char conv) {
359 return
360 #define ABSL_INTERNAL_CHAR_SET_CASE(c) \
361 conv == #c[0] \
362 ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \
363 :
364 ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
365 #undef ABSL_INTERNAL_CHAR_SET_CASE
366 conv == '*'
367 ? 1
368 : 0;
369 }
370
FormatConversionCharToConvValue(char conv)371 constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) {
372 return static_cast<FormatConversionCharSet>(
373 FormatConversionCharToConvInt(conv));
374 }
375
376 struct FormatConversionCharSetInternal {
377 #define ABSL_INTERNAL_CHAR_SET_CASE(c) \
378 static constexpr FormatConversionCharSet c = \
379 FormatConversionCharToConvValue(#c[0]);
380 ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
381 #undef ABSL_INTERNAL_CHAR_SET_CASE
382
383 // Used for width/precision '*' specification.
384 static constexpr FormatConversionCharSet kStar =
385 FormatConversionCharToConvValue('*');
386
387 static constexpr FormatConversionCharSet kIntegral =
388 FormatConversionCharSetUnion(d, i, u, o, x, X);
389 static constexpr FormatConversionCharSet kFloating =
390 FormatConversionCharSetUnion(a, e, f, g, A, E, F, G);
391 static constexpr FormatConversionCharSet kNumeric =
392 FormatConversionCharSetUnion(kIntegral, kFloating);
393 static constexpr FormatConversionCharSet kPointer = p;
394 };
395
396 // Type safe OR operator.
397 // We need this for two reasons:
398 // 1. operator| on enums makes them decay to integers and the result is an
399 // integer. We need the result to stay as an enum.
400 // 2. We use "enum class" which would not work even if we accepted the decay.
401 constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
402 FormatConversionCharSet b) {
403 return FormatConversionCharSetUnion(a, b);
404 }
405
406 // Overloaded conversion functions to support absl::ParsedFormat.
407 // Get a conversion with a single character in it.
ToFormatConversionCharSet(char c)408 constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) {
409 return static_cast<FormatConversionCharSet>(
410 FormatConversionCharToConvValue(c));
411 }
412
413 // Get a conversion with a single character in it.
ToFormatConversionCharSet(FormatConversionCharSet c)414 constexpr FormatConversionCharSet ToFormatConversionCharSet(
415 FormatConversionCharSet c) {
416 return c;
417 }
418
419 template <typename T>
420 void ToFormatConversionCharSet(T) = delete;
421
422 // Checks whether `c` exists in `set`.
Contains(FormatConversionCharSet set,char c)423 constexpr bool Contains(FormatConversionCharSet set, char c) {
424 return (static_cast<uint64_t>(set) &
425 static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0;
426 }
427
428 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionCharSet c)429 constexpr bool Contains(FormatConversionCharSet set,
430 FormatConversionCharSet c) {
431 return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
432 static_cast<uint64_t>(c);
433 }
434
435 // Checks whether all the characters in `c` are contained in `set`
Contains(FormatConversionCharSet set,FormatConversionChar c)436 constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) {
437 return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0;
438 }
439
440 // Return capacity - used, clipped to a minimum of 0.
Excess(size_t used,size_t capacity)441 inline size_t Excess(size_t used, size_t capacity) {
442 return used < capacity ? capacity - used : 0;
443 }
444
445 } // namespace str_format_internal
446
447 ABSL_NAMESPACE_END
448 } // namespace absl
449
450 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
451