|  | // | 
|  | // Copyright 2017 The Abseil Authors. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | // | 
|  | // ----------------------------------------------------------------------------- | 
|  | // File: str_cat.h | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // This package contains functions for efficiently concatenating and appending | 
|  | // strings: `StrCat()` and `StrAppend()`. Most of the work within these routines | 
|  | // is actually handled through use of a special AlphaNum type, which was | 
|  | // designed to be used as a parameter type that efficiently manages conversion | 
|  | // to strings and avoids copies in the above operations. | 
|  | // | 
|  | // Any routine accepting either a std::string or a number may accept `AlphaNum`. | 
|  | // The basic idea is that by accepting a `const AlphaNum &` as an argument | 
|  | // to your function, your callers will automagically convert bools, integers, | 
|  | // and floating point values to strings for you. | 
|  | // | 
|  | // NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported | 
|  | // except for the specific case of function parameters of type `AlphaNum` or | 
|  | // `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a | 
|  | // stack variable is not supported. | 
|  | // | 
|  | // Conversion from 8-bit values is not accepted because, if it were, then an | 
|  | // attempt to pass ':' instead of ":" might result in a 58 ending up in your | 
|  | // result. | 
|  | // | 
|  | // Bools convert to "0" or "1". | 
|  | // | 
|  | // Floating point numbers are formatted with six-digit precision, which is | 
|  | // the default for "std::cout <<" or printf "%g" (the same as "%.6g"). | 
|  | // | 
|  | // | 
|  | // You can convert to hexadecimal output rather than decimal output using the | 
|  | // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to | 
|  | // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using | 
|  | // a `PadSpec` enum. | 
|  | // | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | #ifndef ABSL_STRINGS_STR_CAT_H_ | 
|  | #define ABSL_STRINGS_STR_CAT_H_ | 
|  |  | 
|  | #include <array> | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  |  | 
|  | #include "absl/base/port.h" | 
|  | #include "absl/strings/numbers.h" | 
|  | #include "absl/strings/string_view.h" | 
|  |  | 
|  | namespace absl { | 
|  |  | 
|  | namespace strings_internal { | 
|  | // AlphaNumBuffer allows a way to pass a std::string to StrCat without having to do | 
|  | // memory allocation.  It is simply a pair of a fixed-size character array, and | 
|  | // a size.  Please don't use outside of absl, yet. | 
|  | template <size_t max_size> | 
|  | struct AlphaNumBuffer { | 
|  | std::array<char, max_size> data; | 
|  | size_t size; | 
|  | }; | 
|  |  | 
|  | }  // namespace strings_internal | 
|  |  | 
|  | // Enum that specifies the number of significant digits to return in a `Hex` or | 
|  | // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example, | 
|  | // would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value | 
|  | // would produce hexadecimal strings such as "    A","    F". | 
|  | enum PadSpec : uint8_t { | 
|  | kNoPad = 1, | 
|  | kZeroPad2, | 
|  | kZeroPad3, | 
|  | kZeroPad4, | 
|  | kZeroPad5, | 
|  | kZeroPad6, | 
|  | kZeroPad7, | 
|  | kZeroPad8, | 
|  | kZeroPad9, | 
|  | kZeroPad10, | 
|  | kZeroPad11, | 
|  | kZeroPad12, | 
|  | kZeroPad13, | 
|  | kZeroPad14, | 
|  | kZeroPad15, | 
|  | kZeroPad16, | 
|  |  | 
|  | kSpacePad2 = kZeroPad2 + 64, | 
|  | kSpacePad3, | 
|  | kSpacePad4, | 
|  | kSpacePad5, | 
|  | kSpacePad6, | 
|  | kSpacePad7, | 
|  | kSpacePad8, | 
|  | kSpacePad9, | 
|  | kSpacePad10, | 
|  | kSpacePad11, | 
|  | kSpacePad12, | 
|  | kSpacePad13, | 
|  | kSpacePad14, | 
|  | kSpacePad15, | 
|  | kSpacePad16, | 
|  | }; | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  | // Hex | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // `Hex` stores a set of hexadecimal std::string conversion parameters for use | 
|  | // within `AlphaNum` std::string conversions. | 
|  | struct Hex { | 
|  | uint64_t value; | 
|  | uint8_t width; | 
|  | char fill; | 
|  |  | 
|  | template <typename Int> | 
|  | explicit Hex( | 
|  | Int v, PadSpec spec = absl::kNoPad, | 
|  | typename std::enable_if<sizeof(Int) == 1 && | 
|  | !std::is_pointer<Int>::value>::type* = nullptr) | 
|  | : Hex(spec, static_cast<uint8_t>(v)) {} | 
|  | template <typename Int> | 
|  | explicit Hex( | 
|  | Int v, PadSpec spec = absl::kNoPad, | 
|  | typename std::enable_if<sizeof(Int) == 2 && | 
|  | !std::is_pointer<Int>::value>::type* = nullptr) | 
|  | : Hex(spec, static_cast<uint16_t>(v)) {} | 
|  | template <typename Int> | 
|  | explicit Hex( | 
|  | Int v, PadSpec spec = absl::kNoPad, | 
|  | typename std::enable_if<sizeof(Int) == 4 && | 
|  | !std::is_pointer<Int>::value>::type* = nullptr) | 
|  | : Hex(spec, static_cast<uint32_t>(v)) {} | 
|  | template <typename Int> | 
|  | explicit Hex( | 
|  | Int v, PadSpec spec = absl::kNoPad, | 
|  | typename std::enable_if<sizeof(Int) == 8 && | 
|  | !std::is_pointer<Int>::value>::type* = nullptr) | 
|  | : Hex(spec, static_cast<uint64_t>(v)) {} | 
|  | template <typename Pointee> | 
|  | explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad) | 
|  | : Hex(spec, reinterpret_cast<uintptr_t>(v)) {} | 
|  |  | 
|  | private: | 
|  | Hex(PadSpec spec, uint64_t v) | 
|  | : value(v), | 
|  | width(spec == absl::kNoPad | 
|  | ? 1 | 
|  | : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2 | 
|  | : spec - absl::kZeroPad2 + 2), | 
|  | fill(spec >= absl::kSpacePad2 ? ' ' : '0') {} | 
|  | }; | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  | // Dec | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // `Dec` stores a set of decimal std::string conversion parameters for use | 
|  | // within `AlphaNum` std::string conversions.  Dec is slower than the default | 
|  | // integer conversion, so use it only if you need padding. | 
|  | struct Dec { | 
|  | uint64_t value; | 
|  | uint8_t width; | 
|  | char fill; | 
|  | bool neg; | 
|  |  | 
|  | template <typename Int> | 
|  | explicit Dec(Int v, PadSpec spec = absl::kNoPad, | 
|  | typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr) | 
|  | : value(v >= 0 ? static_cast<uint64_t>(v) | 
|  | : uint64_t{0} - static_cast<uint64_t>(v)), | 
|  | width(spec == absl::kNoPad | 
|  | ? 1 | 
|  | : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2 | 
|  | : spec - absl::kZeroPad2 + 2), | 
|  | fill(spec >= absl::kSpacePad2 ? ' ' : '0'), | 
|  | neg(v < 0) {} | 
|  | }; | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  | // AlphaNum | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // The `AlphaNum` class acts as the main parameter type for `StrCat()` and | 
|  | // `StrAppend()`, providing efficient conversion of numeric, boolean, and | 
|  | // hexadecimal values (through the `Hex` type) into strings. | 
|  |  | 
|  | class AlphaNum { | 
|  | public: | 
|  | // No bool ctor -- bools convert to an integral type. | 
|  | // A bool ctor would also convert incoming pointers (bletch). | 
|  |  | 
|  | AlphaNum(int x)  // NOLINT(runtime/explicit) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  | AlphaNum(unsigned int x)  // NOLINT(runtime/explicit) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  | AlphaNum(long x)  // NOLINT(*) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  | AlphaNum(unsigned long x)  // NOLINT(*) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  | AlphaNum(long long x)  // NOLINT(*) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  | AlphaNum(unsigned long long x)  // NOLINT(*) | 
|  | : piece_(digits_, | 
|  | numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} | 
|  |  | 
|  | AlphaNum(float f)  // NOLINT(runtime/explicit) | 
|  | : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} | 
|  | AlphaNum(double f)  // NOLINT(runtime/explicit) | 
|  | : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} | 
|  |  | 
|  | AlphaNum(Hex hex);  // NOLINT(runtime/explicit) | 
|  | AlphaNum(Dec dec);  // NOLINT(runtime/explicit) | 
|  |  | 
|  | template <size_t size> | 
|  | AlphaNum(  // NOLINT(runtime/explicit) | 
|  | const strings_internal::AlphaNumBuffer<size>& buf) | 
|  | : piece_(&buf.data[0], buf.size) {} | 
|  |  | 
|  | AlphaNum(const char* c_str) : piece_(c_str) {}  // NOLINT(runtime/explicit) | 
|  | AlphaNum(absl::string_view pc) : piece_(pc) {}  // NOLINT(runtime/explicit) | 
|  | template <typename Allocator> | 
|  | AlphaNum(  // NOLINT(runtime/explicit) | 
|  | const std::basic_string<char, std::char_traits<char>, Allocator>& str) | 
|  | : piece_(str) {} | 
|  |  | 
|  | // Use std::string literals ":" instead of character literals ':'. | 
|  | AlphaNum(char c) = delete;  // NOLINT(runtime/explicit) | 
|  |  | 
|  | AlphaNum(const AlphaNum&) = delete; | 
|  | AlphaNum& operator=(const AlphaNum&) = delete; | 
|  |  | 
|  | absl::string_view::size_type size() const { return piece_.size(); } | 
|  | const char* data() const { return piece_.data(); } | 
|  | absl::string_view Piece() const { return piece_; } | 
|  |  | 
|  | // Normal enums are already handled by the integer formatters. | 
|  | // This overload matches only scoped enums. | 
|  | template <typename T, | 
|  | typename = typename std::enable_if< | 
|  | std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type> | 
|  | AlphaNum(T e)  // NOLINT(runtime/explicit) | 
|  | : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {} | 
|  |  | 
|  | private: | 
|  | absl::string_view piece_; | 
|  | char digits_[numbers_internal::kFastToBufferSize]; | 
|  | }; | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  | // StrCat() | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // Merges given strings or numbers, using no delimiter(s). | 
|  | // | 
|  | // `StrCat()` is designed to be the fastest possible way to construct a std::string | 
|  | // out of a mix of raw C strings, string_views, strings, bool values, | 
|  | // and numeric values. | 
|  | // | 
|  | // Don't use `StrCat()` for user-visible strings. The localization process | 
|  | // works poorly on strings built up out of fragments. | 
|  | // | 
|  | // For clarity and performance, don't use `StrCat()` when appending to a | 
|  | // std::string. Use `StrAppend()` instead. In particular, avoid using any of these | 
|  | // (anti-)patterns: | 
|  | // | 
|  | //   str.append(StrCat(...)) | 
|  | //   str += StrCat(...) | 
|  | //   str = StrCat(str, ...) | 
|  | // | 
|  | // The last case is the worst, with a potential to change a loop | 
|  | // from a linear time operation with O(1) dynamic allocations into a | 
|  | // quadratic time operation with O(n) dynamic allocations. | 
|  | // | 
|  | // See `StrAppend()` below for more information. | 
|  |  | 
|  | namespace strings_internal { | 
|  |  | 
|  | // Do not call directly - this is not part of the public API. | 
|  | std::string CatPieces(std::initializer_list<absl::string_view> pieces); | 
|  | void AppendPieces(std::string* dest, | 
|  | std::initializer_list<absl::string_view> pieces); | 
|  |  | 
|  | }  // namespace strings_internal | 
|  |  | 
|  | ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); } | 
|  |  | 
|  | ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) { | 
|  | return std::string(a.data(), a.size()); | 
|  | } | 
|  |  | 
|  | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b); | 
|  | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c); | 
|  | ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c, const AlphaNum& d); | 
|  |  | 
|  | // Support 5 or more arguments | 
|  | template <typename... AV> | 
|  | ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c, const AlphaNum& d, | 
|  | const AlphaNum& e, | 
|  | const AV&... args) { | 
|  | return strings_internal::CatPieces( | 
|  | {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), | 
|  | static_cast<const AlphaNum&>(args).Piece()...}); | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  | // StrAppend() | 
|  | // ----------------------------------------------------------------------------- | 
|  | // | 
|  | // Appends a std::string or set of strings to an existing std::string, in a similar | 
|  | // fashion to `StrCat()`. | 
|  | // | 
|  | // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the | 
|  | // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does | 
|  | // not try to check each of its input arguments to be sure that they are not | 
|  | // a subset of the std::string being appended to. That is, while this will work: | 
|  | // | 
|  | //   std::string s = "foo"; | 
|  | //   s += s; | 
|  | // | 
|  | // This output is undefined: | 
|  | // | 
|  | //   std::string s = "foo"; | 
|  | //   StrAppend(&s, s); | 
|  | // | 
|  | // This output is undefined as well, since `absl::string_view` does not own its | 
|  | // data: | 
|  | // | 
|  | //   std::string s = "foobar"; | 
|  | //   absl::string_view p = s; | 
|  | //   StrAppend(&s, p); | 
|  |  | 
|  | inline void StrAppend(std::string*) {} | 
|  | void StrAppend(std::string* dest, const AlphaNum& a); | 
|  | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b); | 
|  | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c); | 
|  | void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c, const AlphaNum& d); | 
|  |  | 
|  | // Support 5 or more arguments | 
|  | template <typename... AV> | 
|  | inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, | 
|  | const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, | 
|  | const AV&... args) { | 
|  | strings_internal::AppendPieces( | 
|  | dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), | 
|  | static_cast<const AlphaNum&>(args).Piece()...}); | 
|  | } | 
|  |  | 
|  | // Helper function for the future StrCat default floating-point format, %.6g | 
|  | // This is fast. | 
|  | inline strings_internal::AlphaNumBuffer< | 
|  | numbers_internal::kSixDigitsToBufferSize> | 
|  | SixDigits(double d) { | 
|  | strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize> | 
|  | result; | 
|  | result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | }  // namespace absl | 
|  |  | 
|  | #endif  // ABSL_STRINGS_STR_CAT_H_ |