about summary refs log tree commit diff
path: root/absl/strings/str_cat.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/str_cat.h')
-rw-r--r--absl/strings/str_cat.h348
1 files changed, 348 insertions, 0 deletions
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
new file mode 100644
index 000000000000..5b4c9baacf59
--- /dev/null
+++ b/absl/strings/str_cat.h
@@ -0,0 +1,348 @@
+//
+// 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_