Coverage Report

Created: 2026-10-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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/macros.h"
29
#include "absl/base/nullability.h"
30
#include "absl/base/optimization.h"
31
#include "absl/base/throw_delegate.h"
32
#include "absl/strings/internal/append_and_overwrite.h"
33
#include "absl/strings/numbers.h"
34
#include "absl/strings/resize_and_overwrite.h"
35
#include "absl/strings/string_view.h"
36
37
namespace absl {
38
ABSL_NAMESPACE_BEGIN
39
40
// ----------------------------------------------------------------------
41
// StrCat()
42
//    This merges the given strings or integers, with no delimiter. This
43
//    is designed to be the fastest possible way to construct a string out
44
//    of a mix of raw C strings, string_views, strings, and integer values.
45
// ----------------------------------------------------------------------
46
47
namespace {
48
// Append is merely a version of memcpy that returns the address of the byte
49
// after the area just overwritten.
50
0
inline char* absl_nonnull Append(char* absl_nonnull out, const AlphaNum& x) {
51
  // memcpy is allowed to overwrite arbitrary memory, so doing this after the
52
  // call would force an extra fetch of x.size().
53
0
  char* after = out + x.size();
54
0
  if (x.size() != 0) {
55
0
    memcpy(out, x.data(), x.size());
56
0
  }
57
0
  return after;
58
0
}
59
60
// Safely adds size_t values, throwing std::length_error if overflow occurs.
61
0
inline size_t SafeAdd(size_t a, size_t b) {
62
0
  const uint64_t sum = static_cast<uint64_t>(a) + b;
63
0
  if (ABSL_PREDICT_FALSE(sum > (std::numeric_limits<size_t>::max)())) {
64
0
    ThrowStdLengthError("absl string append length overflow");
65
0
  }
66
0
  return static_cast<size_t>(sum);
67
0
}
68
69
0
inline size_t SafeAdd(std::initializer_list<size_t> sizes) {
70
0
  uint64_t sum = 0;
71
0
  for (size_t size : sizes) {
72
0
    sum += size;
73
0
  }
74
0
  if (ABSL_PREDICT_FALSE(sum > (std::numeric_limits<size_t>::max)())) {
75
0
    ThrowStdLengthError("absl string append length overflow");
76
0
  }
77
0
  return static_cast<size_t>(sum);
78
0
}
79
80
template <typename Integer>
81
0
std::string IntegerToString(Integer i) {
82
  // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
83
  // with 22 bytes (including NULL at the end).
84
0
  constexpr size_t kMaxDigits10 = 22;
85
0
  std::string result;
86
0
  StringResizeAndOverwrite(
87
0
      result, kMaxDigits10, [i](char* start, size_t buf_size) {
88
        // Note: This can be optimized to not write last zero.
89
0
        char* end = numbers_internal::FastIntToBuffer(i, start);
90
0
        auto size = static_cast<size_t>(end - start);
91
0
        ABSL_ASSERT(size < buf_size);
92
0
        return size;
93
0
      });
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<int>(int)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<unsigned int>(unsigned int)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<long>(long)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<unsigned long>(unsigned long)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<long long>(long long)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::IntegerToString<unsigned long long>(unsigned long long)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
94
0
  return result;
95
0
}
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<int>(int)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<unsigned int>(unsigned int)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<long>(long)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<unsigned long>(unsigned long)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<long long>(long long)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::IntegerToString<unsigned long long>(unsigned long long)
96
97
template <typename Float>
98
0
std::string FloatToString(Float f) {
99
0
  std::string result;
100
0
  StringResizeAndOverwrite(result, numbers_internal::kSixDigitsToBufferSize,
101
0
                           [f](char* start, size_t buf_size) {
102
0
                             size_t size = numbers_internal::SixDigitsToBuffer(
103
0
                                 static_cast<double>(f), start);
104
0
                             ABSL_ASSERT(size < buf_size);
105
0
                             return size;
106
0
                           });
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::FloatToString<float>(float)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
Unexecuted instantiation: str_cat.cc:absl::(anonymous namespace)::FloatToString<double>(double)::{lambda(char*, unsigned long)#1}::operator()(char*, unsigned long) const
107
0
  return result;
108
0
}
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::FloatToString<float>(float)
Unexecuted instantiation: str_cat.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::(anonymous namespace)::FloatToString<double>(double)
109
110
}  // namespace
111
112
strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
113
0
HighPrecision(float f) {
114
0
  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
115
0
  result.size =
116
0
      strlen(numbers_internal::RoundTripFloatToBuffer(f, &result.data[0]));
117
0
  return result;
118
0
}
119
120
strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
121
0
HighPrecision(double d) {
122
0
  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
123
0
  result.size =
124
0
      strlen(numbers_internal::RoundTripDoubleToBuffer(d, &result.data[0]));
125
0
  return result;
126
0
}
127
128
0
std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
129
0
  std::string result;
130
  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
131
  // in memory strings to overflow a uint64_t.
132
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
133
0
  const uint64_t result_size =
134
0
      static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size());
135
0
  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
136
0
  absl::StringResizeAndOverwrite(result, static_cast<size_t>(result_size),
137
0
                                 [&a, &b](char* const begin, size_t buf_size) {
138
0
                                   char* out = begin;
139
0
                                   out = Append(out, a);
140
0
                                   out = Append(out, b);
141
0
                                   assert(out == begin + buf_size);
142
0
                                   return buf_size;
143
0
                                 });
144
0
  return result;
145
0
}
146
147
0
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
148
0
  std::string result;
