Coverage Report

Created: 2026-07-16 06:43

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