| 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 | |