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