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