Coverage Report

Created: 2025-07-11 06:37

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