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