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 // User-defined types can be formatted with the `AbslStringify()` customization
52 // point. The API relies on detecting an overload in the user-defined type's
53 // namespace of a free (non-member) `AbslStringify()` function as a definition
54 // (typically declared as a friend and implemented in-line.
55 // with the following signature:
56 //
57 // class MyClass { ... };
58 //
59 // template <typename Sink>
60 // void AbslStringify(Sink& sink, const MyClass& value);
61 //
62 // An `AbslStringify()` overload for a type should only be declared in the same
63 // file and namespace as said type.
64 //
65 // Note that `AbslStringify()` also supports use with `absl::StrFormat()` and
66 // `absl::Substitute()`.
67 //
68 // Example:
69 //
70 // struct Point {
71 // // To add formatting support to `Point`, we simply need to add a free
72 // // (non-member) function `AbslStringify()`. This method specifies how
73 // // Point should be printed when absl::StrCat() is called on it. You can add
74 // // such a free function using a friend declaration within the body of the
75 // // class. The sink parameter is a templated type to avoid requiring
76 // // dependencies.
77 // template <typename Sink> friend void AbslStringify(Sink&
78 // sink, const Point& p) {
79 // absl::Format(&sink, "(%v, %v)", p.x, p.y);
80 // }
81 //
82 // int x;
83 // int y;
84 // };
85 // -----------------------------------------------------------------------------
86
87 #ifndef ABSL_STRINGS_STR_CAT_H_
88 #define ABSL_STRINGS_STR_CAT_H_
89
90 #include <algorithm>
91 #include <array>
92 #include <cstdint>
93 #include <cstring>
94 #include <string>
95 #include <type_traits>
96 #include <utility>
97 #include <vector>
98
99 #include "absl/base/attributes.h"
100 #include "absl/base/port.h"
101 #include "absl/strings/internal/has_absl_stringify.h"
102 #include "absl/strings/internal/stringify_sink.h"
103 #include "absl/strings/numbers.h"
104 #include "absl/strings/string_view.h"
105
106 namespace absl {
107 ABSL_NAMESPACE_BEGIN
108
109 namespace strings_internal {
110 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
111 // memory allocation. It is simply a pair of a fixed-size character array, and
112 // a size. Please don't use outside of absl, yet.
113 template <size_t max_size>
114 struct AlphaNumBuffer {
115 std::array<char, max_size> data;
116 size_t size;
117 };
118
119 } // namespace strings_internal
120
121 // Enum that specifies the number of significant digits to return in a `Hex` or
122 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
123 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
124 // would produce hexadecimal strings such as " a"," f".
125 enum PadSpec : uint8_t {
126 kNoPad = 1,
127 kZeroPad2,
128 kZeroPad3,
129 kZeroPad4,
130 kZeroPad5,
131 kZeroPad6,
132 kZeroPad7,
133 kZeroPad8,
134 kZeroPad9,
135 kZeroPad10,
136 kZeroPad11,
137 kZeroPad12,
138 kZeroPad13,
139 kZeroPad14,
140 kZeroPad15,
141 kZeroPad16,
142 kZeroPad17,
143 kZeroPad18,
144 kZeroPad19,
145 kZeroPad20,
146
147 kSpacePad2 = kZeroPad2 + 64,
148 kSpacePad3,
149 kSpacePad4,
150 kSpacePad5,
151 kSpacePad6,
152 kSpacePad7,
153 kSpacePad8,
154 kSpacePad9,
155 kSpacePad10,
156 kSpacePad11,
157 kSpacePad12,
158 kSpacePad13,
159 kSpacePad14,
160 kSpacePad15,
161 kSpacePad16,
162 kSpacePad17,
163 kSpacePad18,
164 kSpacePad19,
165 kSpacePad20,
166 };
167
168 // -----------------------------------------------------------------------------
169 // Hex
170 // -----------------------------------------------------------------------------
171 //
172 // `Hex` stores a set of hexadecimal string conversion parameters for use
173 // within `AlphaNum` string conversions.
174 struct Hex {
175 uint64_t value;
176 uint8_t width;
177 char fill;
178
179 template <typename Int>
180 explicit Hex(
181 Int v, PadSpec spec = absl::kNoPad,
182 typename std::enable_if<sizeof(Int) == 1 &&
183 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex184 : Hex(spec, static_cast<uint8_t>(v)) {}
185 template <typename Int>
186 explicit Hex(
187 Int v, PadSpec spec = absl::kNoPad,
188 typename std::enable_if<sizeof(Int) == 2 &&
189 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex190 : Hex(spec, static_cast<uint16_t>(v)) {}
191 template <typename Int>
192 explicit Hex(
193 Int v, PadSpec spec = absl::kNoPad,
194 typename std::enable_if<sizeof(Int) == 4 &&
195 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex196 : Hex(spec, static_cast<uint32_t>(v)) {}
197 template <typename Int>
198 explicit Hex(
199 Int v, PadSpec spec = absl::kNoPad,
200 typename std::enable_if<sizeof(Int) == 8 &&
201 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex202 : Hex(spec, static_cast<uint64_t>(v)) {}
203 template <typename Pointee>
204 explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
HexHex205 : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
206
207 template <typename S>
AbslStringifyHex208 friend void AbslStringify(S& sink, Hex hex) {
209 static_assert(
210 numbers_internal::kFastToBufferSize >= 32,
211 "This function only works when output buffer >= 32 bytes long");
212 char buffer[numbers_internal::kFastToBufferSize];
213 char* const end = &buffer[numbers_internal::kFastToBufferSize];
214 auto real_width =
215 absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
216 if (real_width >= hex.width) {
217 sink.Append(absl::string_view(end - real_width, real_width));
218 } else {
219 // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
220 // max pad width can be up to 20.
221 std::memset(end - 32, hex.fill, 16);
222 // Patch up everything else up to the real_width.
223 std::memset(end - real_width - 16, hex.fill, 16);
224 sink.Append(absl::string_view(end - hex.width, hex.width));
225 }
226 }
227
228 private:
HexHex229 Hex(PadSpec spec, uint64_t v)
230 : value(v),
231 width(spec == absl::kNoPad
232 ? 1
233 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
234 : spec - absl::kZeroPad2 + 2),
235 fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
236 };
237
238 // -----------------------------------------------------------------------------
239 // Dec
240 // -----------------------------------------------------------------------------
241 //
242 // `Dec` stores a set of decimal string conversion parameters for use
243 // within `AlphaNum` string conversions. Dec is slower than the default
244 // integer conversion, so use it only if you need padding.
245 struct Dec {
246 uint64_t value;
247 uint8_t width;
248 char fill;
249 bool neg;
250
251 template <typename Int>
252 explicit Dec(Int v, PadSpec spec = absl::kNoPad,
253 typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
254 : value(v >= 0 ? static_cast<uint64_t>(v)
255 : uint64_t{0} - static_cast<uint64_t>(v)),
256 width(spec == absl::kNoPad
257 ? 1
258 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
259 : spec - absl::kZeroPad2 + 2),
260 fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
261 neg(v < 0) {}
262
263 template <typename S>
AbslStringifyDec264 friend void AbslStringify(S& sink, Dec dec) {
265 assert(dec.width <= numbers_internal::kFastToBufferSize);
266 char buffer[numbers_internal::kFastToBufferSize];
267 char* const end = &buffer[numbers_internal::kFastToBufferSize];
268 char* const minfill = end - dec.width;
269 char* writer = end;
270 uint64_t val = dec.value;
271 while (val > 9) {
272 *--writer = '0' + (val % 10);
273 val /= 10;
274 }
275 *--writer = '0' + static_cast<char>(val);
276 if (dec.neg) *--writer = '-';
277
278 ptrdiff_t fillers = writer - minfill;
279 if (fillers > 0) {
280 // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
281 // But...: if the fill character is '0', then it's <+/-><fill><digits>
282 bool add_sign_again = false;
283 if (dec.neg && dec.fill == '0') { // If filling with '0',
284 ++writer; // ignore the sign we just added
285 add_sign_again = true; // and re-add the sign later.
286 }
287 writer -= fillers;
288 std::fill_n(writer, fillers, dec.fill);
289 if (add_sign_again) *--writer = '-';
290 }
291
292 sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
293 }
294 };
295
296 // -----------------------------------------------------------------------------
297 // AlphaNum
298 // -----------------------------------------------------------------------------
299 //
300 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
301 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
302 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
303 // `AlphaNum` should only be used as a function parameter. Do not instantiate
304 // `AlphaNum` directly as a stack variable.
305
306 class AlphaNum {
307 public:
308 // No bool ctor -- bools convert to an integral type.
309 // A bool ctor would also convert incoming pointers (bletch).
310
AlphaNum(int x)311 AlphaNum(int x) // NOLINT(runtime/explicit)
312 : piece_(digits_, static_cast<size_t>(
313 numbers_internal::FastIntToBuffer(x, digits_) -
314 &digits_[0])) {}
AlphaNum(unsigned int x)315 AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
316 : piece_(digits_, static_cast<size_t>(
317 numbers_internal::FastIntToBuffer(x, digits_) -
318 &digits_[0])) {}
AlphaNum(long x)319 AlphaNum(long x) // NOLINT(*)
320 : piece_(digits_, static_cast<size_t>(
321 numbers_internal::FastIntToBuffer(x, digits_) -
322 &digits_[0])) {}
AlphaNum(unsigned long x)323 AlphaNum(unsigned long x) // NOLINT(*)
324 : piece_(digits_, static_cast<size_t>(
325 numbers_internal::FastIntToBuffer(x, digits_) -
326 &digits_[0])) {}
AlphaNum(long long x)327 AlphaNum(long long x) // NOLINT(*)
328 : piece_(digits_, static_cast<size_t>(
329 numbers_internal::FastIntToBuffer(x, digits_) -
330 &digits_[0])) {}
AlphaNum(unsigned long long x)331 AlphaNum(unsigned long long x) // NOLINT(*)
332 : piece_(digits_, static_cast<size_t>(
333 numbers_internal::FastIntToBuffer(x, digits_) -
334 &digits_[0])) {}
335
AlphaNum(float f)336 AlphaNum(float f) // NOLINT(runtime/explicit)
337 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)338 AlphaNum(double f) // NOLINT(runtime/explicit)
339 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
340
341 template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf ABSL_ATTRIBUTE_LIFETIME_BOUND)342 AlphaNum( // NOLINT(runtime/explicit)
343 const strings_internal::AlphaNumBuffer<size>& buf
344 ABSL_ATTRIBUTE_LIFETIME_BOUND)
345 : piece_(&buf.data[0], buf.size) {}
346
AlphaNum(const char * c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)347 AlphaNum(const char* c_str // NOLINT(runtime/explicit)
348 ABSL_ATTRIBUTE_LIFETIME_BOUND)
349 : piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)350 AlphaNum(absl::string_view pc // NOLINT(runtime/explicit)
351 ABSL_ATTRIBUTE_LIFETIME_BOUND)
352 : piece_(pc) {}
353
354 template <typename T, typename = typename std::enable_if<
355 strings_internal::HasAbslStringify<T>::value>::type>
356 AlphaNum( // NOLINT(runtime/explicit)
357 const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
358 strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
piece_(strings_internal::ExtractStringification (sink,v))359 : piece_(strings_internal::ExtractStringification(sink, v)) {}
360
361 template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str ABSL_ATTRIBUTE_LIFETIME_BOUND)362 AlphaNum( // NOLINT(runtime/explicit)
363 const std::basic_string<char, std::char_traits<char>, Allocator>& str
364 ABSL_ATTRIBUTE_LIFETIME_BOUND)
365 : piece_(str) {}
366
367 // Use string literals ":" instead of character literals ':'.
368 AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
369
370 AlphaNum(const AlphaNum&) = delete;
371 AlphaNum& operator=(const AlphaNum&) = delete;
372
size()373 absl::string_view::size_type size() const { return piece_.size(); }
data()374 const char* data() const { return piece_.data(); }
Piece()375 absl::string_view Piece() const { return piece_; }
376
377 // Match unscoped enums. Use integral promotion so that a `char`-backed
378 // enum becomes a wider integral type AlphaNum will accept.
379 template <typename T,
380 typename = typename std::enable_if<
381 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
382 !strings_internal::HasAbslStringify<T>::value>::type>
383 AlphaNum(T e) // NOLINT(runtime/explicit)
384 : AlphaNum(+e) {}
385
386 // This overload matches scoped enums. We must explicitly cast to the
387 // underlying type, but use integral promotion for the same reason as above.
388 template <typename T,
389 typename std::enable_if<
390 std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
391 !strings_internal::HasAbslStringify<T>::value,
392 char*>::type = nullptr>
393 AlphaNum(T e) // NOLINT(runtime/explicit)
394 : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
395
396 // vector<bool>::reference and const_reference require special help to
397 // convert to `AlphaNum` because it requires two user defined conversions.
398 template <
399 typename T,
400 typename std::enable_if<
401 std::is_class<T>::value &&
402 (std::is_same<T, std::vector<bool>::reference>::value ||
403 std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
404 nullptr>
AlphaNum(T e)405 AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit)
406
407 private:
408 absl::string_view piece_;
409 char digits_[numbers_internal::kFastToBufferSize];
410 };
411
412 // -----------------------------------------------------------------------------
413 // StrCat()
414 // -----------------------------------------------------------------------------
415 //
416 // Merges given strings or numbers, using no delimiter(s), returning the merged
417 // result as a string.
418 //
419 // `StrCat()` is designed to be the fastest possible way to construct a string
420 // out of a mix of raw C strings, string_views, strings, bool values,
421 // and numeric values.
422 //
423 // Don't use `StrCat()` for user-visible strings. The localization process
424 // works poorly on strings built up out of fragments.
425 //
426 // For clarity and performance, don't use `StrCat()` when appending to a
427 // string. Use `StrAppend()` instead. In particular, avoid using any of these
428 // (anti-)patterns:
429 //
430 // str.append(StrCat(...))
431 // str += StrCat(...)
432 // str = StrCat(str, ...)
433 //
434 // The last case is the worst, with a potential to change a loop
435 // from a linear time operation with O(1) dynamic allocations into a
436 // quadratic time operation with O(n) dynamic allocations.
437 //
438 // See `StrAppend()` below for more information.
439
440 namespace strings_internal {
441
442 // Do not call directly - this is not part of the public API.
443 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
444 void AppendPieces(std::string* dest,
445 std::initializer_list<absl::string_view> pieces);
446
447 } // namespace strings_internal
448
StrCat()449 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
450
StrCat(const AlphaNum & a)451 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
452 return std::string(a.data(), a.size());
453 }
454
455 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
456 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
457 const AlphaNum& c);
458 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
459 const AlphaNum& c, const AlphaNum& d);
460
461 // Support 5 or more arguments
462 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)463 ABSL_MUST_USE_RESULT inline std::string StrCat(
464 const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
465 const AlphaNum& e, const AV&... args) {
466 return strings_internal::CatPieces(
467 {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
468 static_cast<const AlphaNum&>(args).Piece()...});
469 }
470
471 // -----------------------------------------------------------------------------
472 // StrAppend()
473 // -----------------------------------------------------------------------------
474 //
475 // Appends a string or set of strings to an existing string, in a similar
476 // fashion to `StrCat()`.
477 //
478 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
479 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
480 // not try to check each of its input arguments to be sure that they are not
481 // a subset of the string being appended to. That is, while this will work:
482 //
483 // std::string s = "foo";
484 // s += s;
485 //
486 // This output is undefined:
487 //
488 // std::string s = "foo";
489 // StrAppend(&s, s);
490 //
491 // This output is undefined as well, since `absl::string_view` does not own its
492 // data:
493 //
494 // std::string s = "foobar";
495 // absl::string_view p = s;
496 // StrAppend(&s, p);
497
StrAppend(std::string *)498 inline void StrAppend(std::string*) {}
499 void StrAppend(std::string* dest, const AlphaNum& a);
500 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
501 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
502 const AlphaNum& c);
503 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
504 const AlphaNum& c, const AlphaNum& d);
505
506 // Support 5 or more arguments
507 template <typename... AV>
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)508 inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
509 const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
510 const AV&... args) {
511 strings_internal::AppendPieces(
512 dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
513 static_cast<const AlphaNum&>(args).Piece()...});
514 }
515
516 // Helper function for the future StrCat default floating-point format, %.6g
517 // This is fast.
518 inline strings_internal::AlphaNumBuffer<
519 numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)520 SixDigits(double d) {
521 strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
522 result;
523 result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
524 return result;
525 }
526
527 ABSL_NAMESPACE_END
528 } // namespace absl
529
530 #endif // ABSL_STRINGS_STR_CAT_H_
531