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