Coverage Report

Created: 2026-10-09 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/internal/byte_string.cc
Line
Count
Source
1
// Copyright 2024 Google LLC
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 "common/internal/byte_string.h"
16
17
#include <algorithm>
18
#include <cstddef>
19
#include <cstring>
20
#include <string>
21
#include <utility>
22
23
#include "absl/base/nullability.h"
24
#include "absl/base/optimization.h"
25
#include "absl/functional/overload.h"
26
#include "absl/log/absl_check.h"
27
#include "absl/strings/cord.h"
28
#include "absl/strings/resize_and_overwrite.h"
29
#include "absl/strings/string_view.h"
30
#include "absl/types/optional.h"
31
#include "google/protobuf/arena.h"
32
33
namespace cel::common_internal {
34
35
ByteString ByteString::From(absl::string_view value,
36
12.6k
                            google::protobuf::Arena* absl_nonnull arena) {
37
12.6k
  ABSL_DCHECK(arena != nullptr);
38
12.6k
  ByteString result(UninitializedTag{});
39
12.6k
  if (value.size() <= kSmallByteStringCapacity) {
40
11.1k
    result.SetSmall(value);
41
11.1k
  } else {
42
1.49k
    char* arena_value =
43
1.49k
        reinterpret_cast<char*>(arena->AllocateAligned(value.size()));
44
1.49k
    std::memcpy(arena_value, value.data(), value.size());
45
1.49k
    result.SetMedium(arena, absl::string_view(arena_value, value.size()));
46
1.49k
  }
47
12.6k
  return result;
48
12.6k
}
49
50
ByteString ByteString::From(absl::Cord value,
51
0
                            google::protobuf::Arena* absl_nonnull arena) {
52
0
  ABSL_DCHECK(arena != nullptr);
53
0
  ByteString result(UninitializedTag{});
54
0
  if (value.size() <= kSmallByteStringCapacity) {
55
0
    result.SetSmall(value);
56
0
  } else {
57
0
    result.SetLarge(arena,
58
0
                    google::protobuf::Arena::Create<absl::Cord>(arena, std::move(value)));
59
0
  }
60
0
  return result;
61
0
}
62
63
ByteString ByteString::From(std::string&& value,
64
1.32k
                            google::protobuf::Arena* absl_nonnull arena) {
65
1.32k
  ABSL_DCHECK(arena != nullptr);
66
1.32k
  ByteString result(UninitializedTag{});
67
1.32k
  if (value.size() <= kSmallByteStringCapacity) {
68
1.32k
    result.SetSmall(value);
69
1.32k
  } else if (value.size() > sizeof(std::string)) {
70
0
    value.shrink_to_fit();
71
0
    result.SetMedium(
72
0
        arena, google::protobuf::Arena::Create<std::string>(arena, std::move(value)));
73
0
  } else {
74
0
    char* arena_value =
75
0
        reinterpret_cast<char*>(arena->AllocateAligned(value.size()));
76
0
    std::memcpy(arena_value, value.data(), value.size());
77
0
    result.SetMedium(arena, absl::string_view(arena_value, value.size()));
78
0
  }
79
1.32k
  return result;
80
1.32k
}
81
82
ByteString ByteString::Concat(const ByteString& lhs, const ByteString& rhs,
83
4.81k
                              google::protobuf::Arena* absl_nonnull arena) {
84
4.81k
  ABSL_DCHECK(arena != nullptr);
85
86
4.81k
  if (lhs.empty()) {
87
800
    return rhs;
88
800
  }
89
4.01k
  if (rhs.empty()) {
90
857
    return lhs;
91
857
  }
92
93
3.15k
  if (lhs.GetKind() == ByteStringKind::kLarge ||
94
3.15k
      rhs.GetKind() == ByteStringKind::kLarge) {
95
    // If either the left or right are absl::Cord, use absl::Cord.
96
0
    absl::Cord result;
97
0
    lhs.AppendToCord(&result);
98
0
    rhs.AppendToCord(&result);
99
0
    return From(result, arena);
100
0
  }
101
102
3.15k
  const size_t lhs_size = lhs.size();
103
3.15k
  const size_t rhs_size = rhs.size();
104
3.15k
  const size_t result_size = lhs_size + rhs_size;
105
3.15k
  ByteString result(UninitializedTag{});
106
3.15k
  if (result_size <= kSmallByteStringCapacity) {
107
    // If the resulting string fits in inline storage, do it.
108
2.81k
    result.rep_.header.kind = ByteStringKind::kSmall;
109
2.81k
    result.rep_.small.size = result_size;
110
2.81k
    lhs.CopyToArray(result.rep_.small.data);
111
2.81k
    rhs.CopyToArray(result.rep_.small.data + lhs_size);
112
2.81k
  } else {
113
    // Otherwise allocate on the arena.
114
349
    char* result_data =
115
349
        reinterpret_cast<char*>(arena->AllocateAligned(result_size));
116
349
    lhs.CopyToArray(result_data);
117
349
    rhs.CopyToArray(result_data + lhs_size);
118
349
    result.rep_.header.kind = ByteStringKind::kMedium;
119
349
    result.rep_.medium.data = result_data;
120
349
    result.rep_.medium.size = result_size;
121
349
    result.rep_.medium.arena = arena;
122
349
  }
123
3.15k
  return result;
124
3.15k
}
125
126
0
absl::optional<absl::string_view> ByteString::TryFlat() const {
127
0
  switch (GetKind()) {
128
0
    case ByteStringKind::kSmall:
129
0
      return GetSmall();
130
0
    case ByteStringKind::kMedium:
131
0
      return GetMedium();
132
0
    case ByteStringKind::kLarge: {
133
0
      if (auto flat = rep_.large.data->TryFlat(); flat.has_value()) {
134
0
        return flat->substr(rep_.large.offset, rep_.large.size);
135
0
      }
136
0
      return absl::nullopt;
137
0
    }
138
0
  }
139
0
}
140
141
absl::optional<size_t> ByteString::Find(absl::string_view needle,
142
0
                                        size_t pos) const {
143
0
  ABSL_DCHECK_LE(pos, size());
144
145
0
  return Visit(absl::Overload(
146
0
      [&needle, pos](absl::string_view lhs) -> absl::optional<size_t> {
147
0
        absl::string_view::size_type i = lhs.find(needle, pos);
148
0
        if (i == absl::string_view::npos) {
149
0
          return std::nullopt;
150
0
        }
151
0
        return i;
152
0
      },
153
0
      [&needle, pos](const absl::Cord& lhs) -> absl::optional<size_t> {
154
0
        absl::Cord cord = lhs.Subcord(pos, lhs.size() - pos);
155
0
        absl::Cord::CharIterator it = cord.Find(needle);
156
0
        if (it == cord.char_end()) {
157
0
          return std::nullopt;
158
0
        }
159
0
        return pos +
160
0
               static_cast<size_t>(absl::Cord::Distance(cord.char_begin(), it));
161
0
      }));
162
0
}
163
164
absl::optional<size_t> ByteString::Find(const absl::Cord& needle,
165
0
                                        size_t pos) const {
166
0
  ABSL_DCHECK_LE(pos, size());
167
168
0
  return Visit(absl::Overload(
169
0
      [&needle, pos](absl::string_view lhs) -> absl::optional<size_t> {
170
0
        if (auto flat_needle = needle.TryFlat(); flat_needle) {
171
0
          absl::string_view::size_type i = lhs.find(*flat_needle, pos);
172
0
          if (i == absl::string_view::npos) {
173
0
            return std::nullopt;
174
0
          }
175
0
          return i;
176
0
        }
177
        // Needle is fragmented, we have to do a linear scan.
178
0
        const size_t needle_size = needle.size();
179
0
        if (pos + needle_size > lhs.size()) {
180
0
          return std::nullopt;
181
0
        }
182
0
        if (ABSL_PREDICT_FALSE(needle_size == 0)) {
183
0
          return pos;
184
0
        }
185
        // Optimization: find the first chunk of the needle, then compare the
186
        // rest. If the first chunk is empty, `lhs.find` will return
187
        // `current_pos`, which correctly degrades to a linear scan.
188
0
        absl::string_view first_chunk = *needle.Chunks().begin();
189
0
        absl::Cord rest_of_needle = needle.Subcord(
190
0
            first_chunk.size(), needle_size - first_chunk.size());
191
0
        size_t current_pos = pos;
192
0
        while (true) {
193
0
          size_t found_pos = lhs.find(first_chunk, current_pos);
194
0
          if (found_pos == absl::string_view::npos ||
195
0
              found_pos > lhs.size() - needle_size) {
196
0
            return std::nullopt;
197
0
          }
198
0
          if (lhs.substr(found_pos + first_chunk.size(),
199
0
                         rest_of_needle.size()) == rest_of_needle) {
200
0
            return found_pos;
201
0
          }
202
0
          current_pos = found_pos + 1;
203
0
        }
204
0
      },
205
0
      [&needle, pos](const absl::Cord& lhs) -> absl::optional<size_t> {
206
0
        absl::Cord cord = lhs.Subcord(pos, lhs.size() - pos);
207
0
        absl::Cord::CharIterator it = cord.Find(needle);
208
0
        if (it == cord.char_end()) {
209
0
          return std::nullopt;
210
0
        }
211
0
        return pos +
212
0
               static_cast<size_t>(absl::Cord::Distance(cord.char_begin(), it));
213
0
      }));
214
0
}
215
216
0
ByteString ByteString::Substring(size_t pos, size_t npos) const {
217
0
  ABSL_DCHECK_LE(npos, size());
218
0
  ABSL_DCHECK_LE(pos, npos);
219
220
0
  switch (GetKind()) {
221
0
    case ByteStringKind::kSmall: {
222
0
      ByteString result(UninitializedTag{});
223
0
      result.SetSmall(GetSmall().substr(pos, npos - pos));
224
0
      return result;
225
0
    }
226
0
    case ByteStringKind::kMedium: {
227
0
      ByteString result(UninitializedTag{});
228
0
      result.SetMedium(GetMediumArena(), GetMedium().substr(pos, npos - pos));
229
0
      return result;
230
0
    }
231
0
    case ByteStringKind::kLarge:
232
0
      ByteString result(UninitializedTag{});
233
0
      result.SetLarge(GetLargeArena(), rep_.large.data, rep_.large.offset + pos,
234
0
                      npos - pos);
235
0
      return result;
236
0
  }
237
0
}
238
239
0
void ByteString::RemovePrefix(size_t n) {
240
0
  ABSL_DCHECK_LE(n, size());
241
0
  if (n == 0) {
242
0
    return;
243
0
  }
244
0
  switch (GetKind()) {
245
0
    case ByteStringKind::kSmall:
246
0
      std::memmove(rep_.small.data, rep_.small.data + n, rep_.small.size - n);
247
0
      rep_.small.size = rep_.small.size - n;
248
0
      break;
249
0
    case ByteStringKind::kMedium:
250
0
      rep_.medium.data = rep_.medium.data + n;
251
0
      rep_.medium.size = rep_.medium.size - n;
252
0
      break;
253
0
    case ByteStringKind::kLarge:
254
0
      rep_.large.offset = rep_.large.offset + n;
255
0
      rep_.large.size = rep_.large.size - n;
256
0
      break;
257
0
  }
258
0
}
259
260
0
void ByteString::RemoveSuffix(size_t n) {
261
0
  ABSL_DCHECK_LE(n, size());
262
0
  if (n == 0) {
263
0
    return;
264
0
  }
265
0
  switch (GetKind()) {
266
0
    case ByteStringKind::kSmall:
267
0
      rep_.small.size = rep_.small.size - n;
268
0
      break;
269
0
    case ByteStringKind::kMedium:
270
0
      rep_.medium.size = rep_.medium.size - n;
271
0
      break;
272
0
    case ByteStringKind::kLarge:
273
0
      rep_.large.size = rep_.large.size - n;
274
0
      break;
275
0
  }
276
0
}
277
278
6.31k
void ByteString::CopyToArray(char* absl_nonnull out) const {
279
6.31k
  ABSL_DCHECK(out != nullptr);
280
6.31k
  switch (GetKind()) {
281
6.22k
    case ByteStringKind::kSmall: {
282
6.22k
      absl::string_view small = GetSmall();
283
6.22k
      std::memcpy(out, small.data(), small.size());
284
6.22k
    } break;
285
97
    case ByteStringKind::kMedium: {
286
97
      absl::string_view medium = GetMedium();
287
97
      std::memcpy(out, medium.data(), medium.size());
288
97
    } break;
289
0
    case ByteStringKind::kLarge: {
290
0
      (CopyCordToArray)(*rep_.large.data, rep_.large.offset, rep_.large.size,
291
0
                        out);
292
0
    } break;
293
6.31k
  }
294
6.31k
}
295
296
228k
std::string ByteString::ToString() const {
297
228k
  switch (GetKind()) {
298
167k
    case ByteStringKind::kSmall:
299
167k
      return std::string(GetSmall());
300
61.4k
    case ByteStringKind::kMedium:
301
61.4k
      return std::string(GetMedium());
302
0
    case ByteStringKind::kLarge: {
303
0
      std::string result;
304
0
      absl::StringResizeAndOverwrite(
305
0
          result, rep_.large.size,
306
0
          [this](char* buffer, size_t buffer_size) -> size_t {
307
0
            (CopyCordToArray)(*rep_.large.data, rep_.large.offset,
308
0
                              rep_.large.size, buffer);
309
0
            return rep_.large.size;
310
0
          });
311
0
      return result;
312
0
    }
313
228k
  }
314
228k
}
315
316
0
void ByteString::CopyToString(std::string* absl_nonnull out) const {
317
0
  ABSL_DCHECK(out != nullptr);
318
0
  switch (GetKind()) {
319
0
    case ByteStringKind::kSmall:
320
0
      out->assign(GetSmall());
321
0
      break;
322
0
    case ByteStringKind::kMedium:
323
0
      out->assign(GetMedium());
324
0
      break;
325
0
    case ByteStringKind::kLarge:
326
0
      absl::StringResizeAndOverwrite(
327
0
          *out, rep_.large.size,
328
0
          [this](char* buffer, size_t buffer_size) -> size_t {
329
0
            CopyCordToArray(*rep_.large.data, rep_.large.offset,
330
0
                            rep_.large.size, buffer);
331
0
            return rep_.large.size;
332
0
          });
333
0
      break;
334
0
  }
335
0
}
336
337
0
void ByteString::AppendToString(std::string* absl_nonnull out) const {
338
0
  ABSL_DCHECK(out != nullptr);
339
0
  switch (GetKind()) {
340
0
    case ByteStringKind::kSmall:
341
0
      out->append(GetSmall());
342
0
      break;
343
0
    case ByteStringKind::kMedium:
344
0
      out->append(GetMedium());
345
0
      break;
346
0
    case ByteStringKind::kLarge:
347
0
      AppendCordToString(*rep_.large.data, rep_.large.offset, rep_.large.size,
348
0
                         *out);
349
0
      break;
350
0
  }
351
0
}
352
353
0
absl::Cord ByteString::ToCord() const {
354
0
  switch (GetKind()) {
355
0
    case ByteStringKind::kSmall:
356
0
      return absl::Cord(GetSmall());
357
0
    case ByteStringKind::kMedium:
358
0
      return absl::Cord(GetMedium());
359
0
    case ByteStringKind::kLarge:
360
0
      return GetLarge();
361
0
  }
362
0
}
363
364
0
void ByteString::CopyToCord(absl::Cord* absl_nonnull out) const {
365
0
  ABSL_DCHECK(out != nullptr);
366
0
  switch (GetKind()) {
367
0
    case ByteStringKind::kSmall:
368
0
      *out = absl::Cord(GetSmall());
369
0
      break;
370
0
    case ByteStringKind::kMedium:
371
0
      *out = absl::Cord(GetMedium());
372
0
      break;
373
0
    case ByteStringKind::kLarge:
374
0
      *out = GetLarge();
375
0
      break;
376
0
  }
377
0
}
378
379
0
void ByteString::AppendToCord(absl::Cord* absl_nonnull out) const {
380
0
  ABSL_DCHECK(out != nullptr);
381
0
  switch (GetKind()) {
382
0
    case ByteStringKind::kSmall:
383
0
      out->Append(GetSmall());
384
0
      break;
385
0
    case ByteStringKind::kMedium:
386
0
      out->Append(GetMedium());
387
0
      break;
388
0
    case ByteStringKind::kLarge:
389
0
      out->Append(GetLarge());
390
0
      break;
391
0
  }
392
0
}
393
394
absl::string_view ByteString::ToStringView(
395
1.75k
    std::string* absl_nonnull scratch) const {
396
1.75k
  ABSL_DCHECK(scratch != nullptr);
397
1.75k
  switch (GetKind()) {
398
280
    case ByteStringKind::kSmall:
399
280
      return GetSmall();
400
1.47k
    case ByteStringKind::kMedium:
401
1.47k
      return GetMedium();
402
0
    case ByteStringKind::kLarge:
403
0
      if (auto flat = rep_.large.data->TryFlat(); flat.has_value()) {
404
0
        return flat->substr(rep_.large.offset, rep_.large.size);
405
0
      }
406
0
      absl::StringResizeAndOverwrite(
407
0
          *scratch, rep_.large.size,
408
0
          [this](char* buffer, size_t buffer_size) -> size_t {
409
0
            CopyCordToArray(*rep_.large.data, rep_.large.offset,
410
0
                            rep_.large.size, buffer);
411
0
            return rep_.large.size;
412
0
          });
413
0
      return absl::string_view(*scratch);
414
1.75k
  }
415
1.75k
}
416
417
0
ByteString ByteString::Clone(google::protobuf::Arena* absl_nonnull arena) const {
418
0
  ABSL_DCHECK(arena != nullptr);
419
0
  switch (GetKind()) {
420
0
    case ByteStringKind::kSmall: {
421
0
      ByteString result(UninitializedTag{});
422
0
      result.SetSmall(GetSmall());
423
0
      return result;
424
0
    }
425
0
    case ByteStringKind::kMedium: {
426
0
      google::protobuf::Arena* absl_nullable other_arena = GetMediumArena();
427
0
      if (other_arena != arena) {
428
0
        return From(GetMedium(), arena);
429
0
      }
430
0
      return *this;
431
0
    }
432
0
    case ByteStringKind::kLarge: {
433
0
      google::protobuf::Arena* absl_nullable other_arena = GetLargeArena();
434
0
      if (other_arena != arena) {
435
0
        return From(GetLarge(), arena);
436
0
      }
437
0
      return *this;
438
0
    }
439
0
  }
440
0
}
441
442
void ByteString::CopyCordToArray(const absl::Cord& cord, size_t offset,
443
0
                                 size_t size, char* data) {
444
0
  for (auto chunk : cord.Chunks()) {
445
0
    if (size == 0) {
446
0
      break;
447
0
    }
448
0
    if (offset > 0) {
449
0
      size_t min_offset = std::min(chunk.size(), offset);
450
0
      offset -= min_offset;
451
0
      if (offset > 0) {
452
0
        continue;
453
0
      }
454
0
      chunk.remove_prefix(min_offset);
455
0
    }
456
0
    size_t min_size = std::min(size, chunk.size());
457
0
    std::memcpy(data, chunk.data(), min_size);
458
0
    data += min_size;
459
0
    size -= min_size;
460
0
  }
461
0
}
462
463
void ByteString::AppendCordToString(const absl::Cord& cord, size_t offset,
464
0
                                    size_t size, std::string& data) {
465
0
  size_t original_size = data.size();
466
0
  size_t target_size = original_size + size;
467
0
  absl::StringResizeAndOverwrite(
468
0
      data, original_size + size,
469
0
      [&cord, offset, size, original_size, target_size](
470
0
          char* buf, size_t buf_size) -> size_t {
471
0
        CopyCordToArray(cord, offset, size, buf + original_size);
472
0
        return target_size;
473
0
      });
474
0
}
475
476
absl::string_view LegacyByteString(const ByteString& string, bool stable,
477
502
                                   google::protobuf::Arena* absl_nonnull arena) {
478
502
  ABSL_DCHECK(arena != nullptr);
479
502
  if (string.empty()) {
480
38
    return absl::string_view();
481
38
  }
482
464
  const ByteStringKind kind = string.GetKind();
483
464
  if (kind == ByteStringKind::kMedium && string.GetMediumArena() == arena) {
484
7
    google::protobuf::Arena* absl_nullable other_arena = string.GetMediumArena();
485
7
    if (other_arena == arena || other_arena == nullptr) {
486
      // Legacy values do not preserve arena. For speed, we assume the arena is
487
      // compatible.
488
7
      return string.GetMedium();
489
7
    }
490
7
  }
491
457
  if (stable && kind == ByteStringKind::kSmall) {
492
0
    return string.GetSmall();
493
0
  }
494
457
  std::string* absl_nonnull result = google::protobuf::Arena::Create<std::string>(arena);
495
457
  switch (kind) {
496
401
    case ByteStringKind::kSmall:
497
401
      result->assign(string.GetSmall());
498
401
      break;
499
56
    case ByteStringKind::kMedium:
500
56
      result->assign(string.GetMedium());
501
56
      break;
502
0
    case ByteStringKind::kLarge:
503
0
      absl::CopyCordToString(string.GetLarge(), result);
504
0
      break;
505
457
  }
506
457
  return absl::string_view(*result);
507
457
}
508
509
}  // namespace cel::common_internal