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