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