Coverage Report

Created: 2024-09-23 06:29

/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
inline absl::Nonnull<char*> Append(absl::Nonnull<char*> out,
46
0
                                   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::strings_internal::STLStringResizeUninitialized(
72
0
      &result, static_cast<size_t>(result_size));
73
0
  char* const begin = &result[0];
74
0
  char* out = begin;
75
0
  out = Append(out, a);
76
0
  out = Append(out, b);
77
0
  assert(out == begin + result.size());
78
0
  return result;
79
0
}
80
81
0
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
82
0
  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
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
86
0
  const uint64_t result_size = static_cast<uint64_t>(a.size()) +
87
0
                               static_cast<uint64_t>(b.size()) +
88
0
                               static_cast<uint64_t>(c.size());
89
0
  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
90
0
  strings_internal::STLStringResizeUninitialized(
91
0
      &result, static_cast<size_t>(result_size));
92
0
  char* const begin = &result[0];
93
0
  char* out = begin;
94
0
  out = Append(out, a);
95
0
  out = Append(out, b);
96
0
  out = Append(out, c);
97
0
  assert(out == begin + result.size());
98
0
  return result;
99
0
}
100
101
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
102
0
                   const AlphaNum& d) {
103
0
  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
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
107
0
  const uint64_t result_size = static_cast<uint64_t>(a.size()) +
108
0
                               static_cast<uint64_t>(b.size()) +
109
0
                               static_cast<uint64_t>(c.size()) +
110
0
                               static_cast<uint64_t>(d.size());
111
0
  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
112
0
  strings_internal::STLStringResizeUninitialized(
113
0
      &result, static_cast<size_t>(result_size));
114
0
  char* const begin = &result[0];
115
0
  char* out = begin;
116
0
  out = Append(out, a);
117
0
  out = Append(out, b);
118
0
  out = Append(out, c);
119
0
  out = Append(out, d);
120
0
  assert(out == begin + result.size());
121
0
  return result;
122
0
}
123
124
namespace strings_internal {
125
126
// Do not call directly - these are not part of the public API.
127
0
std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
128
0
  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
0
  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
132
0
  uint64_t total_size = 0;
133
0
  for (absl::string_view piece : pieces) {
134
0
    total_size += piece.size();
135
0
  }
136
0
  ABSL_INTERNAL_CHECK(total_size <= kMaxSize, "size_t overflow");
137
0
  strings_internal::STLStringResizeUninitialized(
138
0
      &result, static_cast<size_t>(total_size));
139
140
0
  char* const begin = &result[0];
141
0
  char* out = begin;
142
0
  for (absl::string_view piece : pieces) {
143
0
    const size_t this_size = piece.size();
144
0
    if (this_size != 0) {
145
0
      memcpy(out, piece.data(), this_size);
146
0
      out += this_size;
147
0
    }
148
0
  }
149
0
  assert(out == begin + result.size());
150
0
  return result;
151
0
}
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
0
  assert(((src).size() == 0) ||      \
160
0
         (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
161
162
void AppendPieces(absl::Nonnull<std::string*> dest,
163
0
                  std::initializer_list<absl::string_view> pieces) {
164
0
  size_t old_size = dest->size();
165
0
  size_t to_append = 0;
166
0
  for (absl::string_view piece : pieces) {
167
0
    ASSERT_NO_OVERLAP(*dest, piece);
168
0
    to_append += piece.size();
169
0
  }
170
0
  STLStringAppendUninitializedAmortized(dest, to_append);
171
172
0
  char* const begin = &(*dest)[0];
173
0
  char* out = begin + old_size;
174
0
  for (absl::string_view piece : pieces) {
175
0
    const size_t this_size = piece.size();
176
0
    if (this_size != 0) {
177
0
      memcpy(out, piece.data(), this_size);
178
0
      out += this_size;
179
0
    }
180
0
  }
181
0
  assert(out == begin + dest->size());
182
0
}
183
184
}  // namespace strings_internal
185
186
0
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) {
187
0
  ASSERT_NO_OVERLAP(*dest, a);
188
0
  std::string::size_type old_size = dest->size();
189
0
  STLStringAppendUninitializedAmortized(dest, a.size());
190
0
  char* const begin = &(*dest)[0];
191
0
  char* out = begin + old_size;
192
0
  out = Append(out, a);
193
0
  assert(out == begin + dest->size());
194
0
}
195
196
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
197
0
               const AlphaNum& b) {
198
0
  ASSERT_NO_OVERLAP(*dest, a);
199
0
  ASSERT_NO_OVERLAP(*dest, b);
200
0
  std::string::size_type old_size = dest->size();
201
0
  STLStringAppendUninitializedAmortized(dest, a.size() + b.size());
202
0
  char* const begin = &(*dest)[0];
203
0
  char* out = begin + old_size;
204
0
  out = Append(out, a);
205
0
  out = Append(out, b);
206
0
  assert(out == begin + dest->size());
207
0
}
208
209
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
210
0
               const AlphaNum& b, const AlphaNum& c) {
211
0
  ASSERT_NO_OVERLAP(*dest, a);
212
0
  ASSERT_NO_OVERLAP(*dest, b);
213
0
  ASSERT_NO_OVERLAP(*dest, c);
214
0
  std::string::size_type old_size = dest->size();
215
0
  STLStringAppendUninitializedAmortized(dest, a.size() + b.size() + c.size());
216
0
  char* const begin = &(*dest)[0];
217
0
  char* out = begin + old_size;
218
0
  out = Append(out, a);
219
0
  out = Append(out, b);
220
0
  out = Append(out, c);
221
0
  assert(out == begin + dest->size());
222
0
}
223
224
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
225
0
               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
226
0
  ASSERT_NO_OVERLAP(*dest, a);
227
0
  ASSERT_NO_OVERLAP(*dest, b);
228
0
  ASSERT_NO_OVERLAP(*dest, c);
229
0
  ASSERT_NO_OVERLAP(*dest, d);
230
0
  std::string::size_type old_size = dest->size();
231
0
  STLStringAppendUninitializedAmortized(
232
0
      dest, a.size() + b.size() + c.size() + d.size());
233
0
  char* const begin = &(*dest)[0];
234
0
  char* out = begin + old_size;
235
0
  out = Append(out, a);
236
0
  out = Append(out, b);
237
0
  out = Append(out, c);
238
0
  out = Append(out, d);
239
0
  assert(out == begin + dest->size());
240
0
}
241
242
ABSL_NAMESPACE_END
243
}  // namespace absl