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