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