149
  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
150
  // in memory strings to overflow a uint64_t.
151
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
152
0
  const uint64_t result_size = static_cast<uint64_t>(a.size()) +
153
0
                               static_cast<uint64_t>(b.size()) +
154
0
                               static_cast<uint64_t>(c.size());
155
0
  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
156
0
  absl::StringResizeAndOverwrite(
157
0
      result, static_cast<size_t>(result_size),
158
0
      [&a, &b, &c](char* const begin, size_t buf_size) {
159
0
        char* out = begin;
160
0
        out = Append(out, a);
161
0
        out = Append(out, b);
162
0
        out = Append(out, c);
163
0
        assert(out == begin + buf_size);
164
0
        return buf_size;
165
0
      });
166
0
  return result;
167
0
}
168
169
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
170
0
                   const AlphaNum& d) {
171
0
  std::string result;
172
  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
173
  // in memory strings to overflow a uint64_t.
174
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
175
0
  const uint64_t result_size =
176
0
      static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size()) +
177
0
      static_cast<uint64_t>(c.size()) + static_cast<uint64_t>(d.size());
178
0
  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
179
0
  absl::StringResizeAndOverwrite(
180
0
      result, static_cast<size_t>(result_size),
181
0
      [&a, &b, &c, &d](char* const begin, size_t buf_size) {
182
0
        char* out = begin;
183
0
        out = Append(out, a);
184
0
        out = Append(out, b);
185
0
        out = Append(out, c);
186
0
        out = Append(out, d);
187
0
        assert(out == begin + buf_size);
188
0
        return buf_size;
189
0
      });
190
0
  return result;
