xref: /aosp_15_r20/external/webrtc/third_party/abseil-cpp/absl/strings/str_cat.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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