349 lines
13 KiB
C
349 lines
13 KiB
C
|
//
|
||
|
// 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, so the equivalent of `StringPrintf("%04x", my_int)` is
|
||
|
// `absl::StrCat(absl::Hex(my_int, absl::kZeroPad4))`.
|
||
|
//
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
#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`
|
||
|
// conversion and fill character to use. A `kZeroPad2` value, for example, would
|
||
|
// produce hexadecimal strings such as "0A","0F" and 'kSpacePad5' value would
|
||
|
// produce hexadecimal strings such as " A"," F".
|
||
|
enum PadSpec {
|
||
|
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>::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>::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>::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>::type* = nullptr)
|
||
|
: Hex(spec, static_cast<uint64_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') {}
|
||
|
};
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// 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)
|
||
|
|
||
|
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_
|