• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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