• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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