Coverage Report

Created: 2025-10-27 06:07

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/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