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 | #include <algorithm> |
19 | #include <cstdint> |
20 | #include <cstring> |
21 | |
22 | #include "absl/strings/ascii.h" |
23 | #include "absl/strings/internal/resize_uninitialized.h" |
24 | |
25 | namespace absl { |
26 | |
27 | AlphaNum::AlphaNum(Hex hex) { |
28 | char* const end = &digits_[numbers_internal::kFastToBufferSize]; |
29 | char* writer = end; |
30 | uint64_t value = hex.value; |
31 | static const char hexdigits[] = "0123456789abcdef" ; |
32 | do { |
33 | *--writer = hexdigits[value & 0xF]; |
34 | value >>= 4; |
35 | } while (value != 0); |
36 | |
37 | char* beg; |
38 | if (end - writer < hex.width) { |
39 | beg = end - hex.width; |
40 | std::fill_n(beg, writer - beg, hex.fill); |
41 | } else { |
42 | beg = writer; |
43 | } |
44 | |
45 | piece_ = absl::string_view(beg, end - beg); |
46 | } |
47 | |
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. |
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 | |
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.begin(); |
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 | |
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.begin(); |
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 | |
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.begin(); |
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. |
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.begin(); |
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 | |
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->begin(); |
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 | |
193 | void StrAppend(std::string* dest, const AlphaNum& a) { |
194 | ASSERT_NO_OVERLAP(*dest, a); |
195 | dest->append(a.data(), a.size()); |
196 | } |
197 | |
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->begin(); |
205 | char* out = begin + old_size; |
206 | out = Append(out, a); |
207 | out = Append(out, b); |
208 | assert(out == begin + dest->size()); |
209 | } |
210 | |
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->begin(); |
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 | |
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->begin(); |
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 | } // namespace absl |
246 | |