xref: /aosp_15_r20/external/webrtc/third_party/abseil-cpp/absl/strings/str_cat.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/strings/str_cat.h"
16 
17 #include <assert.h>
18 
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <string>
24 
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/internal/resize_uninitialized.h"
27 #include "absl/strings/numbers.h"
28 #include "absl/strings/string_view.h"
29 
30 namespace absl {
31 ABSL_NAMESPACE_BEGIN
32 
AlphaNum(Hex hex)33 AlphaNum::AlphaNum(Hex hex) {
34   static_assert(numbers_internal::kFastToBufferSize >= 32,
35                 "This function only works when output buffer >= 32 bytes long");
36   char* const end = &digits_[numbers_internal::kFastToBufferSize];
37   auto real_width =
38       absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
39   if (real_width >= hex.width) {
40     piece_ = absl::string_view(end - real_width, real_width);
41   } else {
42     // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
43     // max pad width can be up to 20.
44     std::memset(end - 32, hex.fill, 16);
45     // Patch up everything else up to the real_width.
46     std::memset(end - real_width - 16, hex.fill, 16);
47     piece_ = absl::string_view(end - hex.width, hex.width);
48   }
49 }
50 
AlphaNum(Dec dec)51 AlphaNum::AlphaNum(Dec dec) {
52   assert(dec.width <= numbers_internal::kFastToBufferSize);
53   char* const end = &digits_[numbers_internal::kFastToBufferSize];
54   char* const minfill = end - dec.width;
55   char* writer = end;
56   uint64_t value = dec.value;
57   bool neg = dec.neg;
58   while (value > 9) {
59     *--writer = '0' + (value % 10);
60     value /= 10;
61   }
62   *--writer = '0' + static_cast<char>(value);
63   if (neg) *--writer = '-';
64 
65   ptrdiff_t fillers = writer - minfill;
66   if (fillers > 0) {
67     // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
68     // But...: if the fill character is '0', then it's <+/-><fill><digits>
69     bool add_sign_again = false;
70     if (neg && dec.fill == '0') {  // If filling with '0',
71       ++writer;                    // ignore the sign we just added
72       add_sign_again = true;       // and re-add the sign later.
73     }
74     writer -= fillers;
75     std::fill_n(writer, fillers, dec.fill);
76     if (add_sign_again) *--writer = '-';
77   }
78 
79   piece_ = absl::string_view(writer, static_cast<size_t>(end - writer));
80 }
81 
82 // ----------------------------------------------------------------------
83 // StrCat()
84 //    This merges the given strings or integers, with no delimiter. This
85 //    is designed to be the fastest possible way to construct a string out
86 //    of a mix of raw C strings, string_views, strings, and integer values.
87 // ----------------------------------------------------------------------
88 
89 // Append is merely a version of memcpy that returns the address of the byte
90 // after the area just overwritten.
Append(char * out,const AlphaNum & x)91 static char* Append(char* out, const AlphaNum& x) {
92   // memcpy is allowed to overwrite arbitrary memory, so doing this after the
93   // call would force an extra fetch of x.size().
94   char* after = out + x.size();
95   if (x.size() != 0) {
96     memcpy(out, x.data(), x.size());
97   }
98   return after;
99 }
100 
StrCat(const AlphaNum & a,const AlphaNum & b)101 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
102   std::string result;
103   absl::strings_internal::STLStringResizeUninitialized(&result,
104                                                        a.size() + b.size());
105   char* const begin = &result[0];
106   char* out = begin;
107   out = Append(out, a);
108   out = Append(out, b);
109   assert(out == begin + result.size());
110   return result;
111 }
112 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)113 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
114   std::string result;
115   strings_internal::STLStringResizeUninitialized(
116       &result, a.size() + b.size() + c.size());
117   char* const begin = &result[0];
118   char* out = begin;
119   out = Append(out, a);
120   out = Append(out, b);
121   out = Append(out, c);
122   assert(out == begin + result.size());
123   return result;
124 }
125 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)126 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
127                    const AlphaNum& d) {
128   std::string result;
129   strings_internal::STLStringResizeUninitialized(
130       &result, a.size() + b.size() + c.size() + d.size());
131   char* const begin = &result[0];
132   char* out = begin;
133   out = Append(out, a);
134   out = Append(out, b);
135   out = Append(out, c);
136   out = Append(out, d);
137   assert(out == begin + result.size());
138   return result;
139 }
140 
141 namespace strings_internal {
142 
143 // Do not call directly - these are not part of the public API.
CatPieces(std::initializer_list<absl::string_view> pieces)144 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
145   std::string result;
146   size_t total_size = 0;
147   for (const absl::string_view& piece : pieces) total_size += piece.size();
148   strings_internal::STLStringResizeUninitialized(&result, total_size);
149 
150   char* const begin = &result[0];
151   char* out = begin;
152   for (const absl::string_view& piece : pieces) {
153     const size_t this_size = piece.size();
154     if (this_size != 0) {
155       memcpy(out, piece.data(), this_size);
156       out += this_size;
157     }
158   }
159   assert(out == begin + result.size());
160   return result;
161 }
162 
163 // It's possible to call StrAppend with an absl::string_view that is itself a
164 // fragment of the string we're appending to.  However the results of this are
165 // random. Therefore, check for this in debug mode.  Use unsigned math so we
166 // only have to do one comparison. Note, there's an exception case: appending an
167 // empty string is always allowed.
168 #define ASSERT_NO_OVERLAP(dest, src) \
169   assert(((src).size() == 0) ||      \
170          (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
171 
AppendPieces(std::string * dest,std::initializer_list<absl::string_view> pieces)172 void AppendPieces(std::string* dest,
173                   std::initializer_list<absl::string_view> pieces) {
174   size_t old_size = dest->size();
175   size_t total_size = old_size;
176   for (const absl::string_view& piece : pieces) {
177     ASSERT_NO_OVERLAP(*dest, piece);
178     total_size += piece.size();
179   }
180   strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
181 
182   char* const begin = &(*dest)[0];
183   char* out = begin + old_size;
184   for (const absl::string_view& piece : pieces) {
185     const size_t this_size = piece.size();
186     if (this_size != 0) {
187       memcpy(out, piece.data(), this_size);
188       out += this_size;
189     }
190   }
191   assert(out == begin + dest->size());
192 }
193 
194 }  // namespace strings_internal
195 
StrAppend(std::string * dest,const AlphaNum & a)196 void StrAppend(std::string* dest, const AlphaNum& a) {
197   ASSERT_NO_OVERLAP(*dest, a);
198   dest->append(a.data(), a.size());
199 }
200 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b)201 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
202   ASSERT_NO_OVERLAP(*dest, a);
203   ASSERT_NO_OVERLAP(*dest, b);
204   std::string::size_type old_size = dest->size();
205   strings_internal::STLStringResizeUninitializedAmortized(
206       dest, old_size + a.size() + b.size());
207   char* const begin = &(*dest)[0];
208   char* out = begin + old_size;
209   out = Append(out, a);
210   out = Append(out, b);
211   assert(out == begin + dest->size());
212 }
213 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)214 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
215                const AlphaNum& c) {
216   ASSERT_NO_OVERLAP(*dest, a);
217   ASSERT_NO_OVERLAP(*dest, b);
218   ASSERT_NO_OVERLAP(*dest, c);
219   std::string::size_type old_size = dest->size();
220   strings_internal::STLStringResizeUninitializedAmortized(
221       dest, old_size + a.size() + b.size() + c.size());
222   char* const begin = &(*dest)[0];
223   char* out = begin + old_size;
224   out = Append(out, a);
225   out = Append(out, b);
226   out = Append(out, c);
227   assert(out == begin + dest->size());
228 }
229 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)230 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
231                const AlphaNum& c, const AlphaNum& d) {
232   ASSERT_NO_OVERLAP(*dest, a);
233   ASSERT_NO_OVERLAP(*dest, b);
234   ASSERT_NO_OVERLAP(*dest, c);
235   ASSERT_NO_OVERLAP(*dest, d);
236   std::string::size_type old_size = dest->size();
237   strings_internal::STLStringResizeUninitializedAmortized(
238       dest, old_size + a.size() + b.size() + c.size() + d.size());
239   char* const begin = &(*dest)[0];
240   char* out = begin + old_size;
241   out = Append(out, a);
242   out = Append(out, b);
243   out = Append(out, c);
244   out = Append(out, d);
245   assert(out == begin + dest->size());
246 }
247 
248 ABSL_NAMESPACE_END
249 }  // namespace absl
250