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 <array>
91 #include <cstdint>
92 #include <string>
93 #include <type_traits>
94 #include <utility>
95 #include <vector>
96 
97 #include "absl/base/port.h"
98 #include "absl/strings/internal/has_absl_stringify.h"
99 #include "absl/strings/internal/stringify_sink.h"
100 #include "absl/strings/numbers.h"
101 #include "absl/strings/string_view.h"
102 
103 namespace absl {
104 ABSL_NAMESPACE_BEGIN
105 
106 namespace strings_internal {
107 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
108 // memory allocation.  It is simply a pair of a fixed-size character array, and
109 // a size.  Please don't use outside of absl, yet.
110 template <size_t max_size>
111 struct AlphaNumBuffer {
112   std::array<char, max_size> data;
113   size_t size;
114 };
115 
116 }  // namespace strings_internal
117 
118 // Enum that specifies the number of significant digits to return in a `Hex` or
119 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
120 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
121 // would produce hexadecimal strings such as "    a","    f".
122 enum PadSpec : uint8_t {
123   kNoPad = 1,
124   kZeroPad2,
125   kZeroPad3,
126   kZeroPad4,
127   kZeroPad5,
128   kZeroPad6,
129   kZeroPad7,
130   kZeroPad8,
131   kZeroPad9,
132   kZeroPad10,
133   kZeroPad11,
134   kZeroPad12,
135   kZeroPad13,
136   kZeroPad14,
137   kZeroPad15,
138   kZeroPad16,
139   kZeroPad17,
140   kZeroPad18,
141   kZeroPad19,
142   kZeroPad20,
143 
144   kSpacePad2 = kZeroPad2 + 64,
145   kSpacePad3,
146   kSpacePad4,
147   kSpacePad5,
148   kSpacePad6,
149   kSpacePad7,
150   kSpacePad8,
151   kSpacePad9,
152   kSpacePad10,
153   kSpacePad11,
154   kSpacePad12,
155   kSpacePad13,
156   kSpacePad14,
157   kSpacePad15,
158   kSpacePad16,
159   kSpacePad17,
160   kSpacePad18,
161   kSpacePad19,
162   kSpacePad20,
163 };
164 
165 // -----------------------------------------------------------------------------
166 // Hex
167 // -----------------------------------------------------------------------------
168 //
169 // `Hex` stores a set of hexadecimal string conversion parameters for use
170 // within `AlphaNum` string conversions.
171 struct Hex {
172   uint64_t value;
173   uint8_t width;
174   char fill;
175 
176   template <typename Int>
177   explicit Hex(
178       Int v, PadSpec spec = absl::kNoPad,
179       typename std::enable_if<sizeof(Int) == 1 &&
180                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex181       : Hex(spec, static_cast<uint8_t>(v)) {}
182   template <typename Int>
183   explicit Hex(
184       Int v, PadSpec spec = absl::kNoPad,
185       typename std::enable_if<sizeof(Int) == 2 &&
186                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex187       : Hex(spec, static_cast<uint16_t>(v)) {}
188   template <typename Int>
189   explicit Hex(
190       Int v, PadSpec spec = absl::kNoPad,
191       typename std::enable_if<sizeof(Int) == 4 &&
192                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex193       : Hex(spec, static_cast<uint32_t>(v)) {}
194   template <typename Int>
195   explicit Hex(
196       Int v, PadSpec spec = absl::kNoPad,
197       typename std::enable_if<sizeof(Int) == 8 &&
198                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex199       : Hex(spec, static_cast<uint64_t>(v)) {}
200   template <typename Pointee>
201   explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
HexHex202       : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
203 
204  private:
HexHex205   Hex(PadSpec spec, uint64_t v)
206       : value(v),
207         width(spec == absl::kNoPad
208                   ? 1
209                   : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
210                                              : spec - absl::kZeroPad2 + 2),
211         fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
212 };
213 
214 // -----------------------------------------------------------------------------
215 // Dec
216 // -----------------------------------------------------------------------------
217 //
218 // `Dec` stores a set of decimal string conversion parameters for use
219 // within `AlphaNum` string conversions.  Dec is slower than the default
220 // integer conversion, so use it only if you need padding.
221 struct Dec {
222   uint64_t value;
223   uint8_t width;
224   char fill;
225   bool neg;
226 
227   template <typename Int>
228   explicit Dec(Int v, PadSpec spec = absl::kNoPad,
229                typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
230       : value(v >= 0 ? static_cast<uint64_t>(v)
231                      : uint64_t{0} - static_cast<uint64_t>(v)),
232         width(spec == absl::kNoPad
233                   ? 1
234                   : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
235                                              : spec - absl::kZeroPad2 + 2),
236         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
237         neg(v < 0) {}
238 };
239 
240 // -----------------------------------------------------------------------------
241 // AlphaNum
242 // -----------------------------------------------------------------------------
243 //
244 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
245 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
246 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
247 // `AlphaNum` should only be used as a function parameter. Do not instantiate
248 //  `AlphaNum` directly as a stack variable.
249 
250 class AlphaNum {
251  public:
252   // No bool ctor -- bools convert to an integral type.
253   // A bool ctor would also convert incoming pointers (bletch).
254 
AlphaNum(int x)255   AlphaNum(int x)  // NOLINT(runtime/explicit)
256       : piece_(digits_, static_cast<size_t>(
257                             numbers_internal::FastIntToBuffer(x, digits_) -
258                             &digits_[0])) {}
AlphaNum(unsigned int x)259   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
260       : piece_(digits_, static_cast<size_t>(
261                             numbers_internal::FastIntToBuffer(x, digits_) -
262                             &digits_[0])) {}
AlphaNum(long x)263   AlphaNum(long x)  // NOLINT(*)
264       : piece_(digits_, static_cast<size_t>(
265                             numbers_internal::FastIntToBuffer(x, digits_) -
266                             &digits_[0])) {}
AlphaNum(unsigned long x)267   AlphaNum(unsigned long x)  // NOLINT(*)
268       : piece_(digits_, static_cast<size_t>(
269                             numbers_internal::FastIntToBuffer(x, digits_) -
270                             &digits_[0])) {}
AlphaNum(long long x)271   AlphaNum(long long x)  // NOLINT(*)
272       : piece_(digits_, static_cast<size_t>(
273                             numbers_internal::FastIntToBuffer(x, digits_) -
274                             &digits_[0])) {}
AlphaNum(unsigned long long x)275   AlphaNum(unsigned long long x)  // NOLINT(*)
276       : piece_(digits_, static_cast<size_t>(
277                             numbers_internal::FastIntToBuffer(x, digits_) -
278                             &digits_[0])) {}
279 
AlphaNum(float f)280   AlphaNum(float f)  // NOLINT(runtime/explicit)
281       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)282   AlphaNum(double f)  // NOLINT(runtime/explicit)
283       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
284 
285   AlphaNum(Hex hex);  // NOLINT(runtime/explicit)
286   AlphaNum(Dec dec);  // NOLINT(runtime/explicit)
287 
288   template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf)289   AlphaNum(  // NOLINT(runtime/explicit)
290       const strings_internal::AlphaNumBuffer<size>& buf)
291       : piece_(&buf.data[0], buf.size) {}
292 
AlphaNum(const char * c_str)293   AlphaNum(const char* c_str)                     // NOLINT(runtime/explicit)
294       : piece_(NullSafeStringView(c_str)) {}      // NOLINT(runtime/explicit)
AlphaNum(absl::string_view pc)295   AlphaNum(absl::string_view pc) : piece_(pc) {}  // NOLINT(runtime/explicit)
296 
297   template <typename T, typename = typename std::enable_if<
298                             strings_internal::HasAbslStringify<T>::value>::type>
299   AlphaNum(                                         // NOLINT(runtime/explicit)
300       const T& v,                                   // NOLINT(runtime/explicit)
301       strings_internal::StringifySink&& sink = {})  // NOLINT(runtime/explicit)
piece_(strings_internal::ExtractStringification (sink,v))302       : piece_(strings_internal::ExtractStringification(sink, v)) {}
303 
304   template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str)305   AlphaNum(  // NOLINT(runtime/explicit)
306       const std::basic_string<char, std::char_traits<char>, Allocator>& str)
307       : piece_(str) {}
308 
309   // Use string literals ":" instead of character literals ':'.
310   AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
311 
312   AlphaNum(const AlphaNum&) = delete;
313   AlphaNum& operator=(const AlphaNum&) = delete;
314 
size()315   absl::string_view::size_type size() const { return piece_.size(); }
data()316   const char* data() const { return piece_.data(); }
Piece()317   absl::string_view Piece() const { return piece_; }
318 
319   // Normal enums are already handled by the integer formatters.
320   // This overload matches only scoped enums.
321   template <typename T,
322             typename = typename std::enable_if<
323                 std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
324                 !strings_internal::HasAbslStringify<T>::value>::type>
325   AlphaNum(T e)  // NOLINT(runtime/explicit)
326       : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
327 
328   // vector<bool>::reference and const_reference require special help to
329   // convert to `AlphaNum` because it requires two user defined conversions.
330   template <
331       typename T,
332       typename std::enable_if<
333           std::is_class<T>::value &&
334           (std::is_same<T, std::vector<bool>::reference>::value ||
335            std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
336           nullptr>
AlphaNum(T e)337   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
338 
339  private:
340   absl::string_view piece_;
341   char digits_[numbers_internal::kFastToBufferSize];
342 };
343 
344 // -----------------------------------------------------------------------------
345 // StrCat()
346 // -----------------------------------------------------------------------------
347 //
348 // Merges given strings or numbers, using no delimiter(s), returning the merged
349 // result as a string.
350 //
351 // `StrCat()` is designed to be the fastest possible way to construct a string
352 // out of a mix of raw C strings, string_views, strings, bool values,
353 // and numeric values.
354 //
355 // Don't use `StrCat()` for user-visible strings. The localization process
356 // works poorly on strings built up out of fragments.
357 //
358 // For clarity and performance, don't use `StrCat()` when appending to a
359 // string. Use `StrAppend()` instead. In particular, avoid using any of these
360 // (anti-)patterns:
361 //
362 //   str.append(StrCat(...))
363 //   str += StrCat(...)
364 //   str = StrCat(str, ...)
365 //
366 // The last case is the worst, with a potential to change a loop
367 // from a linear time operation with O(1) dynamic allocations into a
368 // quadratic time operation with O(n) dynamic allocations.
369 //
370 // See `StrAppend()` below for more information.
371 
372 namespace strings_internal {
373 
374 // Do not call directly - this is not part of the public API.
375 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
376 void AppendPieces(std::string* dest,
377                   std::initializer_list<absl::string_view> pieces);
378 
379 }  // namespace strings_internal
380 
StrCat()381 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
382 
StrCat(const AlphaNum & a)383 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
384   return std::string(a.data(), a.size());
385 }
386 
387 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
388 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
389                                         const AlphaNum& c);
390 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
391                                         const AlphaNum& c, const AlphaNum& d);
392 
393 // Support 5 or more arguments
394 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)395 ABSL_MUST_USE_RESULT inline std::string StrCat(
396     const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
397     const AlphaNum& e, const AV&... args) {
398   return strings_internal::CatPieces(
399       {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
400        static_cast<const AlphaNum&>(args).Piece()...});
401 }
402 
403 // -----------------------------------------------------------------------------
404 // StrAppend()
405 // -----------------------------------------------------------------------------
406 //
407 // Appends a string or set of strings to an existing string, in a similar
408 // fashion to `StrCat()`.
409 //
410 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
411 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
412 // not try to check each of its input arguments to be sure that they are not
413 // a subset of the string being appended to. That is, while this will work:
414 //
415 //   std::string s = "foo";
416 //   s += s;
417 //
418 // This output is undefined:
419 //
420 //   std::string s = "foo";
421 //   StrAppend(&s, s);
422 //
423 // This output is undefined as well, since `absl::string_view` does not own its
424 // data:
425 //
426 //   std::string s = "foobar";
427 //   absl::string_view p = s;
428 //   StrAppend(&s, p);
429 
StrAppend(std::string *)430 inline void StrAppend(std::string*) {}
431 void StrAppend(std::string* dest, const AlphaNum& a);
432 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
433 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
434                const AlphaNum& c);
435 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
436                const AlphaNum& c, const AlphaNum& d);
437 
438 // Support 5 or more arguments
439 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)440 inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
441                       const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
442                       const AV&... args) {
443   strings_internal::AppendPieces(
444       dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
445              static_cast<const AlphaNum&>(args).Piece()...});
446 }
447 
448 // Helper function for the future StrCat default floating-point format, %.6g
449 // This is fast.
450 inline strings_internal::AlphaNumBuffer<
451     numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)452 SixDigits(double d) {
453   strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
454       result;
455   result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
456   return result;
457 }
458 
459 ABSL_NAMESPACE_END
460 }  // namespace absl
461 
462 #endif  // ABSL_STRINGS_STR_CAT_H_
463