#ifndef ABSL_STRINGS_STR_CAT_H_
#define ABSL_STRINGS_STR_CAT_H_
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/base/port.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/stringify_sink.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace strings_internal {
template <size_t max_size>
struct AlphaNumBuffer {
std::array<char, max_size> data;
size_t size;
};
}
enum PadSpec : uint8_t {
kNoPad = 1,
kZeroPad2,
kZeroPad3,
kZeroPad4,
kZeroPad5,
kZeroPad6,
kZeroPad7,
kZeroPad8,
kZeroPad9,
kZeroPad10,
kZeroPad11,
kZeroPad12,
kZeroPad13,
kZeroPad14,
kZeroPad15,
kZeroPad16,
kZeroPad17,
kZeroPad18,
kZeroPad19,
kZeroPad20,
kSpacePad2 = kZeroPad2 + 64,
kSpacePad3,
kSpacePad4,
kSpacePad5,
kSpacePad6,
kSpacePad7,
kSpacePad8,
kSpacePad9,
kSpacePad10,
kSpacePad11,
kSpacePad12,
kSpacePad13,
kSpacePad14,
kSpacePad15,
kSpacePad16,
kSpacePad17,
kSpacePad18,
kSpacePad19,
kSpacePad20,
};
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(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
: Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
template <typename S>
friend void AbslStringify(S& sink, Hex hex) {
static_assert(
numbers_internal::kFastToBufferSize >= 32,
"This function only works when output buffer >= 32 bytes long");
char buffer[numbers_internal::kFastToBufferSize];
char* const end = &buffer[numbers_internal::kFastToBufferSize];
auto real_width =
absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
if (real_width >= hex.width) {
sink.Append(absl::string_view(end - real_width, real_width));
} else {
std::memset(end - 32, hex.fill, 16);
std::memset(end - real_width - 16, hex.fill, 16);
sink.Append(absl::string_view(end - hex.width, hex.width));
}
}
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') {}
};
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) {}
template <typename S>
friend void AbslStringify(S& sink, Dec dec) {
assert(dec.width <= numbers_internal::kFastToBufferSize);
char buffer[numbers_internal::kFastToBufferSize];
char* const end = &buffer[numbers_internal::kFastToBufferSize];
char* const minfill = end - dec.width;
char* writer = end;
uint64_t val = dec.value;
while (val > 9) {
*--writer = '0' + (val % 10);
val /= 10;
}
*--writer = '0' + static_cast<char>(val);
if (dec.neg) *--writer = '-';
ptrdiff_t fillers = writer - minfill;
if (fillers > 0) {
bool add_sign_again = false;
if (dec.neg && dec.fill == '0') { ++writer; add_sign_again = true; }
writer -= fillers;
std::fill_n(writer, fillers, dec.fill);
if (add_sign_again) *--writer = '-';
}
sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
}
};
class AlphaNum {
public:
template <typename T>
AlphaNum(std::initializer_list<T>) = delete;
AlphaNum(int x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(unsigned int x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(long x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(unsigned long x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(long long x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(unsigned long long x) : piece_(digits_, static_cast<size_t>(
numbers_internal::FastIntToBuffer(x, digits_) -
&digits_[0])) {}
AlphaNum(float f) : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f) : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
template <size_t size>
AlphaNum( const strings_internal::AlphaNumBuffer<size>& buf
ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(&buf.data[0], buf.size) {}
AlphaNum(absl::Nullable<const char*> c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(pc) {}
template <typename T, typename = typename std::enable_if<
HasAbslStringify<T>::value>::type>
AlphaNum( const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: piece_(strings_internal::ExtractStringification(sink, v)) {}
template <typename Allocator>
AlphaNum( const std::basic_string<char, std::char_traits<char>, Allocator>& str
ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(str) {}
AlphaNum(char c) = delete;
AlphaNum(const AlphaNum&) = delete;
AlphaNum& operator=(const AlphaNum&) = delete;
absl::string_view::size_type size() const { return piece_.size(); }
absl::Nullable<const char*> data() const { return piece_.data(); }
absl::string_view Piece() const { return piece_; }
template <typename T,
typename = typename std::enable_if<
std::is_enum<T>{} && std::is_convertible<T, int>{} &&
!HasAbslStringify<T>::value>::type>
AlphaNum(T e) : AlphaNum(+e) {}
template <typename T,
typename std::enable_if<std::is_enum<T>{} &&
!std::is_convertible<T, int>{} &&
!HasAbslStringify<T>::value,
char*>::type = nullptr>
AlphaNum(T e) : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
template <
typename T,
typename std::enable_if<
std::is_class<T>::value &&
(std::is_same<T, std::vector<bool>::reference>::value ||
std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
nullptr>
AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}
private:
absl::string_view piece_;
char digits_[numbers_internal::kFastToBufferSize];
};
namespace strings_internal {
std::string CatPieces(std::initializer_list<absl::string_view> pieces);
void AppendPieces(absl::Nonnull<std::string*> dest,
std::initializer_list<absl::string_view> pieces);
template <typename Integer>
std::string IntegerToString(Integer i) {
constexpr size_t kMaxDigits10 = 22;
std::string result;
strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10);
char* start = &result[0];
char* end = numbers_internal::FastIntToBuffer(i, start);
auto size = static_cast<size_t>(end - start);
assert((size < result.size()) &&
"StrCat(Integer) does not fit into kMaxDigits10");
result.erase(size);
return result;
}
template <typename Float>
std::string FloatToString(Float f) {
std::string result;
strings_internal::STLStringResizeUninitialized(
&result, numbers_internal::kSixDigitsToBufferSize);
char* start = &result[0];
result.erase(numbers_internal::SixDigitsToBuffer(f, start));
return result;
}
inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
inline std::string SingleArgStrCat(unsigned int x) {
return IntegerToString(x);
}
inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
inline std::string SingleArgStrCat(unsigned long x) {
return IntegerToString(x);
}
inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
inline std::string SingleArgStrCat(unsigned long long x) {
return IntegerToString(x);
}
inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
#ifdef _LIBCPP_VERSION
#define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE true
#else
#define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE false
#endif
template <typename T, typename = std::enable_if_t<
ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE &&
std::is_arithmetic<T>{} && !std::is_same<T, char>{}>>
using EnableIfFastCase = T;
#undef ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE
}
ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
template <typename T>
ABSL_MUST_USE_RESULT inline std::string StrCat(
strings_internal::EnableIfFastCase<T> a) {
return strings_internal::SingleArgStrCat(a);
}
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);
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()...});
}
inline void StrAppend(absl::Nonnull<std::string*>) {}
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
const AlphaNum& b);
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
const AlphaNum& b, const AlphaNum& c);
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
template <typename... AV>
inline void StrAppend(absl::Nonnull<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()...});
}
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;
}
ABSL_NAMESPACE_END
}
#endif