1//
2// Copyright 2017 The Abseil Authors.
3//
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
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
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.
15//
16// -----------------------------------------------------------------------------
17// File: str_cat.h
18// -----------------------------------------------------------------------------
19//
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.
25//
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.
30//
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.
35//
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
38// result.
39//
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.
42//
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").
45//
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
49// a `PadSpec` enum.
50//
51// -----------------------------------------------------------------------------
52
53#ifndef ABSL_STRINGS_STR_CAT_H_
54#define ABSL_STRINGS_STR_CAT_H_
55
56#include <array>
57#include <cstdint>
58#include <string>
59#include <type_traits>
60#include <vector>
61
62#include "absl/base/port.h"
63#include "absl/strings/numbers.h"
64#include "absl/strings/string_view.h"
65
66namespace absl {
67
68namespace 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.
72template <size_t max_size>
73struct AlphaNumBuffer {
74 std::array<char, max_size> data;
75 size_t size;
76};
77
78} // namespace strings_internal
79
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".
84enum PadSpec : uint8_t {
85 kNoPad = 1,
86 kZeroPad2,
87 kZeroPad3,
88 kZeroPad4,
89 kZeroPad5,
90 kZeroPad6,
91 kZeroPad7,
92 kZeroPad8,
93 kZeroPad9,
94 kZeroPad10,
95 kZeroPad11,
96 kZeroPad12,
97 kZeroPad13,
98 kZeroPad14,
99 kZeroPad15,
100 kZeroPad16,
101 kZeroPad17,
102 kZeroPad18,
103 kZeroPad19,
104 kZeroPad20,
105
106 kSpacePad2 = kZeroPad2 + 64,
107 kSpacePad3,
108 kSpacePad4,
109 kSpacePad5,
110 kSpacePad6,
111 kSpacePad7,
112 kSpacePad8,
113 kSpacePad9,
114 kSpacePad10,
115 kSpacePad11,
116 kSpacePad12,
117 kSpacePad13,
118 kSpacePad14,
119 kSpacePad15,
120 kSpacePad16,
121 kSpacePad17,
122 kSpacePad18,
123 kSpacePad19,
124 kSpacePad20,
125};
126
127// -----------------------------------------------------------------------------
128// Hex
129// -----------------------------------------------------------------------------
130//
131// `Hex` stores a set of hexadecimal string conversion parameters for use
132// within `AlphaNum` string conversions.
133struct Hex {
134 uint64_t value;
135 uint8_t width;
136 char fill;
137
138 template <typename Int>
139 explicit Hex(
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>
145 explicit Hex(
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>
151 explicit Hex(
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>
157 explicit Hex(
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)) {}
165
166 private:
167 Hex(PadSpec spec, uint64_t v)
168 : value(v),
169 width(spec == absl::kNoPad
170 ? 1
171 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
172 : spec - absl::kZeroPad2 + 2),
173 fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
174};
175
176// -----------------------------------------------------------------------------
177// Dec
178// -----------------------------------------------------------------------------
179//
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.
183struct Dec {
184 uint64_t value;
185 uint8_t width;
186 char fill;
187 bool neg;
188
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
195 ? 1
196 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
197 : spec - absl::kZeroPad2 + 2),
198 fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
199 neg(v < 0) {}
200};
201
202// -----------------------------------------------------------------------------
203// AlphaNum
204// -----------------------------------------------------------------------------
205//
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.
209
210class AlphaNum {
211 public:
212 // No bool ctor -- bools convert to an integral type.
213 // A bool ctor would also convert incoming pointers (bletch).
214
215 AlphaNum(int x) // NOLINT(runtime/explicit)
216 : piece_(digits_,
217 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
218 AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
219 : piece_(digits_,
220 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
221 AlphaNum(long x) // NOLINT(*)
222 : piece_(digits_,
223 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
224 AlphaNum(unsigned long x) // NOLINT(*)
225 : piece_(digits_,
226 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
227 AlphaNum(long long x) // NOLINT(*)
228 : piece_(digits_,
229 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
230 AlphaNum(unsigned long long x) // NOLINT(*)
231 : piece_(digits_,
232 numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
233
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_)) {}
238
239 AlphaNum(Hex hex); // NOLINT(runtime/explicit)
240 AlphaNum(Dec dec); // NOLINT(runtime/explicit)
241
242 template <size_t size>
243 AlphaNum( // NOLINT(runtime/explicit)
244 const strings_internal::AlphaNumBuffer<size>& buf)
245 : piece_(&buf.data[0], buf.size) {}
246
247 AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit)
248 AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit)
249
250 template <typename Allocator>
251 AlphaNum( // NOLINT(runtime/explicit)
252 const std::basic_string<char, std::char_traits<char>, Allocator>& str)
253 : piece_(str) {}
254
255 // Use std::string literals ":" instead of character literals ':'.
256 AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
257
258 AlphaNum(const AlphaNum&) = delete;
259 AlphaNum& operator=(const AlphaNum&) = delete;
260
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_; }
264
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)) {}
272
273 // vector<bool>::reference and const_reference require special help to
274 // convert to `AlphaNum` because it requires two user defined conversions.
275 template <
276 typename T,
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* =
281 nullptr>
282 AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit)
283
284 private:
285 absl::string_view piece_;
286 char digits_[numbers_internal::kFastToBufferSize];
287};
288
289// -----------------------------------------------------------------------------
290// StrCat()
291// -----------------------------------------------------------------------------
292//
293// Merges given strings or numbers, using no delimiter(s).
294//
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.
298//
299// Don't use `StrCat()` for user-visible strings. The localization process
300// works poorly on strings built up out of fragments.
301//
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
304// (anti-)patterns:
305//
306// str.append(StrCat(...))
307// str += StrCat(...)
308// str = StrCat(str, ...)
309//
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.
313//
314// See `StrAppend()` below for more information.
315
316namespace strings_internal {
317
318// Do not call directly - this is not part of the public API.
319std::string CatPieces(std::initializer_list<absl::string_view> pieces);
320void AppendPieces(std::string* dest,
321 std::initializer_list<absl::string_view> pieces);
322
323} // namespace strings_internal
324
325ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
326
327ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
328 return std::string(a.data(), a.size());
329}
330
331ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
332ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
333 const AlphaNum& c);
334ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
335 const AlphaNum& c, const AlphaNum& d);
336
337// Support 5 or more arguments
338template <typename... AV>
339ABSL_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()...});
345}
346
347// -----------------------------------------------------------------------------
348// StrAppend()
349// -----------------------------------------------------------------------------
350//
351// Appends a string or set of strings to an existing string, in a similar
352// fashion to `StrCat()`.
353//
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:
358//
359// std::string s = "foo";
360// s += s;
361//
362// This output is undefined:
363//
364// std::string s = "foo";
365// StrAppend(&s, s);
366//
367// This output is undefined as well, since `absl::string_view` does not own its
368// data:
369//
370// std::string s = "foobar";
371// absl::string_view p = s;
372// StrAppend(&s, p);
373
374inline void StrAppend(std::string*) {}
375void StrAppend(std::string* dest, const AlphaNum& a);
376void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
377void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
378 const AlphaNum& c);
379void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
380 const AlphaNum& c, const AlphaNum& d);
381
382// Support 5 or more arguments
383template <typename... AV>
384inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
385 const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
386 const AV&... args) {
387 strings_internal::AppendPieces(
388 dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
389 static_cast<const AlphaNum&>(args).Piece()...});
390}
391
392// Helper function for the future StrCat default floating-point format, %.6g
393// This is fast.
394inline strings_internal::AlphaNumBuffer<
395 numbers_internal::kSixDigitsToBufferSize>
396SixDigits(double d) {
397 strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
398 result;
399 result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
400 return result;
401}
402
403} // namespace absl
404
405#endif // ABSL_STRINGS_STR_CAT_H_
406