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
25namespace absl {
26
27AlphaNum::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
48AlphaNum::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.
88static 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
98std::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
110std::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
123std::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
138namespace strings_internal {
139
140// Do not call directly - these are not part of the public API.
141std::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
169void 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
193void StrAppend(std::string* dest, const AlphaNum& a) {
194 ASSERT_NO_OVERLAP(*dest, a);
195 dest->append(a.data(), a.size());
196}
197
198void 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
211void 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
227void 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