2 // Copyright 2017 The Abseil Authors.
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // https://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
16 // -----------------------------------------------------------------------------
18 // -----------------------------------------------------------------------------
20 // This package contains functions for efficiently concatenating and appending
21 // strings: `StrCat()` and `StrAppend()`. Most of the work within these routines
22 // is actually handled through use of a special AlphaNum type, which was
23 // designed to be used as a parameter type that efficiently manages conversion
24 // to strings and avoids copies in the above operations.
26 // Any routine accepting either a string or a number may accept `AlphaNum`.
27 // The basic idea is that by accepting a `const AlphaNum &` as an argument
28 // to your function, your callers will automagically convert bools, integers,
29 // and floating point values to strings for you.
31 // NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported
32 // except for the specific case of function parameters of type `AlphaNum` or
33 // `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a
34 // stack variable is not supported.
36 // Conversion from 8-bit values is not accepted because, if it were, then an
37 // attempt to pass ':' instead of ":" might result in a 58 ending up in your
40 // Bools convert to "0" or "1". Pointers to types other than `char *` are not
41 // valid inputs. No output is generated for null `char *` pointers.
43 // Floating point numbers are formatted with six-digit precision, which is
44 // the default for "std::cout <<" or printf "%g" (the same as "%.6g").
46 // You can convert to hexadecimal output rather than decimal output using the
47 // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
48 // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
51 // -----------------------------------------------------------------------------
53 #ifndef ABSL_STRINGS_STR_CAT_H_
54 #define ABSL_STRINGS_STR_CAT_H_
59 #include <type_traits>
62 #include "absl/base/port.h"
63 #include "absl/strings/numbers.h"
64 #include "absl/strings/string_view.h"
68 namespace strings_internal {
69 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
70 // memory allocation. It is simply a pair of a fixed-size character array, and
71 // a size. Please don't use outside of absl, yet.
72 template <size_t max_size>
73 struct AlphaNumBuffer {
74 std::array<char, max_size> data;
78 } // namespace strings_internal
80 // Enum that specifies the number of significant digits to return in a `Hex` or
81 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
82 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
83 // would produce hexadecimal strings such as " a"," f".
84 enum PadSpec : uint8_t {
106 kSpacePad2 = kZeroPad2 + 64,
127 // -----------------------------------------------------------------------------
129 // -----------------------------------------------------------------------------
131 // `Hex` stores a set of hexadecimal string conversion parameters for use
132 // within `AlphaNum` string conversions.
138 template <typename Int>
140 Int v, PadSpec spec = absl::kNoPad,
141 typename std::enable_if<sizeof(Int) == 1 &&
142 !std::is_pointer<Int>::value>::type* = nullptr)
143 : Hex(spec, static_cast<uint8_t>(v)) {}
144 template <typename Int>
146 Int v, PadSpec spec = absl::kNoPad,
147 typename std::enable_if<sizeof(Int) == 2 &&
148 !std::is_pointer<Int>::value>::type* = nullptr)
149 : Hex(spec, static_cast<uint16_t>(v)) {}
150 template <typename Int>
152 Int v, PadSpec spec = absl::kNoPad,
153 typename std::enable_if<sizeof(Int) == 4 &&
154 !std::is_pointer<Int>::value>::type* = nullptr)
155 : Hex(spec, static_cast<uint32_t>(v)) {}
156 template <typename Int>
158 Int v, PadSpec spec = absl::kNoPad,
159 typename std::enable_if<sizeof(Int) == 8 &&
160 !std::is_pointer<Int>::value>::type* = nullptr)
161 : Hex(spec, static_cast<uint64_t>(v)) {}
162 template <typename Pointee>
163 explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
164 : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
167 Hex(PadSpec spec, uint64_t v)
169 width(spec == absl::kNoPad
171 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
172 : spec - absl::kZeroPad2 + 2),
173 fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
176 // -----------------------------------------------------------------------------
178 // -----------------------------------------------------------------------------
180 // `Dec` stores a set of decimal string conversion parameters for use
181 // within `AlphaNum` string conversions. Dec is slower than the default
182 // integer conversion, so use it only if you need padding.
189 template <typename Int>
190 explicit Dec(Int v, PadSpec spec = absl::kNoPad,
191 typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
192 : value(v >= 0 ? static_cast<uint64_t>(v)
193 : uint64_t{0} - static_cast<uint64_t>(v)),
194 width(spec == absl::kNoPad
196 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
197 : spec - absl::kZeroPad2 + 2),
198 fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
202 // -----------------------------------------------------------------------------
204 // -----------------------------------------------------------------------------
206 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
207 // `StrAppend()`, providing efficient conversion of numeric, boolean, and
208 // hexadecimal values (through the `Hex` type) into strings.
212 // No bool ctor -- bools convert to an integral type.
213 // A bool ctor would also convert incoming pointers (bletch).
215 AlphaNum(int x) // NOLINT(runtime/explicit)
217 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
218 AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
220 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
221 AlphaNum(long x) // NOLINT(*)
223 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
224 AlphaNum(unsigned long x) // NOLINT(*)
226 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
227 AlphaNum(long long x) // NOLINT(*)
229 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
230 AlphaNum(unsigned long long x) // NOLINT(*)
232 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
234 AlphaNum(float f) // NOLINT(runtime/explicit)
235 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
236 AlphaNum(double f) // NOLINT(runtime/explicit)
237 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
239 AlphaNum(Hex hex); // NOLINT(runtime/explicit)
240 AlphaNum(Dec dec); // NOLINT(runtime/explicit)
242 template <size_t size>
243 AlphaNum( // NOLINT(runtime/explicit)
244 const strings_internal::AlphaNumBuffer<size>& buf)
245 : piece_(&buf.data[0], buf.size) {}
247 AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit)
248 AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit)
250 template <typename Allocator>
251 AlphaNum( // NOLINT(runtime/explicit)
252 const std::basic_string<char, std::char_traits<char>, Allocator>& str)
255 // Use std::string literals ":" instead of character literals ':'.
256 AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
258 AlphaNum(const AlphaNum&) = delete;
259 AlphaNum& operator=(const AlphaNum&) = delete;
261 absl::string_view::size_type size() const { return piece_.size(); }
262 const char* data() const { return piece_.data(); }
263 absl::string_view Piece() const { return piece_; }
265 // Normal enums are already handled by the integer formatters.
266 // This overload matches only scoped enums.
267 template <typename T,
268 typename = typename std::enable_if<
269 std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
270 AlphaNum(T e) // NOLINT(runtime/explicit)
271 : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
273 // vector<bool>::reference and const_reference require special help to
274 // convert to `AlphaNum` because it requires two user defined conversions.
277 typename std::enable_if<
278 std::is_class<T>::value &&
279 (std::is_same<T, std::vector<bool>::reference>::value ||
280 std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
282 AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit)
285 absl::string_view piece_;
286 char digits_[numbers_internal::kFastToBufferSize];
289 // -----------------------------------------------------------------------------
291 // -----------------------------------------------------------------------------
293 // Merges given strings or numbers, using no delimiter(s).
295 // `StrCat()` is designed to be the fastest possible way to construct a string
296 // out of a mix of raw C strings, string_views, strings, bool values,
297 // and numeric values.
299 // Don't use `StrCat()` for user-visible strings. The localization process
300 // works poorly on strings built up out of fragments.
302 // For clarity and performance, don't use `StrCat()` when appending to a
303 // string. Use `StrAppend()` instead. In particular, avoid using any of these
306 // str.append(StrCat(...))
307 // str += StrCat(...)
308 // str = StrCat(str, ...)
310 // The last case is the worst, with a potential to change a loop
311 // from a linear time operation with O(1) dynamic allocations into a
312 // quadratic time operation with O(n) dynamic allocations.
314 // See `StrAppend()` below for more information.
316 namespace strings_internal {
318 // Do not call directly - this is not part of the public API.
319 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
320 void AppendPieces(std::string* dest,
321 std::initializer_list<absl::string_view> pieces);
323 } // namespace strings_internal
325 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
327 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
328 return std::string(a.data(), a.size());
331 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
332 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
334 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
335 const AlphaNum& c, const AlphaNum& d);
337 // Support 5 or more arguments
338 template <typename... AV>
339 ABSL_MUST_USE_RESULT inline std::string StrCat(
340 const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
341 const AlphaNum& e, const AV&... args) {
342 return strings_internal::CatPieces(
343 {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
344 static_cast<const AlphaNum&>(args).Piece()...});
347 // -----------------------------------------------------------------------------
349 // -----------------------------------------------------------------------------
351 // Appends a string or set of strings to an existing string, in a similar
352 // fashion to `StrCat()`.
354 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
355 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
356 // not try to check each of its input arguments to be sure that they are not
357 // a subset of the string being appended to. That is, while this will work:
359 // std::string s = "foo";
362 // This output is undefined:
364 // std::string s = "foo";
367 // This output is undefined as well, since `absl::string_view` does not own its
370 // std::string s = "foobar";
371 // absl::string_view p = s;
374 inline void StrAppend(std::string*) {}
375 void StrAppend(std::string* dest, const AlphaNum& a);
376 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
377 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
379 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
380 const AlphaNum& c, const AlphaNum& d);
382 // Support 5 or more arguments
383 template <typename... AV>
384 inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
385 const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
387 strings_internal::AppendPieces(
388 dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
389 static_cast<const AlphaNum&>(args).Piece()...});
392 // Helper function for the future StrCat default floating-point format, %.6g
394 inline strings_internal::AlphaNumBuffer<
395 numbers_internal::kSixDigitsToBufferSize>
396 SixDigits(double d) {
397 strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
399 result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
405 #endif // ABSL_STRINGS_STR_CAT_H_