str_cat.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  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 //y_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 `y_absl::StrFormat()` and
  66. // `y_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 y_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. // y_absl::Format(&sink, "(%v, %v)", p.x, p.y);
  80. // }
  81. //
  82. // int x;
  83. // int y;
  84. // };
  85. // -----------------------------------------------------------------------------
  86. #ifndef Y_ABSL_STRINGS_STR_CAT_H_
  87. #define Y_ABSL_STRINGS_STR_CAT_H_
  88. #include <algorithm>
  89. #include <array>
  90. #include <cassert>
  91. #include <cstddef>
  92. #include <cstdint>
  93. #include <cstring>
  94. #include <util/generic/string.h>
  95. #include <type_traits>
  96. #include <utility>
  97. #include <vector>
  98. #include "y_absl/base/attributes.h"
  99. #include "y_absl/base/nullability.h"
  100. #include "y_absl/base/port.h"
  101. #include "y_absl/meta/type_traits.h"
  102. #include "y_absl/strings/has_absl_stringify.h"
  103. #include "y_absl/strings/internal/resize_uninitialized.h"
  104. #include "y_absl/strings/internal/stringify_sink.h"
  105. #include "y_absl/strings/numbers.h"
  106. #include "y_absl/strings/string_view.h"
  107. namespace y_absl {
  108. Y_ABSL_NAMESPACE_BEGIN
  109. namespace strings_internal {
  110. // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
  111. // memory allocation. It is simply a pair of a fixed-size character array, and
  112. // a size. Please don't use outside of y_absl, yet.
  113. template <size_t max_size>
  114. struct AlphaNumBuffer {
  115. std::array<char, max_size> data;
  116. size_t size;
  117. };
  118. } // namespace strings_internal
  119. // Enum that specifies the number of significant digits to return in a `Hex` or
  120. // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
  121. // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
  122. // would produce hexadecimal strings such as " a"," f".
  123. enum PadSpec : uint8_t {
  124. kNoPad = 1,
  125. kZeroPad2,
  126. kZeroPad3,
  127. kZeroPad4,
  128. kZeroPad5,
  129. kZeroPad6,
  130. kZeroPad7,
  131. kZeroPad8,
  132. kZeroPad9,
  133. kZeroPad10,
  134. kZeroPad11,
  135. kZeroPad12,
  136. kZeroPad13,
  137. kZeroPad14,
  138. kZeroPad15,
  139. kZeroPad16,
  140. kZeroPad17,
  141. kZeroPad18,
  142. kZeroPad19,
  143. kZeroPad20,
  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. // 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. template <typename Int>
  175. explicit Hex(
  176. Int v, PadSpec spec = y_absl::kNoPad,
  177. typename std::enable_if<sizeof(Int) == 1 &&
  178. !std::is_pointer<Int>::value>::type* = nullptr)
  179. : Hex(spec, static_cast<uint8_t>(v)) {}
  180. template <typename Int>
  181. explicit Hex(
  182. Int v, PadSpec spec = y_absl::kNoPad,
  183. typename std::enable_if<sizeof(Int) == 2 &&
  184. !std::is_pointer<Int>::value>::type* = nullptr)
  185. : Hex(spec, static_cast<uint16_t>(v)) {}
  186. template <typename Int>
  187. explicit Hex(
  188. Int v, PadSpec spec = y_absl::kNoPad,
  189. typename std::enable_if<sizeof(Int) == 4 &&
  190. !std::is_pointer<Int>::value>::type* = nullptr)
  191. : Hex(spec, static_cast<uint32_t>(v)) {}
  192. template <typename Int>
  193. explicit Hex(
  194. Int v, PadSpec spec = y_absl::kNoPad,
  195. typename std::enable_if<sizeof(Int) == 8 &&
  196. !std::is_pointer<Int>::value>::type* = nullptr)
  197. : Hex(spec, static_cast<uint64_t>(v)) {}
  198. template <typename Pointee>
  199. explicit Hex(y_absl::Nullable<Pointee*> v, PadSpec spec = y_absl::kNoPad)
  200. : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
  201. template <typename S>
  202. friend void AbslStringify(S& sink, Hex hex) {
  203. static_assert(
  204. numbers_internal::kFastToBufferSize >= 32,
  205. "This function only works when output buffer >= 32 bytes long");
  206. char buffer[numbers_internal::kFastToBufferSize];
  207. char* const end = &buffer[numbers_internal::kFastToBufferSize];
  208. auto real_width =
  209. y_absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
  210. if (real_width >= hex.width) {
  211. sink.Append(y_absl::string_view(end - real_width, real_width));
  212. } else {
  213. // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
  214. // max pad width can be up to 20.
  215. std::memset(end - 32, hex.fill, 16);
  216. // Patch up everything else up to the real_width.
  217. std::memset(end - real_width - 16, hex.fill, 16);
  218. sink.Append(y_absl::string_view(end - hex.width, hex.width));
  219. }
  220. }
  221. private:
  222. Hex(PadSpec spec, uint64_t v)
  223. : value(v),
  224. width(spec == y_absl::kNoPad
  225. ? 1
  226. : spec >= y_absl::kSpacePad2 ? spec - y_absl::kSpacePad2 + 2
  227. : spec - y_absl::kZeroPad2 + 2),
  228. fill(spec >= y_absl::kSpacePad2 ? ' ' : '0') {}
  229. };
  230. // -----------------------------------------------------------------------------
  231. // Dec
  232. // -----------------------------------------------------------------------------
  233. //
  234. // `Dec` stores a set of decimal string conversion parameters for use
  235. // within `AlphaNum` string conversions. Dec is slower than the default
  236. // integer conversion, so use it only if you need padding.
  237. struct Dec {
  238. uint64_t value;
  239. uint8_t width;
  240. char fill;
  241. bool neg;
  242. template <typename Int>
  243. explicit Dec(Int v, PadSpec spec = y_absl::kNoPad,
  244. typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
  245. : value(v >= 0 ? static_cast<uint64_t>(v)
  246. : uint64_t{0} - static_cast<uint64_t>(v)),
  247. width(spec == y_absl::kNoPad ? 1
  248. : spec >= y_absl::kSpacePad2 ? spec - y_absl::kSpacePad2 + 2
  249. : spec - y_absl::kZeroPad2 + 2),
  250. fill(spec >= y_absl::kSpacePad2 ? ' ' : '0'),
  251. neg(v < 0) {}
  252. template <typename S>
  253. friend void AbslStringify(S& sink, Dec dec) {
  254. assert(dec.width <= numbers_internal::kFastToBufferSize);
  255. char buffer[numbers_internal::kFastToBufferSize];
  256. char* const end = &buffer[numbers_internal::kFastToBufferSize];
  257. char* const minfill = end - dec.width;
  258. char* writer = end;
  259. uint64_t val = dec.value;
  260. while (val > 9) {
  261. *--writer = '0' + (val % 10);
  262. val /= 10;
  263. }
  264. *--writer = '0' + static_cast<char>(val);
  265. if (dec.neg) *--writer = '-';
  266. ptrdiff_t fillers = writer - minfill;
  267. if (fillers > 0) {
  268. // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
  269. // But...: if the fill character is '0', then it's <+/-><fill><digits>
  270. bool add_sign_again = false;
  271. if (dec.neg && dec.fill == '0') { // If filling with '0',
  272. ++writer; // ignore the sign we just added
  273. add_sign_again = true; // and re-add the sign later.
  274. }
  275. writer -= fillers;
  276. std::fill_n(writer, fillers, dec.fill);
  277. if (add_sign_again) *--writer = '-';
  278. }
  279. sink.Append(y_absl::string_view(writer, static_cast<size_t>(end - writer)));
  280. }
  281. };
  282. // -----------------------------------------------------------------------------
  283. // AlphaNum
  284. // -----------------------------------------------------------------------------
  285. //
  286. // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
  287. // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
  288. // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
  289. // `AlphaNum` should only be used as a function parameter. Do not instantiate
  290. // `AlphaNum` directly as a stack variable.
  291. class AlphaNum {
  292. public:
  293. // No bool ctor -- bools convert to an integral type.
  294. // A bool ctor would also convert incoming pointers (bletch).
  295. AlphaNum(int x) // NOLINT(runtime/explicit)
  296. : piece_(digits_, static_cast<size_t>(
  297. numbers_internal::FastIntToBuffer(x, digits_) -
  298. &digits_[0])) {}
  299. AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
  300. : piece_(digits_, static_cast<size_t>(
  301. numbers_internal::FastIntToBuffer(x, digits_) -
  302. &digits_[0])) {}
  303. AlphaNum(long x) // NOLINT(*)
  304. : piece_(digits_, static_cast<size_t>(
  305. numbers_internal::FastIntToBuffer(x, digits_) -
  306. &digits_[0])) {}
  307. AlphaNum(unsigned long x) // NOLINT(*)
  308. : piece_(digits_, static_cast<size_t>(
  309. numbers_internal::FastIntToBuffer(x, digits_) -
  310. &digits_[0])) {}
  311. AlphaNum(long long x) // NOLINT(*)
  312. : piece_(digits_, static_cast<size_t>(
  313. numbers_internal::FastIntToBuffer(x, digits_) -
  314. &digits_[0])) {}
  315. AlphaNum(unsigned long long x) // NOLINT(*)
  316. : piece_(digits_, static_cast<size_t>(
  317. numbers_internal::FastIntToBuffer(x, digits_) -
  318. &digits_[0])) {}
  319. AlphaNum(float f) // NOLINT(runtime/explicit)
  320. : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
  321. AlphaNum(double f) // NOLINT(runtime/explicit)
  322. : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
  323. template <size_t size>
  324. AlphaNum( // NOLINT(runtime/explicit)
  325. const strings_internal::AlphaNumBuffer<size>& buf
  326. Y_ABSL_ATTRIBUTE_LIFETIME_BOUND)
  327. : piece_(&buf.data[0], buf.size) {}
  328. AlphaNum(y_absl::Nullable<const char*> c_str // NOLINT(runtime/explicit)
  329. Y_ABSL_ATTRIBUTE_LIFETIME_BOUND)
  330. : piece_(NullSafeStringView(c_str)) {}
  331. AlphaNum(y_absl::string_view pc // NOLINT(runtime/explicit)
  332. Y_ABSL_ATTRIBUTE_LIFETIME_BOUND)
  333. : piece_(pc) {}
  334. template <typename T, typename = typename std::enable_if<
  335. HasAbslStringify<T>::value>::type>
  336. AlphaNum( // NOLINT(runtime/explicit)
  337. const T& v Y_ABSL_ATTRIBUTE_LIFETIME_BOUND,
  338. strings_internal::StringifySink&& sink Y_ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
  339. : piece_(strings_internal::ExtractStringification(sink, v)) {}
  340. template <typename Allocator>
  341. AlphaNum( // NOLINT(runtime/explicit)
  342. const std::basic_string<char, std::char_traits<char>, Allocator>& str
  343. Y_ABSL_ATTRIBUTE_LIFETIME_BOUND)
  344. : piece_(str) {}
  345. AlphaNum(const TString& str)
  346. : piece_(str.data(), str.size()) {}
  347. // Use string literals ":" instead of character literals ':'.
  348. AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
  349. AlphaNum(const AlphaNum&) = delete;
  350. AlphaNum& operator=(const AlphaNum&) = delete;
  351. y_absl::string_view::size_type size() const { return piece_.size(); }
  352. y_absl::Nullable<const char*> data() const { return piece_.data(); }
  353. y_absl::string_view Piece() const { return piece_; }
  354. // Match unscoped enums. Use integral promotion so that a `char`-backed
  355. // enum becomes a wider integral type AlphaNum will accept.
  356. template <typename T,
  357. typename = typename std::enable_if<
  358. std::is_enum<T>{} && std::is_convertible<T, int>{} &&
  359. !HasAbslStringify<T>::value>::type>
  360. AlphaNum(T e) // NOLINT(runtime/explicit)
  361. : AlphaNum(+e) {}
  362. // This overload matches scoped enums. We must explicitly cast to the
  363. // underlying type, but use integral promotion for the same reason as above.
  364. template <typename T,
  365. typename std::enable_if<std::is_enum<T>{} &&
  366. !std::is_convertible<T, int>{} &&
  367. !HasAbslStringify<T>::value,
  368. char*>::type = nullptr>
  369. AlphaNum(T e) // NOLINT(runtime/explicit)
  370. : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
  371. // vector<bool>::reference and const_reference require special help to
  372. // convert to `AlphaNum` because it requires two user defined conversions.
  373. template <
  374. typename T,
  375. typename std::enable_if<
  376. std::is_class<T>::value &&
  377. (std::is_same<T, std::vector<bool>::reference>::value ||
  378. std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
  379. nullptr>
  380. AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit)
  381. private:
  382. y_absl::string_view piece_;
  383. char digits_[numbers_internal::kFastToBufferSize];
  384. };
  385. // -----------------------------------------------------------------------------
  386. // StrCat()
  387. // -----------------------------------------------------------------------------
  388. //
  389. // Merges given strings or numbers, using no delimiter(s), returning the merged
  390. // result as a string.
  391. //
  392. // `StrCat()` is designed to be the fastest possible way to construct a string
  393. // out of a mix of raw C strings, string_views, strings, bool values,
  394. // and numeric values.
  395. //
  396. // Don't use `StrCat()` for user-visible strings. The localization process
  397. // works poorly on strings built up out of fragments.
  398. //
  399. // For clarity and performance, don't use `StrCat()` when appending to a
  400. // string. Use `StrAppend()` instead. In particular, avoid using any of these
  401. // (anti-)patterns:
  402. //
  403. // str.append(StrCat(...))
  404. // str += StrCat(...)
  405. // str = StrCat(str, ...)
  406. //
  407. // The last case is the worst, with a potential to change a loop
  408. // from a linear time operation with O(1) dynamic allocations into a
  409. // quadratic time operation with O(n) dynamic allocations.
  410. //
  411. // See `StrAppend()` below for more information.
  412. namespace strings_internal {
  413. // Do not call directly - this is not part of the public API.
  414. TString CatPieces(std::initializer_list<y_absl::string_view> pieces);
  415. void AppendPieces(y_absl::Nonnull<TString*> dest,
  416. std::initializer_list<y_absl::string_view> pieces);
  417. void STLStringAppendUninitializedAmortized(TString* dest, size_t to_append);
  418. // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
  419. // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
  420. // and int64_t, then at least one of the three (`int` / `long` / `long long`)
  421. // would have been ambiguous when passed to `SingleArgStrCat`.
  422. TString SingleArgStrCat(int x);
  423. TString SingleArgStrCat(unsigned int x);
  424. TString SingleArgStrCat(long x); // NOLINT
  425. TString SingleArgStrCat(unsigned long x); // NOLINT
  426. TString SingleArgStrCat(long long x); // NOLINT
  427. TString SingleArgStrCat(unsigned long long x); // NOLINT
  428. TString SingleArgStrCat(float x);
  429. TString SingleArgStrCat(double x);
  430. // `SingleArgStrAppend` overloads are defined here for the same reasons as with
  431. // `SingleArgStrCat` above.
  432. void SingleArgStrAppend(TString& str, int x);
  433. void SingleArgStrAppend(TString& str, unsigned int x);
  434. void SingleArgStrAppend(TString& str, long x); // NOLINT
  435. void SingleArgStrAppend(TString& str, unsigned long x); // NOLINT
  436. void SingleArgStrAppend(TString& str, long long x); // NOLINT
  437. void SingleArgStrAppend(TString& str, unsigned long long x); // NOLINT
  438. template <typename T,
  439. typename = std::enable_if_t<std::is_arithmetic<T>::value &&
  440. !std::is_same<T, char>::value &&
  441. !std::is_same<T, bool>::value>>
  442. using EnableIfFastCase = T;
  443. } // namespace strings_internal
  444. Y_ABSL_MUST_USE_RESULT inline TString StrCat() { return TString(); }
  445. template <typename T>
  446. Y_ABSL_MUST_USE_RESULT inline TString StrCat(
  447. strings_internal::EnableIfFastCase<T> a) {
  448. return strings_internal::SingleArgStrCat(a);
  449. }
  450. Y_ABSL_MUST_USE_RESULT inline TString StrCat(const AlphaNum& a) {
  451. return TString(a.data(), a.size());
  452. }
  453. Y_ABSL_MUST_USE_RESULT TString StrCat(const AlphaNum& a, const AlphaNum& b);
  454. Y_ABSL_MUST_USE_RESULT TString StrCat(const AlphaNum& a, const AlphaNum& b,
  455. const AlphaNum& c);
  456. Y_ABSL_MUST_USE_RESULT TString StrCat(const AlphaNum& a, const AlphaNum& b,
  457. const AlphaNum& c, const AlphaNum& d);
  458. // Support 5 or more arguments
  459. template <typename... AV>
  460. Y_ABSL_MUST_USE_RESULT inline TString StrCat(
  461. const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
  462. const AlphaNum& e, const AV&... args) {
  463. return strings_internal::CatPieces(
  464. {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
  465. static_cast<const AlphaNum&>(args).Piece()...});
  466. }
  467. // -----------------------------------------------------------------------------
  468. // StrAppend()
  469. // -----------------------------------------------------------------------------
  470. //
  471. // Appends a string or set of strings to an existing string, in a similar
  472. // fashion to `StrCat()`.
  473. //
  474. // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
  475. // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
  476. // not try to check each of its input arguments to be sure that they are not
  477. // a subset of the string being appended to. That is, while this will work:
  478. //
  479. // TString s = "foo";
  480. // s += s;
  481. //
  482. // This output is undefined:
  483. //
  484. // TString s = "foo";
  485. // StrAppend(&s, s);
  486. //
  487. // This output is undefined as well, since `y_absl::string_view` does not own its
  488. // data:
  489. //
  490. // TString s = "foobar";
  491. // y_absl::string_view p = s;
  492. // StrAppend(&s, p);
  493. inline void StrAppend(y_absl::Nonnull<TString*>) {}
  494. void StrAppend(y_absl::Nonnull<TString*> dest, const AlphaNum& a);
  495. void StrAppend(y_absl::Nonnull<TString*> dest, const AlphaNum& a,
  496. const AlphaNum& b);
  497. void StrAppend(y_absl::Nonnull<TString*> dest, const AlphaNum& a,
  498. const AlphaNum& b, const AlphaNum& c);
  499. void StrAppend(y_absl::Nonnull<TString*> dest, const AlphaNum& a,
  500. const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
  501. // Support 5 or more arguments
  502. template <typename... AV>
  503. inline void StrAppend(y_absl::Nonnull<TString*> dest, const AlphaNum& a,
  504. const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
  505. const AlphaNum& e, const AV&... args) {
  506. strings_internal::AppendPieces(
  507. dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
  508. static_cast<const AlphaNum&>(args).Piece()...});
  509. }
  510. template <class String, class T>
  511. std::enable_if_t<
  512. std::is_integral<y_absl::strings_internal::EnableIfFastCase<T>>::value, void>
  513. StrAppend(y_absl::Nonnull<String*> result, T i) {
  514. return y_absl::strings_internal::SingleArgStrAppend(*result, i);
  515. }
  516. // This overload is only selected if all the parameters are numbers that can be
  517. // handled quickly.
  518. // Later we can look into how we can extend this to more general argument
  519. // mixtures without bloating codegen too much, or copying unnecessarily.
  520. template <typename String, typename... T>
  521. std::enable_if_t<
  522. (sizeof...(T) > 1),
  523. std::common_type_t<std::conditional_t<
  524. true, void, y_absl::strings_internal::EnableIfFastCase<T>>...>>
  525. StrAppend(y_absl::Nonnull<String*> str, T... args) {
  526. // Do not add unnecessary variables, logic, or even "free" lambdas here.
  527. // They can add overhead for the compiler and/or at run time.
  528. // Furthermore, assume this function will be inlined.
  529. // This function is carefully tailored to be able to be largely optimized away
  530. // so that it becomes near-equivalent to the caller handling each argument
  531. // individually while minimizing register pressure, so that the compiler
  532. // can inline it with minimal overhead.
  533. // First, calculate the total length, so we can perform just a single resize.
  534. // Save all the lengths for later.
  535. size_t total_length = 0;
  536. const ptrdiff_t lengths[] = {
  537. y_absl::numbers_internal::GetNumDigitsOrNegativeIfNegative(args)...};
  538. for (const ptrdiff_t possibly_negative_length : lengths) {
  539. // Lengths are negative for negative numbers. Keep them for later use, but
  540. // take their absolute values for calculating total lengths;
  541. total_length += possibly_negative_length < 0
  542. ? static_cast<size_t>(-possibly_negative_length)
  543. : static_cast<size_t>(possibly_negative_length);
  544. }
  545. // Now reserve space for all the arguments.
  546. const size_t old_size = str->size();
  547. y_absl::strings_internal::STLStringAppendUninitializedAmortized(str,
  548. total_length);
  549. // Finally, output each argument one-by-one, from left to right.
  550. size_t i = 0; // The current argument we're processing
  551. ptrdiff_t n; // The length of the current argument
  552. typename String::pointer pos = &(*str)[old_size];
  553. using SomeTrivialEmptyType = std::false_type;
  554. const SomeTrivialEmptyType dummy;
  555. // Ugly code due to the lack of C++17 fold expressions
  556. const SomeTrivialEmptyType dummies[] = {
  557. (/* Comma expressions are poor man's C++17 fold expression for C++14 */
  558. (void)(n = lengths[i]),
  559. (void)(n < 0 ? (void)(*pos++ = '-'), (n = ~n) : 0),
  560. (void)y_absl::numbers_internal::FastIntToBufferBackward(
  561. y_absl::numbers_internal::UnsignedAbsoluteValue(std::move(args)),
  562. pos += n, static_cast<uint32_t>(n)),
  563. (void)++i, dummy)...};
  564. (void)dummies; // Remove & migrate to fold expressions in C++17
  565. }
  566. // Helper function for the future StrCat default floating-point format, %.6g
  567. // This is fast.
  568. inline strings_internal::AlphaNumBuffer<
  569. numbers_internal::kSixDigitsToBufferSize>
  570. SixDigits(double d) {
  571. strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
  572. result;
  573. result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
  574. return result;
  575. }
  576. Y_ABSL_NAMESPACE_END
  577. } // namespace y_absl
  578. #endif // Y_ABSL_STRINGS_STR_CAT_H_