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