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
33 // ----------------------------------------------------------------------
34 // StrCat()
35 // This merges the given strings or integers, with no delimiter. This
36 // is designed to be the fastest possible way to construct a string out
37 // of a mix of raw C strings, string_views, strings, and integer values.
38 // ----------------------------------------------------------------------
39
40 // Append is merely a version of memcpy that returns the address of the byte
41 // after the area just overwritten.
Append(char * out,const AlphaNum & x)42 static char* Append(char* out, const AlphaNum& x) {
43 // memcpy is allowed to overwrite arbitrary memory, so doing this after the
44 // call would force an extra fetch of x.size().
45 char* after = out + x.size();
46 if (x.size() != 0) {
47 memcpy(out, x.data(), x.size());
48 }
49 return after;
50 }
51
StrCat(const AlphaNum & a,const AlphaNum & b)52 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
53 std::string result;
54 absl::strings_internal::STLStringResizeUninitialized(&result,
55 a.size() + b.size());
56 char* const begin = &result[0];
57 char* out = begin;
58 out = Append(out, a);
59 out = Append(out, b);
60 assert(out == begin + result.size());
61 return result;
62 }
63
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)64 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
65 std::string result;
66 strings_internal::STLStringResizeUninitialized(
67 &result, a.size() + b.size() + c.size());
68 char* const begin = &result[0];
69 char* out = begin;
70 out = Append(out, a);
71 out = Append(out, b);
72 out = Append(out, c);
73 assert(out == begin + result.size());
74 return result;
75 }
76
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)77 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
78 const AlphaNum& d) {
79 std::string result;
80 strings_internal::STLStringResizeUninitialized(
81 &result, a.size() + b.size() + c.size() + d.size());
82 char* const begin = &result[0];
83 char* out = begin;
84 out = Append(out, a);
85 out = Append(out, b);
86 out = Append(out, c);
87 out = Append(out, d);
88 assert(out == begin + result.size());
89 return result;
90 }
91
92 namespace strings_internal {
93
94 // Do not call directly - these are not part of the public API.
CatPieces(std::initializer_list<absl::string_view> pieces)95 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
96 std::string result;
97 size_t total_size = 0;
98 for (absl::string_view piece : pieces) total_size += piece.size();
99 strings_internal::STLStringResizeUninitialized(&result, total_size);
100
101 char* const begin = &result[0];
102 char* out = begin;
103 for (absl::string_view piece : pieces) {
104 const size_t this_size = piece.size();
105 if (this_size != 0) {
106 memcpy(out, piece.data(), this_size);
107 out += this_size;
108 }
109 }
110 assert(out == begin + result.size());
111 return result;
112 }
113
114 // It's possible to call StrAppend with an absl::string_view that is itself a
115 // fragment of the string we're appending to. However the results of this are
116 // random. Therefore, check for this in debug mode. Use unsigned math so we
117 // only have to do one comparison. Note, there's an exception case: appending an
118 // empty string is always allowed.
119 #define ASSERT_NO_OVERLAP(dest, src) \
120 assert(((src).size() == 0) || \
121 (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
122
AppendPieces(std::string * dest,std::initializer_list<absl::string_view> pieces)123 void AppendPieces(std::string* dest,
124 std::initializer_list<absl::string_view> pieces) {
125 size_t old_size = dest->size();
126 size_t total_size = old_size;
127 for (absl::string_view piece : pieces) {
128 ASSERT_NO_OVERLAP(*dest, piece);
129 total_size += piece.size();
130 }
131 strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
132
133 char* const begin = &(*dest)[0];
134 char* out = begin + old_size;
135 for (absl::string_view piece : pieces) {
136 const size_t this_size = piece.size();
137 if (this_size != 0) {
138 memcpy(out, piece.data(), this_size);
139 out += this_size;
140 }
141 }
142 assert(out == begin + dest->size());
143 }
144
145 } // namespace strings_internal
146
StrAppend(std::string * dest,const AlphaNum & a)147 void StrAppend(std::string* dest, const AlphaNum& a) {
148 ASSERT_NO_OVERLAP(*dest, a);
149 std::string::size_type old_size = dest->size();
150 strings_internal::STLStringResizeUninitializedAmortized(dest,
151 old_size + a.size());
152 char* const begin = &(*dest)[0];
153 char* out = begin + old_size;
154 out = Append(out, a);
155 assert(out == begin + dest->size());
156 }
157
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b)158 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
159 ASSERT_NO_OVERLAP(*dest, a);
160 ASSERT_NO_OVERLAP(*dest, b);
161 std::string::size_type old_size = dest->size();
162 strings_internal::STLStringResizeUninitializedAmortized(
163 dest, old_size + a.size() + b.size());
164 char* const begin = &(*dest)[0];
165 char* out = begin + old_size;
166 out = Append(out, a);
167 out = Append(out, b);
168 assert(out == begin + dest->size());
169 }
170
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)171 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
172 const AlphaNum& c) {
173 ASSERT_NO_OVERLAP(*dest, a);
174 ASSERT_NO_OVERLAP(*dest, b);
175 ASSERT_NO_OVERLAP(*dest, c);
176 std::string::size_type old_size = dest->size();
177 strings_internal::STLStringResizeUninitializedAmortized(
178 dest, old_size + a.size() + b.size() + c.size());
179 char* const begin = &(*dest)[0];
180 char* out = begin + old_size;
181 out = Append(out, a);
182 out = Append(out, b);
183 out = Append(out, c);
184 assert(out == begin + dest->size());
185 }
186
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)187 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
188 const AlphaNum& c, const AlphaNum& d) {
189 ASSERT_NO_OVERLAP(*dest, a);
190 ASSERT_NO_OVERLAP(*dest, b);
191 ASSERT_NO_OVERLAP(*dest, c);
192 ASSERT_NO_OVERLAP(*dest, d);
193 std::string::size_type old_size = dest->size();
194 strings_internal::STLStringResizeUninitializedAmortized(
195 dest, old_size + a.size() + b.size() + c.size() + d.size());
196 char* const begin = &(*dest)[0];
197 char* out = begin + old_size;
198 out = Append(out, a);
199 out = Append(out, b);
200 out = Append(out, c);
201 out = Append(out, d);
202 assert(out == begin + dest->size());
203 }
204
205 ABSL_NAMESPACE_END
206 } // namespace absl
207