/src/abseil-cpp/absl/strings/str_cat.cc
Line  | Count  | Source  | 
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 <limits>  | 
24  |  | #include <string>  | 
25  |  |  | 
26  |  | #include "absl/base/config.h"  | 
27  |  | #include "absl/base/internal/raw_logging.h"  | 
28  |  | #include "absl/base/nullability.h"  | 
29  |  | #include "absl/strings/internal/resize_uninitialized.h"  | 
30  |  | #include "absl/strings/resize_and_overwrite.h"  | 
31  |  | #include "absl/strings/string_view.h"  | 
32  |  |  | 
33  |  | namespace absl { | 
34  |  | ABSL_NAMESPACE_BEGIN  | 
35  |  |  | 
36  |  | // ----------------------------------------------------------------------  | 
37  |  | // StrCat()  | 
38  |  | //    This merges the given strings or integers, with no delimiter. This  | 
39  |  | //    is designed to be the fastest possible way to construct a string out  | 
40  |  | //    of a mix of raw C strings, string_views, strings, and integer values.  | 
41  |  | // ----------------------------------------------------------------------  | 
42  |  |  | 
43  |  | namespace { | 
44  |  | // Append is merely a version of memcpy that returns the address of the byte  | 
45  |  | // after the area just overwritten.  | 
46  | 0  | inline char* absl_nonnull Append(char* absl_nonnull out, const AlphaNum& x) { | 
47  |  |   // memcpy is allowed to overwrite arbitrary memory, so doing this after the  | 
48  |  |   // call would force an extra fetch of x.size().  | 
49  | 0  |   char* after = out + x.size();  | 
50  | 0  |   if (x.size() != 0) { | 
51  | 0  |     memcpy(out, x.data(), x.size());  | 
52  | 0  |   }  | 
53  | 0  |   return after;  | 
54  | 0  | }  | 
55  |  |  | 
56  |  | inline void STLStringAppendUninitializedAmortized(std::string* dest,  | 
57  | 0  |                                                   size_t to_append) { | 
58  | 0  |   strings_internal::AppendUninitializedTraits<std::string>::Append(dest,  | 
59  | 0  |                                                                    to_append);  | 
60  | 0  | }  | 
61  |  | }  // namespace  | 
62  |  |  | 
63  | 0  | std::string StrCat(const AlphaNum& a, const AlphaNum& b) { | 
64  | 0  |   std::string result;  | 
65  |  |   // Use uint64_t to prevent size_t overflow. We assume it is not possible for  | 
66  |  |   // in memory strings to overflow a uint64_t.  | 
67  | 0  |   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()}; | 
68  | 0  |   const uint64_t result_size =  | 
69  | 0  |       static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size());  | 
70  | 0  |   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");  | 
71  | 0  |   absl::StringResizeAndOverwrite(result, static_cast<size_t>(result_size),  | 
72  | 0  |                                  [&a, &b](char* const begin, size_t buf_size) { | 
73  | 0  |                                    char* out = begin;  | 
74  | 0  |                                    out = Append(out, a);  | 
75  | 0  |                                    out = Append(out, b);  | 
76  | 0  |                                    assert(out == begin + buf_size);  | 
77  | 0  |                                    return buf_size;  | 
78  | 0  |                                  });  | 
79  | 0  |   return result;  | 
80  | 0  | }  | 
81  |  |  | 
82  | 0  | std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { | 
83  | 0  |   std::string result;  | 
84  |  |   // Use uint64_t to prevent size_t overflow. We assume it is not possible for  | 
85  |  |   // in memory strings to overflow a uint64_t.  | 
86  | 0  |   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()}; | 
87  | 0  |   const uint64_t result_size = static_cast<uint64_t>(a.size()) +  | 
88  | 0  |                                static_cast<uint64_t>(b.size()) +  | 
89  | 0  |                                static_cast<uint64_t>(c.size());  | 
90  | 0  |   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");  | 
91  | 0  |   absl::StringResizeAndOverwrite(  | 
92  | 0  |       result, static_cast<size_t>(result_size),  | 
93  | 0  |       [&a, &b, &c](char* const begin, size_t buf_size) { | 
94  | 0  |         char* out = begin;  | 
95  | 0  |         out = Append(out, a);  | 
96  | 0  |         out = Append(out, b);  | 
97  | 0  |         out = Append(out, c);  | 
98  | 0  |         assert(out == begin + buf_size);  | 
99  | 0  |         return buf_size;  | 
100  | 0  |       });  | 
101  | 0  |   return result;  | 
102  | 0  | }  | 
103  |  |  | 
104  |  | std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,  | 
105  | 0  |                    const AlphaNum& d) { | 
106  | 0  |   std::string result;  | 
107  |  |   // Use uint64_t to prevent size_t overflow. We assume it is not possible for  | 
108  |  |   // in memory strings to overflow a uint64_t.  | 
109  | 0  |   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()}; | 
110  | 0  |   const uint64_t result_size =  | 
111  | 0  |       static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size()) +  | 
112  | 0  |       static_cast<uint64_t>(c.size()) + static_cast<uint64_t>(d.size());  | 
113  | 0  |   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");  | 
114  | 0  |   absl::StringResizeAndOverwrite(  | 
115  | 0  |       result, static_cast<size_t>(result_size),  | 
116  | 0  |       [&a, &b, &c, &d](char* const begin, size_t buf_size) { | 
117  | 0  |         char* out = begin;  | 
118  | 0  |         out = Append(out, a);  | 
119  | 0  |         out = Append(out, b);  | 
120  | 0  |         out = Append(out, c);  | 
121  | 0  |         out = Append(out, d);  | 
122  | 0  |         assert(out == begin + buf_size);  | 
123  | 0  |         return buf_size;  | 
124  | 0  |       });  | 
125  | 0  |   return result;  | 
126  | 0  | }  | 
127  |  |  | 
128  |  | namespace strings_internal { | 
129  |  |  | 
130  |  | // Do not call directly - these are not part of the public API.  | 
131  | 0  | std::string CatPieces(std::initializer_list<absl::string_view> pieces) { | 
132  | 0  |   std::string result;  | 
133  |  |   // Use uint64_t to prevent size_t overflow. We assume it is not possible for  | 
134  |  |   // in memory strings to overflow a uint64_t.  | 
135  | 0  |   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()}; | 
136  | 0  |   uint64_t total_size = 0;  | 
137  | 0  |   for (absl::string_view piece : pieces) { | 
138  | 0  |     total_size += piece.size();  | 
139  | 0  |   }  | 
140  | 0  |   ABSL_INTERNAL_CHECK(total_size <= kMaxSize, "size_t overflow");  | 
141  | 0  |   absl::StringResizeAndOverwrite(result, static_cast<size_t>(total_size),  | 
142  | 0  |                                  [&pieces](char* const begin, size_t buf_size) { | 
143  | 0  |                                    char* out = begin;  | 
144  | 0  |                                    for (absl::string_view piece : pieces) { | 
145  | 0  |                                      const size_t this_size = piece.size();  | 
146  | 0  |                                      if (this_size != 0) { | 
147  | 0  |                                        memcpy(out, piece.data(), this_size);  | 
148  | 0  |                                        out += this_size;  | 
149  | 0  |                                      }  | 
150  | 0  |                                    }  | 
151  | 0  |                                    assert(out == begin + buf_size);  | 
152  | 0  |                                    return buf_size;  | 
153  | 0  |                                  });  | 
154  | 0  |   return result;  | 
155  | 0  | }  | 
156  |  |  | 
157  |  | // It's possible to call StrAppend with an absl::string_view that is itself a  | 
158  |  | // fragment of the string we're appending to.  However the results of this are  | 
159  |  | // random. Therefore, check for this in debug mode.  Use unsigned math so we  | 
160  |  | // only have to do one comparison. Note, there's an exception case: appending an  | 
161  |  | // empty string is always allowed.  | 
162  |  | #define ASSERT_NO_OVERLAP(dest, src) \  | 
163  | 0  |   assert(((src).size() == 0) ||      \  | 
164  | 0  |          (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))  | 
165  |  |  | 
166  |  | void AppendPieces(std::string* absl_nonnull dest,  | 
167  | 0  |                   std::initializer_list<absl::string_view> pieces) { | 
168  | 0  |   size_t old_size = dest->size();  | 
169  | 0  |   size_t to_append = 0;  | 
170  | 0  |   for (absl::string_view piece : pieces) { | 
171  | 0  |     ASSERT_NO_OVERLAP(*dest, piece);  | 
172  | 0  |     to_append += piece.size();  | 
173  | 0  |   }  | 
174  | 0  |   STLStringAppendUninitializedAmortized(dest, to_append);  | 
175  |  | 
  | 
176  | 0  |   char* const begin = &(*dest)[0];  | 
177  | 0  |   char* out = begin + old_size;  | 
178  | 0  |   for (absl::string_view piece : pieces) { | 
179  | 0  |     const size_t this_size = piece.size();  | 
180  | 0  |     if (this_size != 0) { | 
181  | 0  |       memcpy(out, piece.data(), this_size);  | 
182  | 0  |       out += this_size;  | 
183  | 0  |     }  | 
184  | 0  |   }  | 
185  | 0  |   assert(out == begin + dest->size());  | 
186  | 0  | }  | 
187  |  |  | 
188  |  | }  // namespace strings_internal  | 
189  |  |  | 
190  | 0  | void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a) { | 
191  | 0  |   ASSERT_NO_OVERLAP(*dest, a);  | 
192  | 0  |   std::string::size_type old_size = dest->size();  | 
193  | 0  |   STLStringAppendUninitializedAmortized(dest, a.size());  | 
194  | 0  |   char* const begin = &(*dest)[0];  | 
195  | 0  |   char* out = begin + old_size;  | 
196  | 0  |   out = Append(out, a);  | 
197  | 0  |   assert(out == begin + dest->size());  | 
198  | 0  | }  | 
199  |  |  | 
200  |  | void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,  | 
201  | 0  |                const AlphaNum& b) { | 
202  | 0  |   ASSERT_NO_OVERLAP(*dest, a);  | 
203  | 0  |   ASSERT_NO_OVERLAP(*dest, b);  | 
204  | 0  |   std::string::size_type old_size = dest->size();  | 
205  | 0  |   STLStringAppendUninitializedAmortized(dest, a.size() + b.size());  | 
206  | 0  |   char* const begin = &(*dest)[0];  | 
207  | 0  |   char* out = begin + old_size;  | 
208  | 0  |   out = Append(out, a);  | 
209  | 0  |   out = Append(out, b);  | 
210  | 0  |   assert(out == begin + dest->size());  | 
211  | 0  | }  | 
212  |  |  | 
213  |  | void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,  | 
214  | 0  |                const AlphaNum& b, const AlphaNum& c) { | 
215  | 0  |   ASSERT_NO_OVERLAP(*dest, a);  | 
216  | 0  |   ASSERT_NO_OVERLAP(*dest, b);  | 
217  | 0  |   ASSERT_NO_OVERLAP(*dest, c);  | 
218  | 0  |   std::string::size_type old_size = dest->size();  | 
219  | 0  |   STLStringAppendUninitializedAmortized(dest, a.size() + b.size() + c.size());  | 
220  | 0  |   char* const begin = &(*dest)[0];  | 
221  | 0  |   char* out = begin + old_size;  | 
222  | 0  |   out = Append(out, a);  | 
223  | 0  |   out = Append(out, b);  | 
224  | 0  |   out = Append(out, c);  | 
225  | 0  |   assert(out == begin + dest->size());  | 
226  | 0  | }  | 
227  |  |  | 
228  |  | void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,  | 
229  | 0  |                const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) { | 
230  | 0  |   ASSERT_NO_OVERLAP(*dest, a);  | 
231  | 0  |   ASSERT_NO_OVERLAP(*dest, b);  | 
232  | 0  |   ASSERT_NO_OVERLAP(*dest, c);  | 
233  | 0  |   ASSERT_NO_OVERLAP(*dest, d);  | 
234  | 0  |   std::string::size_type old_size = dest->size();  | 
235  | 0  |   STLStringAppendUninitializedAmortized(  | 
236  | 0  |       dest, a.size() + b.size() + c.size() + d.size());  | 
237  | 0  |   char* const begin = &(*dest)[0];  | 
238  | 0  |   char* out = begin + old_size;  | 
239  | 0  |   out = Append(out, a);  | 
240  | 0  |   out = Append(out, b);  | 
241  | 0  |   out = Append(out, c);  | 
242  | 0  |   out = Append(out, d);  | 
243  |  |   assert(out == begin + dest->size());  | 
244  | 0  | }  | 
245  |  |  | 
246  |  | ABSL_NAMESPACE_END  | 
247  |  | }  // namespace absl  |