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 | #include <cstddef> |
21 | #include <cstring> |
22 | #include <ostream> |
23 | |
24 | #include "absl/base/port.h" |
25 | #include "absl/strings/internal/str_format/output.h" |
26 | #include "absl/strings/string_view.h" |
27 | |
28 | class Cord; |
29 | |
30 | namespace absl { |
31 | |
32 | namespace str_format_internal { |
33 | |
34 | class FormatRawSinkImpl { |
35 | public: |
36 | // Implicitly convert from any type that provides the hook function as |
37 | // described above. |
38 | template <typename T, decltype(str_format_internal::InvokeFlush( |
39 | std::declval<T*>(), string_view()))* = nullptr> |
40 | FormatRawSinkImpl(T* raw) // NOLINT |
41 | : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {} |
42 | |
43 | void Write(string_view s) { write_(sink_, s); } |
44 | |
45 | template <typename T> |
46 | static FormatRawSinkImpl (T s) { |
47 | return s.sink_; |
48 | } |
49 | |
50 | private: |
51 | template <typename T> |
52 | static void Flush(void* r, string_view s) { |
53 | str_format_internal::InvokeFlush(static_cast<T*>(r), s); |
54 | } |
55 | |
56 | void* sink_; |
57 | void (*write_)(void*, string_view); |
58 | }; |
59 | |
60 | // An abstraction to which conversions write their string data. |
61 | class FormatSinkImpl { |
62 | public: |
63 | explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {} |
64 | |
65 | ~FormatSinkImpl() { Flush(); } |
66 | |
67 | void Flush() { |
68 | raw_.Write(string_view(buf_, pos_ - buf_)); |
69 | pos_ = buf_; |
70 | } |
71 | |
72 | void Append(size_t n, char c) { |
73 | if (n == 0) return; |
74 | size_ += n; |
75 | auto raw_append = [&](size_t count) { |
76 | memset(pos_, c, count); |
77 | pos_ += count; |
78 | }; |
79 | while (n > Avail()) { |
80 | n -= Avail(); |
81 | if (Avail() > 0) { |
82 | raw_append(Avail()); |
83 | } |
84 | Flush(); |
85 | } |
86 | raw_append(n); |
87 | } |
88 | |
89 | void Append(string_view v) { |
90 | size_t n = v.size(); |
91 | if (n == 0) return; |
92 | size_ += n; |
93 | if (n >= Avail()) { |
94 | Flush(); |
95 | raw_.Write(v); |
96 | return; |
97 | } |
98 | memcpy(pos_, v.data(), n); |
99 | pos_ += n; |
100 | } |
101 | |
102 | size_t size() const { return size_; } |
103 | |
104 | // Put 'v' to 'sink' with specified width, precision, and left flag. |
105 | bool PutPaddedString(string_view v, int w, int p, bool l); |
106 | |
107 | template <typename T> |
108 | T Wrap() { |
109 | return T(this); |
110 | } |
111 | |
112 | template <typename T> |
113 | static FormatSinkImpl* (T* s) { |
114 | return s->sink_; |
115 | } |
116 | |
117 | private: |
118 | size_t Avail() const { return buf_ + sizeof(buf_) - pos_; } |
119 | |
120 | FormatRawSinkImpl raw_; |
121 | size_t size_ = 0; |
122 | char* pos_ = buf_; |
123 | char buf_[1024]; |
124 | }; |
125 | |
126 | struct Flags { |
127 | bool basic : 1; // fastest conversion: no flags, width, or precision |
128 | bool left : 1; // "-" |
129 | bool show_pos : 1; // "+" |
130 | bool sign_col : 1; // " " |
131 | bool alt : 1; // "#" |
132 | bool zero : 1; // "0" |
133 | std::string ToString() const; |
134 | friend std::ostream& operator<<(std::ostream& os, const Flags& v) { |
135 | return os << v.ToString(); |
136 | } |
137 | }; |
138 | |
139 | struct LengthMod { |
140 | public: |
141 | enum Id : uint8_t { |
142 | h, hh, l, ll, L, j, z, t, q, none |
143 | }; |
144 | static const size_t kNumValues = none + 1; |
145 | |
146 | LengthMod() : id_(none) {} |
147 | |
148 | // Index into the opaque array of LengthMod enums. |
149 | // Requires: i < kNumValues |
150 | static LengthMod FromIndex(size_t i) { |
151 | return LengthMod(kSpecs[i].value); |
152 | } |
153 | |
154 | static LengthMod FromId(Id id) { return LengthMod(id); } |
155 | |
156 | // The length modifier std::string associated with a specified LengthMod. |
157 | string_view name() const { |
158 | const Spec& spec = kSpecs[id_]; |
159 | return {spec.name, spec.name_length}; |
160 | } |
161 | |
162 | Id id() const { return id_; } |
163 | |
164 | friend bool operator==(const LengthMod& a, const LengthMod& b) { |
165 | return a.id() == b.id(); |
166 | } |
167 | friend bool operator!=(const LengthMod& a, const LengthMod& b) { |
168 | return !(a == b); |
169 | } |
170 | friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) { |
171 | return os << v.name(); |
172 | } |
173 | |
174 | private: |
175 | struct Spec { |
176 | Id value; |
177 | const char *name; |
178 | size_t name_length; |
179 | }; |
180 | static const Spec kSpecs[]; |
181 | |
182 | explicit LengthMod(Id id) : id_(id) {} |
183 | |
184 | Id id_; |
185 | }; |
186 | |
187 | // clang-format off |
188 | #define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ |
189 | /* text */ \ |
190 | X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \ |
191 | /* ints */ \ |
192 | X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ |
193 | X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ |
194 | /* floats */ \ |
195 | X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \ |
196 | X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ |
197 | /* misc */ \ |
198 | X_VAL(n) X_SEP X_VAL(p) |
199 | // clang-format on |
200 | |
201 | struct ConversionChar { |
202 | public: |
203 | enum Id : uint8_t { |
204 | c, C, s, S, // text |
205 | d, i, o, u, x, X, // int |
206 | f, F, e, E, g, G, a, A, // float |
207 | n, p, // misc |
208 | none |
209 | }; |
210 | static const size_t kNumValues = none + 1; |
211 | |
212 | ConversionChar() : id_(none) {} |
213 | |
214 | public: |
215 | // Index into the opaque array of ConversionChar enums. |
216 | // Requires: i < kNumValues |
217 | static ConversionChar FromIndex(size_t i) { |
218 | return ConversionChar(kSpecs[i].value); |
219 | } |
220 | |
221 | static ConversionChar FromChar(char c) { |
222 | ConversionChar::Id out_id = ConversionChar::none; |
223 | switch (c) { |
224 | #define X_VAL(id) \ |
225 | case #id[0]: \ |
226 | out_id = ConversionChar::id; \ |
227 | break; |
228 | ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, ) |
229 | #undef X_VAL |
230 | default: |
231 | break; |
232 | } |
233 | return ConversionChar(out_id); |
234 | } |
235 | |
236 | static ConversionChar FromId(Id id) { return ConversionChar(id); } |
237 | Id id() const { return id_; } |
238 | |
239 | int radix() const { |
240 | switch (id()) { |
241 | case x: case X: case a: case A: case p: return 16; |
242 | case o: return 8; |
243 | default: return 10; |
244 | } |
245 | } |
246 | |
247 | bool upper() const { |
248 | switch (id()) { |
249 | case X: case F: case E: case G: case A: return true; |
250 | default: return false; |
251 | } |
252 | } |
253 | |
254 | bool is_signed() const { |
255 | switch (id()) { |
256 | case d: case i: return true; |
257 | default: return false; |
258 | } |
259 | } |
260 | |
261 | bool is_integral() const { |
262 | switch (id()) { |
263 | case d: case i: case u: case o: case x: case X: |
264 | return true; |
265 | default: return false; |
266 | } |
267 | } |
268 | |
269 | bool is_float() const { |
270 | switch (id()) { |
271 | case a: case e: case f: case g: case A: case E: case F: case G: |
272 | return true; |
273 | default: return false; |
274 | } |
275 | } |
276 | |
277 | bool IsValid() const { return id() != none; } |
278 | |
279 | // The associated char. |
280 | char Char() const { return kSpecs[id_].name; } |
281 | |
282 | friend bool operator==(const ConversionChar& a, const ConversionChar& b) { |
283 | return a.id() == b.id(); |
284 | } |
285 | friend bool operator!=(const ConversionChar& a, const ConversionChar& b) { |
286 | return !(a == b); |
287 | } |
288 | friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) { |
289 | char c = v.Char(); |
290 | if (!c) c = '?'; |
291 | return os << c; |
292 | } |
293 | |
294 | private: |
295 | struct Spec { |
296 | Id value; |
297 | char name; |
298 | }; |
299 | static const Spec kSpecs[]; |
300 | |
301 | explicit ConversionChar(Id id) : id_(id) {} |
302 | |
303 | Id id_; |
304 | }; |
305 | |
306 | class ConversionSpec { |
307 | public: |
308 | Flags flags() const { return flags_; } |
309 | LengthMod length_mod() const { return length_mod_; } |
310 | ConversionChar conv() const { |
311 | // Keep this field first in the struct . It generates better code when |
312 | // accessing it when ConversionSpec is passed by value in registers. |
313 | static_assert(offsetof(ConversionSpec, conv_) == 0, "" ); |
314 | return conv_; |
315 | } |
316 | |
317 | // Returns the specified width. If width is unspecfied, it returns a negative |
318 | // value. |
319 | int width() const { return width_; } |
320 | // Returns the specified precision. If precision is unspecfied, it returns a |
321 | // negative value. |
322 | int precision() const { return precision_; } |
323 | |
324 | void set_flags(Flags f) { flags_ = f; } |
325 | void set_length_mod(LengthMod lm) { length_mod_ = lm; } |
326 | void set_conv(ConversionChar c) { conv_ = c; } |
327 | void set_width(int w) { width_ = w; } |
328 | void set_precision(int p) { precision_ = p; } |
329 | void set_left(bool b) { flags_.left = b; } |
330 | |
331 | private: |
332 | ConversionChar conv_; |
333 | Flags flags_; |
334 | LengthMod length_mod_; |
335 | int width_; |
336 | int precision_; |
337 | }; |
338 | |
339 | constexpr uint64_t ConversionCharToConvValue(char conv) { |
340 | return |
341 | #define CONV_SET_CASE(c) \ |
342 | conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)): |
343 | ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) |
344 | #undef CONV_SET_CASE |
345 | conv == '*' |
346 | ? 1 |
347 | : 0; |
348 | } |
349 | |
350 | enum class Conv : uint64_t { |
351 | #define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]), |
352 | ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) |
353 | #undef CONV_SET_CASE |
354 | |
355 | // Used for width/precision '*' specification. |
356 | star = ConversionCharToConvValue('*'), |
357 | |
358 | // Some predefined values: |
359 | integral = d | i | u | o | x | X, |
360 | floating = a | e | f | g | A | E | F | G, |
361 | numeric = integral | floating, |
362 | string = s, |
363 | pointer = p |
364 | }; |
365 | |
366 | // Type safe OR operator. |
367 | // We need this for two reasons: |
368 | // 1. operator| on enums makes them decay to integers and the result is an |
369 | // integer. We need the result to stay as an enum. |
370 | // 2. We use "enum class" which would not work even if we accepted the decay. |
371 | constexpr Conv operator|(Conv a, Conv b) { |
372 | return Conv(static_cast<uint64_t>(a) | static_cast<uint64_t>(b)); |
373 | } |
374 | |
375 | // Get a conversion with a single character in it. |
376 | constexpr Conv ConversionCharToConv(char c) { |
377 | return Conv(ConversionCharToConvValue(c)); |
378 | } |
379 | |
380 | // Checks whether `c` exists in `set`. |
381 | constexpr bool Contains(Conv set, char c) { |
382 | return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0; |
383 | } |
384 | |
385 | // Checks whether all the characters in `c` are contained in `set` |
386 | constexpr bool Contains(Conv set, Conv c) { |
387 | return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) == |
388 | static_cast<uint64_t>(c); |
389 | } |
390 | |
391 | // Return type of the AbslFormatConvert() functions. |
392 | // The Conv template parameter is used to inform the framework of what |
393 | // conversion characters are supported by that AbslFormatConvert routine. |
394 | template <Conv C> |
395 | struct ConvertResult { |
396 | static constexpr Conv kConv = C; |
397 | bool value; |
398 | }; |
399 | template <Conv C> |
400 | constexpr Conv ConvertResult<C>::kConv; |
401 | |
402 | // Return capacity - used, clipped to a minimum of 0. |
403 | inline size_t Excess(size_t used, size_t capacity) { |
404 | return used < capacity ? capacity - used : 0; |
405 | } |
406 | |
407 | } // namespace str_format_internal |
408 | |
409 | } // namespace absl |
410 | |
411 | #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ |
412 | |