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