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