Coverage Report

Created: 2023-09-25 06:27

/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 <string>
24
25
#include "absl/base/config.h"
26
#include "absl/strings/internal/resize_uninitialized.h"
27
#include "absl/strings/string_view.h"
28
29
namespace absl {
30
ABSL_NAMESPACE_BEGIN
31
32
33
// ----------------------------------------------------------------------
34
// StrCat()
35
//    This merges the given strings or integers, with no delimiter. This
36
//    is designed to be the fastest possible way to construct a string out
37
//    of a mix of raw C strings, string_views, strings, and integer values.
38
// ----------------------------------------------------------------------
39
40
namespace {
41
// Append is merely a version of memcpy that returns the address of the byte
42
// after the area just overwritten.
43
0
inline char* Append(char* out, const AlphaNum& x) {
44
  // memcpy is allowed to overwrite arbitrary memory, so doing this after the
45
  // call would force an extra fetch of x.size().
46
0
  char* after = out + x.size();
47
0
  if (x.size() != 0) {
48
0
    memcpy(out, x.data(), x.size());
49
0
  }
50
0
  return after;
51
0
}
52
53
inline void STLStringAppendUninitializedAmortized(std::string* dest,
54
0
                                                  size_t to_append) {
55
0
  strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
56
0
                                                                   to_append);
57
0
}
58
}  // namespace
59
60
0
std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
61
0
  std::string result;
62
0
  absl::strings_internal::STLStringResizeUninitialized(&result,
63
0
                                                       a.size() + b.size());
64
0
  char* const begin = &result[0];
65
0
  char* out = begin;
66
0
  out = Append(out, a);
67
0
  out = Append(out, b);
68
0
  assert(out == begin + result.size());
69
0
  return result;
70
0
}
71
72
0
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
73
0
  std::string result;
74
0
  strings_internal::STLStringResizeUninitialized(
75
0
      &result, a.size() + b.size() + c.size());
76
0
  char* const begin = &result[0];
77
0
  char* out = begin;
78
0
  out = Append(out, a);
79
0
  out = Append(out, b);
80
0
  out = Append(out, c);
81
0
  assert(out == begin + result.size());
82
0
  return result;
83
0
}
84
85
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
86
0
                   const AlphaNum& d) {
87
0
  std::string result;
88
0
  strings_internal::STLStringResizeUninitialized(
89
0
      &result, a.size() + b.size() + c.size() + d.size());
90
0
  char* const begin = &result[0];
91
0
  char* out = begin;
92
0
  out = Append(out, a);
93
0
  out = Append(out, b);
94
0
  out = Append(out, c);
95
0
  out = Append(out, d);
96
0
  assert(out == begin + result.size());
97
0
  return result;
98
0
}
99
100
namespace strings_internal {
101
102
// Do not call directly - these are not part of the public API.
103
0
std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
104
0
  std::string result;
105
0
  size_t total_size = 0;
106
0
  for (absl::string_view piece : pieces) total_size += piece.size();
107
0
  strings_internal::STLStringResizeUninitialized(&result, total_size);
108
109
0
  char* const begin = &result[0];
110
0
  char* out = begin;
111
0
  for (absl::string_view piece : pieces) {
112
0
    const size_t this_size = piece.size();
113
0
    if (this_size != 0) {
114
0
      memcpy(out, piece.data(), this_size);
115
0
      out += this_size;
116
0
    }
117
0
  }
118
0
  assert(out == begin + result.size());
119
0
  return result;
120
0
}
121
122
// It's possible to call StrAppend with an absl::string_view that is itself a
123
// fragment of the string we're appending to.  However the results of this are
124
// random. Therefore, check for this in debug mode.  Use unsigned math so we
125
// only have to do one comparison. Note, there's an exception case: appending an
126
// empty string is always allowed.
127
#define ASSERT_NO_OVERLAP(dest, src) \
128
0
  assert(((src).size() == 0) ||      \
129
0
         (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
130
131
void AppendPieces(std::string* dest,
132
0
                  std::initializer_list<absl::string_view> pieces) {
133
0
  size_t old_size = dest->size();
134
0
  size_t to_append = 0;
135
0
  for (absl::string_view piece : pieces) {
136
0
    ASSERT_NO_OVERLAP(*dest, piece);
137
0
    to_append += piece.size();
138
0
  }
139
0
  STLStringAppendUninitializedAmortized(dest, to_append);
140
141
0
  char* const begin = &(*dest)[0];
142
0
  char* out = begin + old_size;
143
0
  for (absl::string_view piece : pieces) {
144
0
    const size_t this_size = piece.size();
145
0
    if (this_size != 0) {
146
0
      memcpy(out, piece.data(), this_size);
147
0
      out += this_size;
148
0
    }
149
0
  }
150
0
  assert(out == begin + dest->size());
151
0
}
152
153
}  // namespace strings_internal
154
155
0
void StrAppend(std::string* dest, const AlphaNum& a) {
156
0
  ASSERT_NO_OVERLAP(*dest, a);
157
0
  std::string::size_type old_size = dest->size();
158
0
  STLStringAppendUninitializedAmortized(dest, a.size());
159
0
  char* const begin = &(*dest)[0];
160
0
  char* out = begin + old_size;
161
0
  out = Append(out, a);
162
0
  assert(out == begin + dest->size());
163
0
}
164
165
0
void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
166
0
  ASSERT_NO_OVERLAP(*dest, a);
167
0
  ASSERT_NO_OVERLAP(*dest, b);
168
0
  std::string::size_type old_size = dest->size();
169
0
  STLStringAppendUninitializedAmortized(dest, a.size() + b.size());
170
0
  char* const begin = &(*dest)[0];
171
0
  char* out = begin + old_size;
172
0
  out = Append(out, a);
173
0
  out = Append(out, b);
174
0
  assert(out == begin + dest->size());
175
0
}
176
177
void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
178
0
               const AlphaNum& c) {
179
0
  ASSERT_NO_OVERLAP(*dest, a);
180
0
  ASSERT_NO_OVERLAP(*dest, b);
181
0
  ASSERT_NO_OVERLAP(*dest, c);
182
0
  std::string::size_type old_size = dest->size();
183
0
  STLStringAppendUninitializedAmortized(dest, a.size() + b.size() + c.size());
184
0
  char* const begin = &(*dest)[0];
185
0
  char* out = begin + old_size;
186
0
  out = Append(out, a);
187
0
  out = Append(out, b);
188
0
  out = Append(out, c);
189
0
  assert(out == begin + dest->size());
190
0
}
191
192
void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
193
0
               const AlphaNum& c, const AlphaNum& d) {
194
0
  ASSERT_NO_OVERLAP(*dest, a);
195
0
  ASSERT_NO_OVERLAP(*dest, b);
196
0
  ASSERT_NO_OVERLAP(*dest, c);
197
0
  ASSERT_NO_OVERLAP(*dest, d);
198
0
  std::string::size_type old_size = dest->size();
199
0
  STLStringAppendUninitializedAmortized(
200
0
      dest, a.size() + b.size() + c.size() + d.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
  out = Append(out, c);
206
0
  out = Append(out, d);
207
0
  assert(out == begin + dest->size());
208
0
}
209
210
ABSL_NAMESPACE_END
211
}  // namespace absl