xref: /aosp_15_r20/external/abseil-cpp/absl/strings/str_cat.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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 <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <initializer_list>
23 #include <limits>
24 #include <string>
25 
26 #include "absl/base/config.h"
27 #include "absl/base/internal/raw_logging.h"
28 #include "absl/base/nullability.h"
29 #include "absl/strings/internal/resize_uninitialized.h"
30 #include "absl/strings/string_view.h"
31 
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 
35 // ----------------------------------------------------------------------
36 // StrCat()
37 //    This merges the given strings or integers, with no delimiter. This
38 //    is designed to be the fastest possible way to construct a string out
39 //    of a mix of raw C strings, string_views, strings, and integer values.
40 // ----------------------------------------------------------------------
41 
42 namespace {
43 // Append is merely a version of memcpy that returns the address of the byte
44 // after the area just overwritten.
Append(absl::Nonnull<char * > out,const AlphaNum & x)45 inline absl::Nonnull<char*> Append(absl::Nonnull<char*> out,
46                                    const AlphaNum& x) {
47   // memcpy is allowed to overwrite arbitrary memory, so doing this after the
48   // call would force an extra fetch of x.size().
49   char* after = out + x.size();
50   if (x.size() != 0) {
51     memcpy(out, x.data(), x.size());
52   }
53   return after;
54 }
55 
STLStringAppendUninitializedAmortized(std::string * dest,size_t to_append)56 inline void STLStringAppendUninitializedAmortized(std::string* dest,
57                                                   size_t to_append) {
58   strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
59                                                                    to_append);
60 }
61 }  // namespace
62 
StrCat(const AlphaNum & a,const AlphaNum & b)63 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
64   std::string result;
65   // Use uint64_t to prevent size_t overflow. We assume it is not possible for
66   // in memory strings to overflow a uint64_t.
67   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
68   const uint64_t result_size =
69       static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size());
70   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
71   absl::strings_internal::STLStringResizeUninitialized(
72       &result, static_cast<size_t>(result_size));
73   char* const begin = &result[0];
74   char* out = begin;
75   out = Append(out, a);
76   out = Append(out, b);
77   assert(out == begin + result.size());
78   return result;
79 }
80 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)81 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
82   std::string result;
83   // Use uint64_t to prevent size_t overflow. We assume it is not possible for
84   // in memory strings to overflow a uint64_t.
85   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
86   const uint64_t result_size = static_cast<uint64_t>(a.size()) +
87                                static_cast<uint64_t>(b.size()) +
88                                static_cast<uint64_t>(c.size());
89   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
90   strings_internal::STLStringResizeUninitialized(
91       &result, static_cast<size_t>(result_size));
92   char* const begin = &result[0];
93   char* out = begin;
94   out = Append(out, a);
95   out = Append(out, b);
96   out = Append(out, c);
97   assert(out == begin + result.size());
98   return result;
99 }
100 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)101 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
102                    const AlphaNum& d) {
103   std::string result;
104   // Use uint64_t to prevent size_t overflow. We assume it is not possible for
105   // in memory strings to overflow a uint64_t.
106   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
107   const uint64_t result_size = static_cast<uint64_t>(a.size()) +
108                                static_cast<uint64_t>(b.size()) +
109                                static_cast<uint64_t>(c.size()) +
110                                static_cast<uint64_t>(d.size());
111   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
112   strings_internal::STLStringResizeUninitialized(
113       &result, static_cast<size_t>(result_size));
114   char* const begin = &result[0];
115   char* out = begin;
116   out = Append(out, a);
117   out = Append(out, b);
118   out = Append(out, c);
119   out = Append(out, d);
120   assert(out == begin + result.size());
121   return result;
122 }
123 
124 namespace strings_internal {
125 
126 // Do not call directly - these are not part of the public API.
CatPieces(std::initializer_list<absl::string_view> pieces)127 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
128   std::string result;
129   // Use uint64_t to prevent size_t overflow. We assume it is not possible for
130   // in memory strings to overflow a uint64_t.
131   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
132   uint64_t total_size = 0;
133   for (absl::string_view piece : pieces) {
134     total_size += piece.size();
135   }
136   ABSL_INTERNAL_CHECK(total_size <= kMaxSize, "size_t overflow");
137   strings_internal::STLStringResizeUninitialized(
138       &result, static_cast<size_t>(total_size));
139 
140   char* const begin = &result[0];
141   char* out = begin;
142   for (absl::string_view piece : pieces) {
143     const size_t this_size = piece.size();
144     if (this_size != 0) {
145       memcpy(out, piece.data(), this_size);
146       out += this_size;
147     }
148   }
149   assert(out == begin + result.size());
150   return result;
151 }
152 
153 // It's possible to call StrAppend with an absl::string_view that is itself a
154 // fragment of the string we're appending to.  However the results of this are
155 // random. Therefore, check for this in debug mode.  Use unsigned math so we
156 // only have to do one comparison. Note, there's an exception case: appending an
157 // empty string is always allowed.
158 #define ASSERT_NO_OVERLAP(dest, src) \
159   assert(((src).size() == 0) ||      \
160          (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
161 
AppendPieces(absl::Nonnull<std::string * > dest,std::initializer_list<absl::string_view> pieces)162 void AppendPieces(absl::Nonnull<std::string*> dest,
163                   std::initializer_list<absl::string_view> pieces) {
164   size_t old_size = dest->size();
165   size_t to_append = 0;
166   for (absl::string_view piece : pieces) {
167     ASSERT_NO_OVERLAP(*dest, piece);
168     to_append += piece.size();
169   }
170   STLStringAppendUninitializedAmortized(dest, to_append);
171 
172   char* const begin = &(*dest)[0];
173   char* out = begin + old_size;
174   for (absl::string_view piece : pieces) {
175     const size_t this_size = piece.size();
176     if (this_size != 0) {
177       memcpy(out, piece.data(), this_size);
178       out += this_size;
179     }
180   }
181   assert(out == begin + dest->size());
182 }
183 
184 }  // namespace strings_internal
185 
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a)186 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) {
187   ASSERT_NO_OVERLAP(*dest, a);
188   std::string::size_type old_size = dest->size();
189   STLStringAppendUninitializedAmortized(dest, a.size());
190   char* const begin = &(*dest)[0];
191   char* out = begin + old_size;
192   out = Append(out, a);
193   assert(out == begin + dest->size());
194 }
195 
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a,const AlphaNum & b)196 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
197                const AlphaNum& b) {
198   ASSERT_NO_OVERLAP(*dest, a);
199   ASSERT_NO_OVERLAP(*dest, b);
200   std::string::size_type old_size = dest->size();
201   STLStringAppendUninitializedAmortized(dest, a.size() + b.size());
202   char* const begin = &(*dest)[0];
203   char* out = begin + old_size;
204   out = Append(out, a);
205   out = Append(out, b);
206   assert(out == begin + dest->size());
207 }
208 
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)209 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
210                const AlphaNum& b, const AlphaNum& c) {
211   ASSERT_NO_OVERLAP(*dest, a);
212   ASSERT_NO_OVERLAP(*dest, b);
213   ASSERT_NO_OVERLAP(*dest, c);
214   std::string::size_type old_size = dest->size();
215   STLStringAppendUninitializedAmortized(dest, a.size() + b.size() + c.size());
216   char* const begin = &(*dest)[0];
217   char* out = begin + old_size;
218   out = Append(out, a);
219   out = Append(out, b);
220   out = Append(out, c);
221   assert(out == begin + dest->size());
222 }
223 
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)224 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
225                const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
226   ASSERT_NO_OVERLAP(*dest, a);
227   ASSERT_NO_OVERLAP(*dest, b);
228   ASSERT_NO_OVERLAP(*dest, c);
229   ASSERT_NO_OVERLAP(*dest, d);
230   std::string::size_type old_size = dest->size();
231   STLStringAppendUninitializedAmortized(
232       dest, a.size() + b.size() + c.size() + d.size());
233   char* const begin = &(*dest)[0];
234   char* out = begin + old_size;
235   out = Append(out, a);
236   out = Append(out, b);
237   out = Append(out, c);
238   out = Append(out, d);
239   assert(out == begin + dest->size());
240 }
241 
242 ABSL_NAMESPACE_END
243 }  // namespace absl
244