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