• 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 <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 
33 // ----------------------------------------------------------------------
34 // StrCat()
35 //    This merges the given strings or integers, with no delimiter. This
36 //    is designed to be the fastest possible way to construct a string out
37 //    of a mix of raw C strings, string_views, strings, and integer values.
38 // ----------------------------------------------------------------------
39 
40 // Append is merely a version of memcpy that returns the address of the byte
41 // after the area just overwritten.
Append(char * out,const AlphaNum & x)42 static char* Append(char* out, const AlphaNum& x) {
43   // memcpy is allowed to overwrite arbitrary memory, so doing this after the
44   // call would force an extra fetch of x.size().
45   char* after = out + x.size();
46   if (x.size() != 0) {
47     memcpy(out, x.data(), x.size());
48   }
49   return after;
50 }
51 
StrCat(const AlphaNum & a,const AlphaNum & b)52 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
53   std::string result;
54   absl::strings_internal::STLStringResizeUninitialized(&result,
55                                                        a.size() + b.size());
56   char* const begin = &result[0];
57   char* out = begin;
58   out = Append(out, a);
59   out = Append(out, b);
60   assert(out == begin + result.size());
61   return result;
62 }
63 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)64 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
65   std::string result;
66   strings_internal::STLStringResizeUninitialized(
67       &result, a.size() + b.size() + c.size());
68   char* const begin = &result[0];
69   char* out = begin;
70   out = Append(out, a);
71   out = Append(out, b);
72   out = Append(out, c);
73   assert(out == begin + result.size());
74   return result;
75 }
76 
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)77 std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
78                    const AlphaNum& d) {
79   std::string result;
80   strings_internal::STLStringResizeUninitialized(
81       &result, a.size() + b.size() + c.size() + d.size());
82   char* const begin = &result[0];
83   char* out = begin;
84   out = Append(out, a);
85   out = Append(out, b);
86   out = Append(out, c);
87   out = Append(out, d);
88   assert(out == begin + result.size());
89   return result;
90 }
91 
92 namespace strings_internal {
93 
94 // Do not call directly - these are not part of the public API.
CatPieces(std::initializer_list<absl::string_view> pieces)95 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
96   std::string result;
97   size_t total_size = 0;
98   for (absl::string_view piece : pieces) total_size += piece.size();
99   strings_internal::STLStringResizeUninitialized(&result, total_size);
100 
101   char* const begin = &result[0];
102   char* out = begin;
103   for (absl::string_view piece : pieces) {
104     const size_t this_size = piece.size();
105     if (this_size != 0) {
106       memcpy(out, piece.data(), this_size);
107       out += this_size;
108     }
109   }
110   assert(out == begin + result.size());
111   return result;
112 }
113 
114 // It's possible to call StrAppend with an absl::string_view that is itself a
115 // fragment of the string we're appending to.  However the results of this are
116 // random. Therefore, check for this in debug mode.  Use unsigned math so we
117 // only have to do one comparison. Note, there's an exception case: appending an
118 // empty string is always allowed.
119 #define ASSERT_NO_OVERLAP(dest, src) \
120   assert(((src).size() == 0) ||      \
121          (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
122 
AppendPieces(std::string * dest,std::initializer_list<absl::string_view> pieces)123 void AppendPieces(std::string* dest,
124                   std::initializer_list<absl::string_view> pieces) {
125   size_t old_size = dest->size();
126   size_t total_size = old_size;
127   for (absl::string_view piece : pieces) {
128     ASSERT_NO_OVERLAP(*dest, piece);
129     total_size += piece.size();
130   }
131   strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
132 
133   char* const begin = &(*dest)[0];
134   char* out = begin + old_size;
135   for (absl::string_view piece : pieces) {
136     const size_t this_size = piece.size();
137     if (this_size != 0) {
138       memcpy(out, piece.data(), this_size);
139       out += this_size;
140     }
141   }
142   assert(out == begin + dest->size());
143 }
144 
145 }  // namespace strings_internal
146 
StrAppend(std::string * dest,const AlphaNum & a)147 void StrAppend(std::string* dest, const AlphaNum& a) {
148   ASSERT_NO_OVERLAP(*dest, a);
149   std::string::size_type old_size = dest->size();
150   strings_internal::STLStringResizeUninitializedAmortized(dest,
151                                                           old_size + a.size());
152   char* const begin = &(*dest)[0];
153   char* out = begin + old_size;
154   out = Append(out, a);
155   assert(out == begin + dest->size());
156 }
157 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b)158 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
159   ASSERT_NO_OVERLAP(*dest, a);
160   ASSERT_NO_OVERLAP(*dest, b);
161   std::string::size_type old_size = dest->size();
162   strings_internal::STLStringResizeUninitializedAmortized(
163       dest, old_size + a.size() + b.size());
164   char* const begin = &(*dest)[0];
165   char* out = begin + old_size;
166   out = Append(out, a);
167   out = Append(out, b);
168   assert(out == begin + dest->size());
169 }
170 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c)171 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
172                const AlphaNum& c) {
173   ASSERT_NO_OVERLAP(*dest, a);
174   ASSERT_NO_OVERLAP(*dest, b);
175   ASSERT_NO_OVERLAP(*dest, c);
176   std::string::size_type old_size = dest->size();
177   strings_internal::STLStringResizeUninitializedAmortized(
178       dest, old_size + a.size() + b.size() + c.size());
179   char* const begin = &(*dest)[0];
180   char* out = begin + old_size;
181   out = Append(out, a);
182   out = Append(out, b);
183   out = Append(out, c);
184   assert(out == begin + dest->size());
185 }
186 
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d)187 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
188                const AlphaNum& c, const AlphaNum& d) {
189   ASSERT_NO_OVERLAP(*dest, a);
190   ASSERT_NO_OVERLAP(*dest, b);
191   ASSERT_NO_OVERLAP(*dest, c);
192   ASSERT_NO_OVERLAP(*dest, d);
193   std::string::size_type old_size = dest->size();
194   strings_internal::STLStringResizeUninitializedAmortized(
195       dest, old_size + a.size() + b.size() + c.size() + d.size());
196   char* const begin = &(*dest)[0];
197   char* out = begin + old_size;
198   out = Append(out, a);
199   out = Append(out, b);
200   out = Append(out, c);
201   out = Append(out, d);
202   assert(out == begin + dest->size());
203 }
204 
205 ABSL_NAMESPACE_END
206 }  // namespace absl
207