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