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
28class Cord;
29
30namespace absl {
31
32namespace str_format_internal {
33
34class 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 Extract(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.
61class 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* Extract(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
126struct 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
139struct 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
201struct 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
306class 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
339constexpr 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
350enum 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.
371constexpr 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.
376constexpr Conv ConversionCharToConv(char c) {
377 return Conv(ConversionCharToConvValue(c));
378}
379
380// Checks whether `c` exists in `set`.
381constexpr 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`
386constexpr 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.
394template <Conv C>
395struct ConvertResult {
396 static constexpr Conv kConv = C;
397 bool value;
398};
399template <Conv C>
400constexpr Conv ConvertResult<C>::kConv;
401
402// Return capacity - used, clipped to a minimum of 0.
403inline 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