Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame^] | 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 | // ----------------------------------------------------------------------------- |
| 17 | // File: str_cat.h |
| 18 | // ----------------------------------------------------------------------------- |
| 19 | // |
| 20 | // This package contains functions for efficiently concatenating and appending |
| 21 | // strings: `StrCat()` and `StrAppend()`. Most of the work within these routines |
| 22 | // is actually handled through use of a special AlphaNum type, which was |
| 23 | // designed to be used as a parameter type that efficiently manages conversion |
| 24 | // to strings and avoids copies in the above operations. |
| 25 | // |
| 26 | // Any routine accepting either a string or a number may accept `AlphaNum`. |
| 27 | // The basic idea is that by accepting a `const AlphaNum &` as an argument |
| 28 | // to your function, your callers will automagically convert bools, integers, |
| 29 | // and floating point values to strings for you. |
| 30 | // |
| 31 | // NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported |
| 32 | // except for the specific case of function parameters of type `AlphaNum` or |
| 33 | // `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a |
| 34 | // stack variable is not supported. |
| 35 | // |
| 36 | // Conversion from 8-bit values is not accepted because, if it were, then an |
| 37 | // attempt to pass ':' instead of ":" might result in a 58 ending up in your |
| 38 | // result. |
| 39 | // |
| 40 | // Bools convert to "0" or "1". Pointers to types other than `char *` are not |
| 41 | // valid inputs. No output is generated for null `char *` pointers. |
| 42 | // |
| 43 | // Floating point numbers are formatted with six-digit precision, which is |
| 44 | // the default for "std::cout <<" or printf "%g" (the same as "%.6g"). |
| 45 | // |
| 46 | // You can convert to hexadecimal output rather than decimal output using the |
| 47 | // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to |
| 48 | // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using |
| 49 | // a `PadSpec` enum. |
| 50 | // |
| 51 | // ----------------------------------------------------------------------------- |
| 52 | |
| 53 | #ifndef ABSL_STRINGS_STR_CAT_H_ |
| 54 | #define ABSL_STRINGS_STR_CAT_H_ |
| 55 | |
| 56 | #include <array> |
| 57 | #include <cstdint> |
| 58 | #include <string> |
| 59 | #include <type_traits> |
| 60 | #include <vector> |
| 61 | |
| 62 | #include "absl/base/port.h" |
| 63 | #include "absl/strings/numbers.h" |
| 64 | #include "absl/strings/string_view.h" |
| 65 | |
| 66 | namespace absl { |
| 67 | |
| 68 | namespace strings_internal { |
| 69 | // AlphaNumBuffer allows a way to pass a string to StrCat without having to do |
| 70 | // memory allocation. It is simply a pair of a fixed-size character array, and |
| 71 | // a size. Please don't use outside of absl, yet. |
| 72 | template <size_t max_size> |
| 73 | struct AlphaNumBuffer { |
| 74 | std::array<char, max_size> data; |
| 75 | size_t size; |
| 76 | }; |
| 77 | |
| 78 | } // namespace strings_internal |
| 79 | |
| 80 | // Enum that specifies the number of significant digits to return in a `Hex` or |
| 81 | // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example, |
| 82 | // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value |
| 83 | // would produce hexadecimal strings such as " a"," f". |
| 84 | enum PadSpec : uint8_t { |
| 85 | kNoPad = 1, |
| 86 | kZeroPad2, |
| 87 | kZeroPad3, |
| 88 | kZeroPad4, |
| 89 | kZeroPad5, |
| 90 | kZeroPad6, |
| 91 | kZeroPad7, |
| 92 | kZeroPad8, |
| 93 | kZeroPad9, |
| 94 | kZeroPad10, |
| 95 | kZeroPad11, |
| 96 | kZeroPad12, |
| 97 | kZeroPad13, |
| 98 | kZeroPad14, |
| 99 | kZeroPad15, |
| 100 | kZeroPad16, |
| 101 | kZeroPad17, |
| 102 | kZeroPad18, |
| 103 | kZeroPad19, |
| 104 | kZeroPad20, |
| 105 | |
| 106 | kSpacePad2 = kZeroPad2 + 64, |
| 107 | kSpacePad3, |
| 108 | kSpacePad4, |
| 109 | kSpacePad5, |
| 110 | kSpacePad6, |
| 111 | kSpacePad7, |
| 112 | kSpacePad8, |
| 113 | kSpacePad9, |
| 114 | kSpacePad10, |
| 115 | kSpacePad11, |
| 116 | kSpacePad12, |
| 117 | kSpacePad13, |
| 118 | kSpacePad14, |
| 119 | kSpacePad15, |
| 120 | kSpacePad16, |
| 121 | kSpacePad17, |
| 122 | kSpacePad18, |
| 123 | kSpacePad19, |
| 124 | kSpacePad20, |
| 125 | }; |
| 126 | |
| 127 | // ----------------------------------------------------------------------------- |
| 128 | // Hex |
| 129 | // ----------------------------------------------------------------------------- |
| 130 | // |
| 131 | // `Hex` stores a set of hexadecimal string conversion parameters for use |
| 132 | // within `AlphaNum` string conversions. |
| 133 | struct Hex { |
| 134 | uint64_t value; |
| 135 | uint8_t width; |
| 136 | char fill; |
| 137 | |
| 138 | template <typename Int> |
| 139 | explicit Hex( |
| 140 | Int v, PadSpec spec = absl::kNoPad, |
| 141 | typename std::enable_if<sizeof(Int) == 1 && |
| 142 | !std::is_pointer<Int>::value>::type* = nullptr) |
| 143 | : Hex(spec, static_cast<uint8_t>(v)) {} |
| 144 | template <typename Int> |
| 145 | explicit Hex( |
| 146 | Int v, PadSpec spec = absl::kNoPad, |
| 147 | typename std::enable_if<sizeof(Int) == 2 && |
| 148 | !std::is_pointer<Int>::value>::type* = nullptr) |
| 149 | : Hex(spec, static_cast<uint16_t>(v)) {} |
| 150 | template <typename Int> |
| 151 | explicit Hex( |
| 152 | Int v, PadSpec spec = absl::kNoPad, |
| 153 | typename std::enable_if<sizeof(Int) == 4 && |
| 154 | !std::is_pointer<Int>::value>::type* = nullptr) |
| 155 | : Hex(spec, static_cast<uint32_t>(v)) {} |
| 156 | template <typename Int> |
| 157 | explicit Hex( |
| 158 | Int v, PadSpec spec = absl::kNoPad, |
| 159 | typename std::enable_if<sizeof(Int) == 8 && |
| 160 | !std::is_pointer<Int>::value>::type* = nullptr) |
| 161 | : Hex(spec, static_cast<uint64_t>(v)) {} |
| 162 | template <typename Pointee> |
| 163 | explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad) |
| 164 | : Hex(spec, reinterpret_cast<uintptr_t>(v)) {} |
| 165 | |
| 166 | private: |
| 167 | Hex(PadSpec spec, uint64_t v) |
| 168 | : value(v), |
| 169 | width(spec == absl::kNoPad |
| 170 | ? 1 |
| 171 | : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2 |
| 172 | : spec - absl::kZeroPad2 + 2), |
| 173 | fill(spec >= absl::kSpacePad2 ? ' ' : '0') {} |
| 174 | }; |
| 175 | |
| 176 | // ----------------------------------------------------------------------------- |
| 177 | // Dec |
| 178 | // ----------------------------------------------------------------------------- |
| 179 | // |
| 180 | // `Dec` stores a set of decimal string conversion parameters for use |
| 181 | // within `AlphaNum` string conversions. Dec is slower than the default |
| 182 | // integer conversion, so use it only if you need padding. |
| 183 | struct Dec { |
| 184 | uint64_t value; |
| 185 | uint8_t width; |
| 186 | char fill; |
| 187 | bool neg; |
| 188 | |
| 189 | template <typename Int> |
| 190 | explicit Dec(Int v, PadSpec spec = absl::kNoPad, |
| 191 | typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr) |
| 192 | : value(v >= 0 ? static_cast<uint64_t>(v) |
| 193 | : uint64_t{0} - static_cast<uint64_t>(v)), |
| 194 | width(spec == absl::kNoPad |
| 195 | ? 1 |
| 196 | : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2 |
| 197 | : spec - absl::kZeroPad2 + 2), |
| 198 | fill(spec >= absl::kSpacePad2 ? ' ' : '0'), |
| 199 | neg(v < 0) {} |
| 200 | }; |
| 201 | |
| 202 | // ----------------------------------------------------------------------------- |
| 203 | // AlphaNum |
| 204 | // ----------------------------------------------------------------------------- |
| 205 | // |
| 206 | // The `AlphaNum` class acts as the main parameter type for `StrCat()` and |
| 207 | // `StrAppend()`, providing efficient conversion of numeric, boolean, and |
| 208 | // hexadecimal values (through the `Hex` type) into strings. |
| 209 | |
| 210 | class AlphaNum { |
| 211 | public: |
| 212 | // No bool ctor -- bools convert to an integral type. |
| 213 | // A bool ctor would also convert incoming pointers (bletch). |
| 214 | |
| 215 | AlphaNum(int x) // NOLINT(runtime/explicit) |
| 216 | : piece_(digits_, |
| 217 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 218 | AlphaNum(unsigned int x) // NOLINT(runtime/explicit) |
| 219 | : piece_(digits_, |
| 220 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 221 | AlphaNum(long x) // NOLINT(*) |
| 222 | : piece_(digits_, |
| 223 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 224 | AlphaNum(unsigned long x) // NOLINT(*) |
| 225 | : piece_(digits_, |
| 226 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 227 | AlphaNum(long long x) // NOLINT(*) |
| 228 | : piece_(digits_, |
| 229 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 230 | AlphaNum(unsigned long long x) // NOLINT(*) |
| 231 | : piece_(digits_, |
| 232 | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} |
| 233 | |
| 234 | AlphaNum(float f) // NOLINT(runtime/explicit) |
| 235 | : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} |
| 236 | AlphaNum(double f) // NOLINT(runtime/explicit) |
| 237 | : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} |
| 238 | |
| 239 | AlphaNum(Hex hex); // NOLINT(runtime/explicit) |
| 240 | AlphaNum(Dec dec); // NOLINT(runtime/explicit) |
| 241 | |
| 242 | template <size_t size> |
| 243 | AlphaNum( // NOLINT(runtime/explicit) |
| 244 | const strings_internal::AlphaNumBuffer<size>& buf) |
| 245 | : piece_(&buf.data[0], buf.size) {} |
| 246 | |
| 247 | AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit) |
| 248 | AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) |
| 249 | |
| 250 | template <typename Allocator> |
| 251 | AlphaNum( // NOLINT(runtime/explicit) |
| 252 | const std::basic_string<char, std::char_traits<char>, Allocator>& str) |
| 253 | : piece_(str) {} |
| 254 | |
| 255 | // Use std::string literals ":" instead of character literals ':'. |
| 256 | AlphaNum(char c) = delete; // NOLINT(runtime/explicit) |
| 257 | |
| 258 | AlphaNum(const AlphaNum&) = delete; |
| 259 | AlphaNum& operator=(const AlphaNum&) = delete; |
| 260 | |
| 261 | absl::string_view::size_type size() const { return piece_.size(); } |
| 262 | const char* data() const { return piece_.data(); } |
| 263 | absl::string_view Piece() const { return piece_; } |
| 264 | |
| 265 | // Normal enums are already handled by the integer formatters. |
| 266 | // This overload matches only scoped enums. |
| 267 | template <typename T, |
| 268 | typename = typename std::enable_if< |
| 269 | std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type> |
| 270 | AlphaNum(T e) // NOLINT(runtime/explicit) |
| 271 | : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {} |
| 272 | |
| 273 | // vector<bool>::reference and const_reference require special help to |
| 274 | // convert to `AlphaNum` because it requires two user defined conversions. |
| 275 | template < |
| 276 | typename T, |
| 277 | typename std::enable_if< |
| 278 | std::is_class<T>::value && |
| 279 | (std::is_same<T, std::vector<bool>::reference>::value || |
| 280 | std::is_same<T, std::vector<bool>::const_reference>::value)>::type* = |
| 281 | nullptr> |
| 282 | AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit) |
| 283 | |
| 284 | private: |
| 285 | absl::string_view piece_; |
| 286 | char digits_[numbers_internal::kFastToBufferSize]; |
| 287 | }; |
| 288 | |
| 289 | // ----------------------------------------------------------------------------- |
| 290 | // StrCat() |
| 291 | // ----------------------------------------------------------------------------- |
| 292 | // |
| 293 | // Merges given strings or numbers, using no delimiter(s). |
| 294 | // |
| 295 | // `StrCat()` is designed to be the fastest possible way to construct a string |
| 296 | // out of a mix of raw C strings, string_views, strings, bool values, |
| 297 | // and numeric values. |
| 298 | // |
| 299 | // Don't use `StrCat()` for user-visible strings. The localization process |
| 300 | // works poorly on strings built up out of fragments. |
| 301 | // |
| 302 | // For clarity and performance, don't use `StrCat()` when appending to a |
| 303 | // string. Use `StrAppend()` instead. In particular, avoid using any of these |
| 304 | // (anti-)patterns: |
| 305 | // |
| 306 | // str.append(StrCat(...)) |
| 307 | // str += StrCat(...) |
| 308 | // str = StrCat(str, ...) |
| 309 | // |
| 310 | // The last case is the worst, with a potential to change a loop |
| 311 | // from a linear time operation with O(1) dynamic allocations into a |
| 312 | // quadratic time operation with O(n) dynamic allocations. |
| 313 | // |
| 314 | // See `StrAppend()` below for more information. |
| 315 | |
| 316 | namespace strings_internal { |
| 317 | |
| 318 | // Do not call directly - this is not part of the public API. |
| 319 | std::string CatPieces(std::initializer_list<absl::string_view> pieces); |
| 320 | void AppendPieces(std::string* dest, |
| 321 | std::initializer_list<absl::string_view> pieces); |
| 322 | |
| 323 | } // namespace strings_internal |
| 324 | |
| 325 | ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); } |
| 326 | |
| 327 | ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) { |
| 328 | return std::string(a.data(), a.size()); |
| 329 | } |
| 330 | |
| 331 | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b); |
| 332 | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, |
| 333 | const AlphaNum& c); |
| 334 | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, |
| 335 | const AlphaNum& c, const AlphaNum& d); |
| 336 | |
| 337 | // Support 5 or more arguments |
| 338 | template <typename... AV> |
| 339 | ABSL_MUST_USE_RESULT inline std::string StrCat( |
| 340 | const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d, |
| 341 | const AlphaNum& e, const AV&... args) { |
| 342 | return strings_internal::CatPieces( |
| 343 | {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), |
| 344 | static_cast<const AlphaNum&>(args).Piece()...}); |
| 345 | } |
| 346 | |
| 347 | // ----------------------------------------------------------------------------- |
| 348 | // StrAppend() |
| 349 | // ----------------------------------------------------------------------------- |
| 350 | // |
| 351 | // Appends a string or set of strings to an existing string, in a similar |
| 352 | // fashion to `StrCat()`. |
| 353 | // |
| 354 | // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the |
| 355 | // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does |
| 356 | // not try to check each of its input arguments to be sure that they are not |
| 357 | // a subset of the string being appended to. That is, while this will work: |
| 358 | // |
| 359 | // std::string s = "foo"; |
| 360 | // s += s; |
| 361 | // |
| 362 | // This output is undefined: |
| 363 | // |
| 364 | // std::string s = "foo"; |
| 365 | // StrAppend(&s, s); |
| 366 | // |
| 367 | // This output is undefined as well, since `absl::string_view` does not own its |
| 368 | // data: |
| 369 | // |
| 370 | // std::string s = "foobar"; |
| 371 | // absl::string_view p = s; |
| 372 | // StrAppend(&s, p); |
| 373 | |
| 374 | inline void StrAppend(std::string*) {} |
| 375 | void StrAppend(std::string* dest, const AlphaNum& a); |
| 376 | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b); |
| 377 | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, |
| 378 | const AlphaNum& c); |
| 379 | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, |
| 380 | const AlphaNum& c, const AlphaNum& d); |
| 381 | |
| 382 | // Support 5 or more arguments |
| 383 | template <typename... AV> |
| 384 | inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, |
| 385 | const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, |
| 386 | const AV&... args) { |
| 387 | strings_internal::AppendPieces( |
| 388 | dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), |
| 389 | static_cast<const AlphaNum&>(args).Piece()...}); |
| 390 | } |
| 391 | |
| 392 | // Helper function for the future StrCat default floating-point format, %.6g |
| 393 | // This is fast. |
| 394 | inline strings_internal::AlphaNumBuffer< |
| 395 | numbers_internal::kSixDigitsToBufferSize> |
| 396 | SixDigits(double d) { |
| 397 | strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize> |
| 398 | result; |
| 399 | result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]); |
| 400 | return result; |
| 401 | } |
| 402 | |
| 403 | } // namespace absl |
| 404 | |
| 405 | #endif // ABSL_STRINGS_STR_CAT_H_ |