191
0
}
192
193
namespace strings_internal {
194
195
// Do not call directly - these are not part of the public API.
196
0
std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
197
0
  std::string result;
198
  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
199
  // in memory strings to overflow a uint64_t.
200
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
201
0
  uint64_t total_size = 0;
202
0
  for (absl::string_view piece : pieces) {
203
0
    total_size += piece.size();
204
0
  }
205
0
  ABSL_INTERNAL_CHECK(total_size <= kMaxSize, "size_t overflow");
206
0
  absl::StringResizeAndOverwrite(result, static_cast<size_t>(total_size),
207
0
                                 [&pieces](char* const begin, size_t buf_size) {
208
0
                                   char* out = begin;
209
0
                                   for (absl::string_view piece : pieces) {
210
0
                                     const size_t this_size = piece.size();
211
0
                                     if (this_size != 0) {
212
0
                                       memcpy(out, piece.data(), this_size);
213
0
                                       out += this_size;
214
0
                                     }
215
0
                                   }
216
0
                                   assert(out == begin + buf_size);
217
0
                                   return buf_size;
218
0
                                 });
219
0
  return result;
220
0
}
221
222
// It's possible to call StrAppend with an absl::string_view that is itself a
223
// fragment of the string we're appending to.  However the results of this are
224
// random. Therefore, check for this in debug mode.  Use unsigned math so we
225
// only have to do one comparison. Note, there's an exception case: appending an
226
// empty string is always allowed.
227
#define ASSERT_NO_OVERLAP(dest, src) \
228
0
  assert(((src).size() == 0) ||      \
229
0
         (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
230
231
void AppendPieces(std::string* absl_nonnull dest,
232
0
                  std::initializer_list<absl::string_view> pieces) {
233
0
  size_t to_append = 0;
234
0
  for (absl::string_view piece : pieces) {
235
0
    ASSERT_NO_OVERLAP(*dest, piece);
236
0
    to_append = SafeAdd(to_append, piece.size());
237
0
  }
238
0
  StringAppendAndOverwrite(*dest, to_append,
239
0
                           [&pieces](char* const buf, size_t buf_size) {
240
0
                             char* out = buf;
241
0
                             for (absl::string_view piece : pieces) {
242
0
                               const size_t this_size = piece.size();
243
0
                               if (this_size != 0) {
244
0
                                 memcpy(out, piece.data(), this_size);
245
0
                                 out += this_size;
246
0
                               }
247
0
                             }
248
0
                             assert(out == buf + buf_size);
249
0
                             return buf_size;
250
0
                           });
251
0
}
252
253
0
std::string SingleArgStrCat(int x) { return IntegerToString(x); }
254
0
std::string SingleArgStrCat(unsigned int x) { return IntegerToString(x); }
255
// NOLINTNEXTLINE
256
0
std::string SingleArgStrCat(long x) { return IntegerToString(x); }
257
// NOLINTNEXTLINE
258
0
std::string SingleArgStrCat(unsigned long x) { return IntegerToString(x); }
259
// NOLINTNEXTLINE
260
0
std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
261
// NOLINTNEXTLINE
262
0
std::string SingleArgStrCat(unsigned long long x) { return IntegerToString(x); }
263
0
std::string SingleArgStrCat(float x) { return FloatToString(x); }
264
0
std::string SingleArgStrCat(double x) { return FloatToString(x); }
265
266
}  // namespace strings_internal
267
268
0
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a) {
269
0
  ASSERT_NO_OVERLAP(*dest, a);
270
0
  strings_internal::StringAppendAndOverwrite(
271
0
      *dest, a.size(), [&a](char* const buf, size_t buf_size) {
272
0
        char* out = buf;
273
0
        out = Append(out, a);
274
0
        assert(out == buf + buf_size);
275
0
        return buf_size;
276
0
      });
277
0
}
278
279
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
280
0
               const AlphaNum& b) {
281
0
  ASSERT_NO_OVERLAP(*dest, a);
282
0
  ASSERT_NO_OVERLAP(*dest, b);
283
0
  strings_internal::StringAppendAndOverwrite(
284
0
      *dest, SafeAdd(a.size(), b.size()),
285
0
      [&a, &b](char* const buf, size_t buf_size) {
286
0
        char* out = buf;
287
0
        out = Append(out, a);
288
0
        out = Append(out, b);
289
0
        assert(out == buf + buf_size);
290
0
        return buf_size;
291
0
      });
292
0
}
293
294
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
295
0
               const AlphaNum& b, const AlphaNum& c) {
296
0
  ASSERT_NO_OVERLAP(*dest, a);
297
0
  ASSERT_NO_OVERLAP(*dest, b);
298
0
  ASSERT_NO_OVERLAP(*dest, c);
299
0
  strings_internal::StringAppendAndOverwrite(
300
0
      *dest, SafeAdd({a.size(), b.size(), c.size()}),
301
0
      [&a, &b, &c](char* const buf, size_t buf_size) {
302
0
        char* out = buf;
303
0
        out = Append(out, a);
304
0
        out = Append(out, b);
305
0
        out = Append(out, c);
306
0
        assert(out == buf + buf_size);
307
0
        return buf_size;
308
0
      });
309
0
}
310
311
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
312
0
               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
313
0
  ASSERT_NO_OVERLAP(*dest, a);
314
0
  ASSERT_NO_OVERLAP(*dest, b);
315
0
  ASSERT_NO_OVERLAP(*dest, c);
316
0
  ASSERT_NO_OVERLAP(*dest, d);
317
0
  strings_internal::StringAppendAndOverwrite(
318
0
      *dest, SafeAdd({a.size(), b.size(), c.size(), d.size()}),
319
0
      [&a, &b, &c, &d](char* const buf, size_t buf_size) {
320
0
        char* out = buf;
321
0
        out = Append(out, a);
322
0
        out = Append(out, b);
323
0
        out = Append(out, c);
324
0
        out = Append(out, d);
325
0
        assert(out == buf + buf_size);
326
0
        return buf_size;
327
0
      });
328
0
}
329
330
strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
331
0
SixDigits(double d) {
332
0
  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
333
0
      result;
334
0
  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
335
0
  return result;
336
0
}
337
338
ABSL_NAMESPACE_END
339
}  // namespace absl