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