Coverage Report

Created: 2026-10-09 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/values/string_value.cc
Line
Count
Source
1
// Copyright 2023 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 <cstddef>
16
#include <cstdint>
17
#include <cstring>
18
#include <limits>
19
#include <string>
20
#include <tuple>
21
#include <utility>
22
#include <vector>
23
24
#include "google/protobuf/wrappers.pb.h"
25
#include "absl/base/nullability.h"
26
#include "absl/functional/overload.h"
27
#include "absl/log/absl_check.h"
28
#include "absl/status/status.h"
29
#include "absl/status/statusor.h"
30
#include "absl/strings/ascii.h"
31
#include "absl/strings/cord.h"
32
#include "absl/strings/cord_buffer.h"
33
#include "absl/strings/match.h"
34
#include "absl/strings/str_cat.h"
35
#include "absl/strings/string_view.h"
36
#include "absl/types/optional.h"
37
#include "common/internal/byte_string.h"
38
#include "common/value.h"
39
#include "internal/status_macros.h"
40
#include "internal/strings.h"
41
#include "internal/utf8.h"
42
#include "internal/well_known_types.h"
43
#include "runtime/internal/errors.h"
44
#include "google/protobuf/arena.h"
45
#include "google/protobuf/descriptor.h"
46
#include "google/protobuf/io/zero_copy_stream.h"
47
#include "google/protobuf/message.h"
48
49
namespace cel {
50
51
namespace {
52
53
using ::cel::well_known_types::ValueReflection;
54
55
template <typename Bytes>
56
47.0k
std::string StringDebugString(const Bytes& value) {
57
47.0k
  return value.NativeValue(absl::Overload(
58
47.0k
      [](absl::string_view string) -> std::string {
59
47.0k
        return internal::FormatStringLiteral(string);
60
47.0k
      },
61
47.0k
      [](const absl::Cord& cord) -> std::string {
62
0
        if (auto flat = cord.TryFlat(); flat.has_value()) {
63
0
          return internal::FormatStringLiteral(*flat);
64
0
        }
65
0
        return internal::FormatStringLiteral(static_cast<std::string>(cord));
66
0
      }));
67
47.0k
}
68
69
}  // namespace
70
71
StringValue StringValue::Concat(const StringValue& lhs, const StringValue& rhs,
72
3.40k
                                google::protobuf::Arena* absl_nonnull arena) {
73
3.40k
  return StringValue(
74
3.40k
      common_internal::ByteString::Concat(lhs.value_, rhs.value_, arena));
75
3.40k
}
76
77
47.0k
std::string StringValue::DebugString() const {
78
47.0k
  return StringDebugString(*this);
79
47.0k
}
80
81
absl::Status StringValue::SerializeTo(
82
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
83
    google::protobuf::MessageFactory* absl_nonnull message_factory,
84
0
    google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const {
85
0
  ABSL_DCHECK(descriptor_pool != nullptr);
86
0
  ABSL_DCHECK(message_factory != nullptr);
87
0
  ABSL_DCHECK(output != nullptr);
88
89
0
  google::protobuf::StringValue message;
90
0
  message.set_value(NativeString());
91
0
  if (!message.SerializePartialToZeroCopyStream(output)) {
92
0
    return absl::UnknownError(
93
0
        absl::StrCat("failed to serialize message: ", message.GetTypeName()));
94
0
  }
95
96
0
  return absl::OkStatus();
97
0
}
98
99
absl::Status StringValue::ConvertToJson(
100
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
101
    google::protobuf::MessageFactory* absl_nonnull message_factory,
102
2.53k
    google::protobuf::Message* absl_nonnull json) const {
103
2.53k
  ABSL_DCHECK(descriptor_pool != nullptr);
104
2.53k
  ABSL_DCHECK(message_factory != nullptr);
105
2.53k
  ABSL_DCHECK(json != nullptr);
106
2.53k
  ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(),
107
2.53k
                 google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE);
108
109
2.53k
  ValueReflection value_reflection;
110
2.53k
  CEL_RETURN_IF_ERROR(value_reflection.Initialize(json->GetDescriptor()));
111
2.53k
  NativeValue(
112
2.53k
      [&](const auto& value) { value_reflection.SetStringValue(json, value); });
string_value.cc:auto cel::StringValue::ConvertToJson(google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Message*) const::$_0::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
112
2.53k
      [&](const auto& value) { value_reflection.SetStringValue(json, value); });
Unexecuted instantiation: string_value.cc:auto cel::StringValue::ConvertToJson(google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Message*) const::$_0::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const
113
114
2.53k
  return absl::OkStatus();
115
2.53k
}
116
117
absl::Status StringValue::Equal(
118
    const Value& other,
119
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
120
    google::protobuf::MessageFactory* absl_nonnull message_factory,
121
2.89k
    google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const {
122
2.89k
  ABSL_DCHECK(descriptor_pool != nullptr);
123
2.89k
  ABSL_DCHECK(message_factory != nullptr);
124
2.89k
  ABSL_DCHECK(arena != nullptr);
125
2.89k
  ABSL_DCHECK(result != nullptr);
126
127
2.89k
  if (auto other_value = other.AsString(); other_value.has_value()) {
128
2.37k
    *result = NativeValue([other_value](const auto& value) -> BoolValue {
129
2.37k
      return other_value->NativeValue(
130
2.37k
          [&value](const auto& other_value) -> BoolValue {
131
2.37k
            return BoolValue{value == other_value};
132
2.37k
          });
string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
130
2.37k
          [&value](const auto& other_value) -> BoolValue {
131
2.37k
            return BoolValue{value == other_value};
132
2.37k
          });
Unexecuted instantiation: string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const::{lambda(auto:1 const&)#1}::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const
Unexecuted instantiation: string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::basic_string_view<char, {lambda(auto:1 const&)#1}::operator()::char_traits<char> > >(std::__1::basic_string_view<char, {lambda(auto:1 const&)#1}::operator()::char_traits<char> > const&) const
Unexecuted instantiation: string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const::{lambda(auto:1 const&)#1}::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const
133
2.37k
    });
string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
128
2.37k
    *result = NativeValue([other_value](const auto& value) -> BoolValue {
129
2.37k
      return other_value->NativeValue(
130
2.37k
          [&value](const auto& other_value) -> BoolValue {
131
2.37k
            return BoolValue{value == other_value};
132
2.37k
          });
133
2.37k
    });
Unexecuted instantiation: string_value.cc:cel::BoolValue cel::StringValue::Equal(cel::Value const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*, cel::Value*) const::$_0::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const
134
2.37k
    return absl::OkStatus();
135
2.37k
  }
136
517
  *result = FalseValue();
137
517
  return absl::OkStatus();
138
2.89k
}
139
140
610
size_t StringValue::Size() const {
141
610
  return value_.Visit([](const auto& alternative) -> size_t {
142
610
    return internal::Utf8CodePointCount(alternative);
143
610
  });
string_value.cc:unsigned long cel::StringValue::Size() const::$_0::operator()<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
141
610
  return value_.Visit([](const auto& alternative) -> size_t {
142
610
    return internal::Utf8CodePointCount(alternative);
143
610
  });
Unexecuted instantiation: string_value.cc:unsigned long cel::StringValue::Size() const::$_0::operator()<absl::lts_20260526::Cord>(absl::lts_20260526::Cord const&) const
144
610
}
145
146
2.07k
bool StringValue::Contains(absl::string_view string) const {
147
2.07k
  return value_.Visit(absl::Overload(
148
2.07k
      [&](absl::string_view lhs) -> bool {
149
2.07k
        return absl::StrContains(lhs, string);
150
2.07k
      },
151
2.07k
      [&](const absl::Cord& lhs) -> bool { return lhs.Contains(string); }));
152
2.07k
}
153
154
0
bool StringValue::Contains(const absl::Cord& string) const {
155
0
  return value_.Visit(absl::Overload(
156
0
      [&](absl::string_view lhs) -> bool {
157
0
        if (auto flat = string.TryFlat(); flat) {
158
0
          return absl::StrContains(lhs, *flat);
159
0
        }
160
        // There is no nice way to do this. We cannot use std::search due to
161
        // absl::Cord::CharIterator being an input iterator instead of a forward
162
        // iterator. So just make an external cord with a noop releaser. We know
163
        // the external cord will not outlive this function.
164
0
        return absl::MakeCordFromExternal(lhs, []() {}).Contains(string);
165
0
      },
166
0
      [&](const absl::Cord& lhs) -> bool { return lhs.Contains(string); }));
167
0
}
168
169
2.07k
bool StringValue::Contains(const StringValue& string) const {
170
2.07k
  return string.value_.Visit(absl::Overload(
171
2.07k
      [&](absl::string_view rhs) -> bool { return Contains(rhs); },
172
2.07k
      [&](const absl::Cord& rhs) -> bool { return Contains(rhs); }));
173
2.07k
}
174
175
0
absl::optional<int64_t> StringValue::IndexOf(absl::string_view string) const {
176
0
  return value_.Visit(absl::Overload(
177
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
178
0
        int64_t code_points = 0;
179
0
        while (lhs.size() >= string.size()) {
180
0
          if (absl::StartsWith(lhs, string)) {
181
0
            return code_points;
182
0
          }
183
0
          if (lhs.size() == string.size()) {
184
0
            break;
185
0
          }
186
0
          size_t code_units =
187
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
188
0
          lhs.remove_prefix(code_units);
189
0
          ++code_points;
190
0
        }
191
0
        return std::nullopt;
192
0
      },
193
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
194
0
        int64_t code_points = 0;
195
0
        while (lhs.size() >= string.size()) {
196
0
          if (lhs.StartsWith(string)) {
197
0
            return code_points;
198
0
          }
199
0
          if (lhs.size() == string.size()) {
200
0
            break;
201
0
          }
202
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
203
0
                                                        /*code_point=*/nullptr);
204
0
          lhs.RemovePrefix(code_units);
205
0
          ++code_points;
206
0
        }
207
0
        return std::nullopt;
208
0
      }));
209
0
}
210
211
0
absl::optional<int64_t> StringValue::IndexOf(const absl::Cord& string) const {
212
0
  return value_.Visit(absl::Overload(
213
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
214
0
        int64_t code_points = 0;
215
0
        while (lhs.size() >= string.size()) {
216
0
          if (lhs.substr(0, string.size()) == string) {
217
0
            return code_points;
218
0
          }
219
0
          if (lhs.size() == string.size()) {
220
0
            break;
221
0
          }
222
0
          size_t code_units =
223
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
224
0
          lhs.remove_prefix(code_units);
225
0
          ++code_points;
226
0
        }
227
0
        return std::nullopt;
228
0
      },
229
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
230
0
        int64_t code_points = 0;
231
0
        while (lhs.size() >= string.size()) {
232
0
          if (lhs.StartsWith(string)) {
233
0
            return code_points;
234
0
          }
235
0
          if (lhs.size() == string.size()) {
236
0
            break;
237
0
          }
238
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
239
0
                                                        /*code_point=*/nullptr);
240
0
          lhs.RemovePrefix(code_units);
241
0
          ++code_points;
242
0
        }
243
0
        return std::nullopt;
244
0
      }));
245
0
}
246
247
0
absl::optional<int64_t> StringValue::IndexOf(const StringValue& string) const {
248
0
  return string.value_.Visit(absl::Overload(
249
0
      [this](absl::string_view rhs) -> absl::optional<int64_t> {
250
0
        return IndexOf(rhs);
251
0
      },
252
0
      [this](const absl::Cord& rhs) -> absl::optional<int64_t> {
253
0
        return IndexOf(rhs);
254
0
      }));
255
0
}
256
257
absl::optional<int64_t> StringValue::IndexOf(absl::string_view string,
258
0
                                             int64_t pos) const {
259
0
  if (pos < 0) {
260
0
    pos = 0;
261
0
  }
262
0
  return value_.Visit(absl::Overload(
263
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
264
0
        int64_t code_points = 0;
265
0
        while (lhs.size() >= string.size()) {
266
0
          if (code_points >= pos && absl::StartsWith(lhs, string)) {
267
0
            return code_points;
268
0
          }
269
0
          if (lhs.size() == string.size()) {
270
0
            break;
271
0
          }
272
0
          size_t code_units =
273
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
274
0
          lhs.remove_prefix(code_units);
275
0
          ++code_points;
276
0
        }
277
0
        return std::nullopt;
278
0
      },
279
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
280
0
        int64_t code_points = 0;
281
0
        while (lhs.size() >= string.size()) {
282
0
          if (code_points >= pos && lhs.StartsWith(string)) {
283
0
            return code_points;
284
0
          }
285
0
          if (lhs.size() == string.size()) {
286
0
            break;
287
0
          }
288
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
289
0
                                                        /*code_point=*/nullptr);
290
0
          lhs.RemovePrefix(code_units);
291
0
          ++code_points;
292
0
        }
293
0
        return std::nullopt;
294
0
      }));
295
0
}
296
297
absl::optional<int64_t> StringValue::IndexOf(const absl::Cord& string,
298
0
                                             int64_t pos) const {
299
0
  if (pos < 0) {
300
0
    pos = 0;
301
0
  }
302
0
  return value_.Visit(absl::Overload(
303
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
304
0
        int64_t code_points = 0;
305
0
        while (lhs.size() >= string.size()) {
306
0
          if (code_points >= pos && lhs.substr(0, string.size()) == string) {
307
0
            return code_points;
308
0
          }
309
0
          if (lhs.size() == string.size()) {
310
0
            break;
311
0
          }
312
0
          size_t code_units =
313
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
314
0
          lhs.remove_prefix(code_units);
315
0
          ++code_points;
316
0
        }
317
0
        return std::nullopt;
318
0
      },
319
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
320
0
        int64_t code_points = 0;
321
0
        while (lhs.size() >= string.size()) {
322
0
          if (code_points >= pos && lhs.StartsWith(string)) {
323
0
            return code_points;
324
0
          }
325
0
          if (lhs.size() == string.size()) {
326
0
            break;
327
0
          }
328
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
329
0
                                                        /*code_point=*/nullptr);
330
0
          lhs.RemovePrefix(code_units);
331
0
          ++code_points;
332
0
        }
333
0
        return std::nullopt;
334
0
      }));
335
0
}
336
337
absl::optional<int64_t> StringValue::IndexOf(const StringValue& string,
338
0
                                             int64_t pos) const {
339
0
  return string.value_.Visit(absl::Overload(
340
0
      [this, pos](absl::string_view rhs) -> absl::optional<int64_t> {
341
0
        return IndexOf(rhs, pos);
342
0
      },
343
0
      [this, pos](const absl::Cord& rhs) -> absl::optional<int64_t> {
344
0
        return IndexOf(rhs, pos);
345
0
      }));
346
0
}
347
348
absl::optional<int64_t> StringValue::LastIndexOf(
349
0
    absl::string_view string) const {
350
0
  return value_.Visit(absl::Overload(
351
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
352
0
        int64_t last_index = -1;
353
0
        int64_t code_points = 0;
354
0
        while (lhs.size() >= string.size()) {
355
0
          if (absl::StartsWith(lhs, string)) {
356
0
            last_index = code_points;
357
0
          }
358
0
          if (lhs.size() == string.size()) {
359
0
            break;
360
0
          }
361
0
          size_t code_units =
362
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
363
0
          lhs.remove_prefix(code_units);
364
0
          ++code_points;
365
0
        }
366
0
        if (last_index < 0) return std::nullopt;
367
0
        return last_index;
368
0
      },
369
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
370
0
        int64_t last_index = -1;
371
0
        int64_t code_points = 0;
372
0
        while (lhs.size() >= string.size()) {
373
0
          if (lhs.StartsWith(string)) {
374
0
            last_index = code_points;
375
0
          }
376
0
          if (lhs.size() == string.size()) {
377
0
            break;
378
0
          }
379
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
380
0
                                                        /*code_point=*/nullptr);
381
0
          lhs.RemovePrefix(code_units);
382
0
          ++code_points;
383
0
        }
384
0
        if (last_index < 0) return std::nullopt;
385
0
        return last_index;
386
0
      }));
387
0
}
388
389
absl::optional<int64_t> StringValue::LastIndexOf(
390
0
    const absl::Cord& string) const {
391
0
  return value_.Visit(absl::Overload(
392
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
393
0
        int64_t last_index = -1;
394
0
        int64_t code_points = 0;
395
0
        while (lhs.size() >= string.size()) {
396
0
          if (lhs.substr(0, string.size()) == string) {
397
0
            last_index = code_points;
398
0
          }
399
0
          if (lhs.size() == string.size()) {
400
0
            break;
401
0
          }
402
0
          size_t code_units =
403
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
404
0
          lhs.remove_prefix(code_units);
405
0
          ++code_points;
406
0
        }
407
0
        if (last_index < 0) return std::nullopt;
408
0
        return last_index;
409
0
      },
410
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
411
0
        int64_t last_index = -1;
412
0
        int64_t code_points = 0;
413
0
        while (lhs.size() >= string.size()) {
414
0
          if (lhs.StartsWith(string)) {
415
0
            last_index = code_points;
416
0
          }
417
0
          if (lhs.size() == string.size()) {
418
0
            break;
419
0
          }
420
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
421
0
                                                        /*code_point=*/nullptr);
422
0
          lhs.RemovePrefix(code_units);
423
0
          ++code_points;
424
0
        }
425
0
        if (last_index < 0) return std::nullopt;
426
0
        return last_index;
427
0
      }));
428
0
}
429
430
absl::optional<int64_t> StringValue::LastIndexOf(
431
0
    const StringValue& string) const {
432
0
  return string.value_.Visit(absl::Overload(
433
0
      [this](absl::string_view rhs) -> absl::optional<int64_t> {
434
0
        return LastIndexOf(rhs);
435
0
      },
436
0
      [this](const absl::Cord& rhs) -> absl::optional<int64_t> {
437
0
        return LastIndexOf(rhs);
438
0
      }));
439
0
}
440
441
absl::optional<int64_t> StringValue::LastIndexOf(absl::string_view string,
442
0
                                                 int64_t pos) const {
443
0
  if (pos < 0) {
444
0
    return std::nullopt;
445
0
  }
446
0
  return value_.Visit(absl::Overload(
447
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
448
0
        int64_t last_index = -1;
449
0
        int64_t code_points = 0;
450
0
        while (lhs.size() >= string.size()) {
451
0
          if (absl::StartsWith(lhs, string)) {
452
0
            last_index = code_points;
453
0
          }
454
0
          if (code_points >= pos || lhs.size() == string.size()) {
455
0
            break;
456
0
          }
457
0
          size_t code_units =
458
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
459
0
          lhs.remove_prefix(code_units);
460
0
          ++code_points;
461
0
        }
462
0
        if (last_index < 0) return std::nullopt;
463
0
        return last_index;
464
0
      },
465
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
466
0
        int64_t last_index = -1;
467
0
        int64_t code_points = 0;
468
0
        while (lhs.size() >= string.size()) {
469
0
          if (lhs.StartsWith(string)) {
470
0
            last_index = code_points;
471
0
          }
472
0
          if (code_points >= pos || lhs.size() == string.size()) {
473
0
            break;
474
0
          }
475
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
476
0
                                                        /*code_point=*/nullptr);
477
0
          lhs.RemovePrefix(code_units);
478
0
          ++code_points;
479
0
        }
480
0
        if (last_index < 0) return std::nullopt;
481
0
        return last_index;
482
0
      }));
483
0
}
484
485
absl::optional<int64_t> StringValue::LastIndexOf(const absl::Cord& string,
486
0
                                                 int64_t pos) const {
487
0
  if (pos < 0) {
488
0
    return std::nullopt;
489
0
  }
490
0
  return value_.Visit(absl::Overload(
491
0
      [&](absl::string_view lhs) -> absl::optional<int64_t> {
492
0
        int64_t last_index = -1;
493
0
        int64_t code_points = 0;
494
0
        while (lhs.size() >= string.size()) {
495
0
          if (lhs.substr(0, string.size()) == string) {
496
0
            last_index = code_points;
497
0
          }
498
0
          if (code_points >= pos || lhs.size() == string.size()) {
499
0
            break;
500
0
          }
501
0
          size_t code_units =
502
0
              cel::internal::Utf8Decode(lhs, /*code_point=*/nullptr);
503
0
          lhs.remove_prefix(code_units);
504
0
          ++code_points;
505
0
        }
506
0
        if (last_index < 0) return std::nullopt;
507
0
        return last_index;
508
0
      },
509
0
      [&](absl::Cord lhs) -> absl::optional<int64_t> {
510
0
        int64_t last_index = -1;
511
0
        int64_t code_points = 0;
512
0
        while (lhs.size() >= string.size()) {
513
0
          if (lhs.StartsWith(string)) {
514
0
            last_index = code_points;
515
0
          }
516
0
          if (code_points >= pos || lhs.size() == string.size()) {
517
0
            break;
518
0
          }
519
0
          size_t code_units = cel::internal::Utf8Decode(lhs.char_begin(),
520
0
                                                        /*code_point=*/nullptr);
521
0
          lhs.RemovePrefix(code_units);
522
0
          ++code_points;
523
0
        }
524
0
        if (last_index < 0) return std::nullopt;
525
0
        return last_index;
526
0
      }));
527
0
}
528
529
absl::optional<int64_t> StringValue::LastIndexOf(const StringValue& string,
530
0
                                                 int64_t pos) const {
531
0
  return string.value_.Visit(absl::Overload(
532
0
      [this, pos](absl::string_view rhs) -> absl::optional<int64_t> {
533
0
        return LastIndexOf(rhs, pos);
534
0
      },
535
0
      [this, pos](const absl::Cord& rhs) -> absl::optional<int64_t> {
536
0
        return LastIndexOf(rhs, pos);
537
0
      }));
538
0
}
539
540
namespace {
541
542
0
absl::StatusOr<size_t> SubstringImpl(absl::string_view string, uint64_t start) {
543
0
  size_t size_code_points = 0;
544
0
  size_t size_code_units = 0;
545
0
  while (!string.empty()) {
546
0
    char32_t code_point;
547
0
    size_t code_units;
548
0
    std::tie(code_point, code_units) = cel::internal::Utf8Decode(string);
549
0
    if (size_code_points == start) {
550
0
      return size_code_units;
551
0
    }
552
0
    string.remove_prefix(code_units);
553
0
    ++size_code_points;
554
0
    size_code_units += code_units;
555
0
  }
556
0
  if (size_code_points == start) {
557
0
    return size_code_units;
558
0
  }
559
0
  return absl::InvalidArgumentError(
560
0
      "<string>.substring(<start>): <start> is greater than <string>.size()");
561
0
}
562
563
0
absl::StatusOr<size_t> SubstringImpl(const absl::Cord& cord, uint64_t start) {
564
0
  absl::Cord::CharIterator char_begin = cord.char_begin();
565
0
  absl::Cord::CharIterator char_end = cord.char_end();
566
0
  size_t size_code_points = 0;
567
0
  size_t size_code_units = 0;
568
0
  while (char_begin != char_end) {
569
0
    char32_t code_point;
570
0
    size_t code_units;
571
0
    std::tie(code_point, code_units) = cel::internal::Utf8Decode(char_begin);
572
0
    if (size_code_points == start) {
573
0
      return size_code_units;
574
0
    }
575
0
    absl::Cord::Advance(&char_begin, code_units);
576
0
    ++size_code_points;
577
0
    size_code_units += code_units;
578
0
  }
579
0
  if (size_code_points == start) {
580
0
    return size_code_units;
581
0
  }
582
0
  return absl::InvalidArgumentError(
583
0
      "<string>.substring(<start>): <start> is greater than <string>.size()");
584
0
}
585
586
}  // namespace
587
588
Value StringValue::Substring(int64_t start,
589
0
                             google::protobuf::Arena* absl_nonnull arena) const {
590
0
  if (start < 0) {
591
0
    return ErrorValue::From(
592
0
        absl::InvalidArgumentError(
593
0
            "<string>.substring(<start>): <start> is less than 0"),
594
0
        arena);
595
0
  }
596
0
  if (static_cast<uint64_t>(start) > value_.size()) {
597
0
    return ErrorValue::From(
598
0
        absl::InvalidArgumentError("<string>.substring(<start>, <end>): "
599
0
                                   "<start> or <end> is greater than "
600
0
                                   "<string>.size()"),
601
0
        arena);
602
0
  }
603
0
  if (start == 0) {
604
0
    return *this;
605
0
  }
606
0
  switch (value_.GetKind()) {
607
0
    case common_internal::ByteStringKind::kSmall: {
608
0
      absl::StatusOr<size_t> status_or_index =
609
0
          (SubstringImpl)(value_.GetSmall(), start);
610
0
      if (!status_or_index.ok()) {
611
0
        return ErrorValue::From(std::move(status_or_index).status(), arena);
612
0
      }
613
0
      StringValue result;
614
0
      result.value_.rep_.header.kind = common_internal::ByteStringKind::kSmall;
615
0
      result.value_.rep_.small.size = value_.rep_.small.size - *status_or_index;
616
0
      std::memcpy(result.value_.rep_.small.data,
617
0
                  value_.rep_.small.data + *status_or_index,
618
0
                  result.value_.rep_.small.size);
619
0
      return result;
620
0
    }
621
0
    case common_internal::ByteStringKind::kMedium: {
622
0
      absl::StatusOr<size_t> status_or_index =
623
0
          (SubstringImpl)(value_.GetMedium(), start);
624
0
      if (!status_or_index.ok()) {
625
0
        return ErrorValue::From(std::move(status_or_index).status(), arena);
626
0
      }
627
0
      StringValue result;
628
0
      result.value_.rep_.header.kind = common_internal::ByteStringKind::kMedium;
629
0
      result.value_.rep_.medium.size =
630
0
          value_.rep_.medium.size - *status_or_index;
631
0
      result.value_.rep_.medium.data =
632
0
          value_.rep_.medium.data + *status_or_index;
633
0
      result.value_.rep_.medium.arena = value_.rep_.medium.arena;
634
0
      return result;
635
0
    }
636
0
    case common_internal::ByteStringKind::kLarge: {
637
0
      absl::StatusOr<size_t> status_or_index =
638
0
          (SubstringImpl)(value_.GetLarge(), start);
639
0
      if (!status_or_index.ok()) {
640
0
        return ErrorValue::From(std::move(status_or_index).status(), arena);
641
0
      }
642
0
      return StringValue(common_internal::ByteString::Wrap(
643
0
          value_.rep_.large.data, value_.rep_.large.offset + *status_or_index,
644
0
          value_.rep_.large.size - *status_or_index, value_.rep_.large.arena));
645
0
    }
646
0
  }
647
0
}
648
649
namespace {
650
651
absl::StatusOr<std::pair<size_t, size_t>> SubstringImpl(
652
0
    absl::string_view string, uint64_t start, uint64_t end) {
653
0
  size_t size_code_points = 0;
654
0
  size_t size_code_units = 0;
655
0
  size_t start_code_units;
656
0
  while (!string.empty()) {
657
0
    if (size_code_points == start) {
658
0
      start_code_units = size_code_units;
659
0
    }
660
0
    if (size_code_points == end) {
661
0
      return std::pair{start_code_units, size_code_units};
662
0
    }
663
0
    char32_t code_point;
664
0
    size_t code_units;
665
0
    std::tie(code_point, code_units) = cel::internal::Utf8Decode(string);
666
0
    string.remove_prefix(code_units);
667
0
    ++size_code_points;
668
0
    size_code_units += code_units;
669
0
  }
670
0
  if (size_code_points == start && start == end) {
671
0
    return std::pair{size_code_units, size_code_units};
672
0
  }
673
0
  return absl::InvalidArgumentError(
674
0
      "<string>.substring(<start>, <end>): <start> or <end> is greater than "
675
0
      "<string>.size()");
676
0
}
677
678
absl::StatusOr<std::pair<size_t, size_t>> SubstringImpl(const absl::Cord& cord,
679
                                                        uint64_t start,
680
0
                                                        uint64_t end) {
681
0
  absl::Cord::CharIterator char_begin = cord.char_begin();
682
0
  absl::Cord::CharIterator char_end = cord.char_end();
683
0
  size_t size_code_points = 0;
684
0
  size_t size_code_units = 0;
685
0
  size_t start_code_units;
686
0
  while (char_begin != char_end) {
687
0
    if (size_code_points == start) {
688
0
      start_code_units = size_code_units;
689
0
    }
690
0
    if (size_code_points == end) {
691
0
      return std::pair{start_code_units, size_code_units};
692
0
    }
693
0
    char32_t code_point;
694
0
    size_t code_units;
695
0
    std::tie(code_point, code_units) = cel::internal::Utf8Decode(char_begin);
696
0
    absl::Cord::Advance(&char_begin, code_units);
697
0
    ++size_code_points;
698
0
    size_code_units += code_units;
699
0
  }
700
0
  if (size_code_points == start && start == end) {
701
0
    return std::pair{size_code_units, size_code_units};
702
0
  }
703
0
  return absl::InvalidArgumentError(
704
0
      "<string>.substring(<start>, <end>): <start> or <end> is greater than "
705
0
      "<string>.size()");
706
0
}
707
708
}  // namespace
709
710
Value StringValue::Substring(int64_t start, int64_t end,
711
0
                             google::protobuf::Arena* absl_nonnull arena) const {
712
0
  if (start < 0) {
713
0
    return ErrorValue::From(
714
0
        absl::InvalidArgumentError(
715
0
            "<string>.substring(<start>, <end>): <start> is less than 0"),
716
0
        arena);
717
0
  }
718
0
  if (end < start) {
719
0
    return ErrorValue::From(
720
0
        absl::InvalidArgumentError(
721
0
            "<string>.substring(<start>, <end>): <end> is less than <start>"),
722
0
        arena);
723
0
  }
724
0
  if (static_cast<uint64_t>(start) > value_.size() ||
725
0
      static_cast<uint64_t>(end) > value_.size()) {
726
0
    return ErrorValue::From(
727
0
        absl::InvalidArgumentError("<string>.substring(<start>, <end>): "
728
0
                                   "<start> or <end> is greater than "
729
0
                                   "<string>.size()"),
730
0
        arena);
731
0
  }
732
0
  switch (value_.GetKind()) {
733
0
    case common_internal::ByteStringKind::kSmall: {
734
0
      absl::StatusOr<std::pair<size_t, size_t>> status_or_indices =
735
0
          (SubstringImpl)(value_.GetSmall(), start, end);
736
0
      if (!status_or_indices.ok()) {
737
0
        return ErrorValue::From(std::move(status_or_indices).status(), arena);
738
0
      }
739
0
      StringValue result;
740
0
      result.value_.rep_.header.kind = common_internal::ByteStringKind::kSmall;
741
0
      result.value_.rep_.small.size =
742
0
          (status_or_indices->second - status_or_indices->first);
743
0
      std::memcpy(result.value_.rep_.small.data,
744
0
                  value_.rep_.small.data + status_or_indices->first,
745
0
                  result.value_.rep_.small.size);
746
0
      return result;
747
0
    }
748
0
    case common_internal::ByteStringKind::kMedium: {
749
0
      absl::StatusOr<std::pair<size_t, size_t>> status_or_indices =
750
0
          (SubstringImpl)(value_.GetMedium(), start, end);
751
0
      if (!status_or_indices.ok()) {
752
0
        return ErrorValue::From(std::move(status_or_indices).status(), arena);
753
0
      }
754
0
      StringValue result;
755
0
      result.value_.rep_.header.kind = common_internal::ByteStringKind::kMedium;
756
0
      result.value_.rep_.medium.size =
757
0
          (status_or_indices->second - status_or_indices->first);
758
0
      result.value_.rep_.medium.data =
759
0
          value_.rep_.medium.data + status_or_indices->first;
760
0
      result.value_.rep_.medium.arena = value_.rep_.medium.arena;
761
0
      return result;
762
0
    }
763
0
    case common_internal::ByteStringKind::kLarge: {
764
0
      absl::StatusOr<std::pair<size_t, size_t>> status_or_indices =
765
0
          (SubstringImpl)(value_.GetLarge(), start, end);
766
0
      if (!status_or_indices.ok()) {
767
0
        return ErrorValue::From(std::move(status_or_indices).status(), arena);
768
0
      }
769
0
      return StringValue(common_internal::ByteString::Wrap(
770
0
          value_.rep_.large.data,
771
0
          value_.rep_.large.offset + status_or_indices->first,
772
0
          status_or_indices->second - status_or_indices->first,
773
0
          value_.rep_.large.arena));
774
0
    }
775
0
  }
776
0
}
777
778
namespace {
779
780
0
bool LowerAsciiImpl(absl::string_view in, std::string* absl_nonnull out) {
781
0
  if (in.empty()) {
782
0
    return false;
783
0
  }
784
0
  bool needs_conversion = false;
785
0
  for (char c : in) {
786
0
    if (absl::ascii_isupper(c)) {
787
0
      needs_conversion = true;
788
0
      break;
789
0
    }
790
0
  }
791
792
0
  if (!needs_conversion) {
793
0
    return false;
794
0
  }
795
796
0
  *out = absl::AsciiStrToLower(in);
797
0
  return true;
798
0
}
799
800
0
bool LowerAsciiImpl(const absl::Cord& in, absl::Cord* absl_nonnull out) {
801
0
  if (in.empty()) {
802
0
    return false;
803
0
  }
804
0
  size_t pos = 0;
805
0
  bool needs_conversion = false;
806
0
  for (char c : in.Chars()) {
807
0
    if (absl::ascii_isupper(c)) {
808
0
      needs_conversion = true;
809
0
      break;
810
0
    }
811
0
    pos++;
812
0
  }
813
0
  if (!needs_conversion) {
814
0
    return false;
815
0
  }
816
0
  absl::Cord prefix = in.Subcord(0, pos);
817
0
  absl::Cord rest = in.Subcord(pos, in.size() - pos);
818
0
  std::string suffix;
819
0
  suffix.resize(rest.size());
820
0
  size_t current = 0;
821
0
  for (char c : rest.Chars()) {
822
0
    suffix[current++] = absl::ascii_tolower(c);
823
0
  }
824
0
  prefix.Append(std::move(suffix));
825
0
  *out = std::move(prefix);
826
0
  return true;
827
0
}
828
829
}  // namespace
830
831
0
StringValue StringValue::LowerAscii(google::protobuf::Arena* absl_nonnull arena) const {
832
0
  ABSL_DCHECK(arena != nullptr);
833
834
0
  switch (value_.GetKind()) {
835
0
    case common_internal::ByteStringKind::kSmall: {
836
0
      std::string out;
837
0
      if (!(LowerAsciiImpl)(value_.GetSmall(), &out)) {
838
0
        return *this;
839
0
      }
840
0
      return StringValue::From(std::move(out), arena);
841
0
    }
842
0
    case common_internal::ByteStringKind::kMedium: {
843
0
      std::string out;
844
0
      if (!(LowerAsciiImpl)(value_.GetMedium(), &out)) {
845
0
        return *this;
846
0
      }
847
0
      return StringValue::From(std::move(out), arena);
848
0
    }
849
0
    case common_internal::ByteStringKind::kLarge: {
850
0
      absl::Cord out;
851
0
      if (!(LowerAsciiImpl)(value_.GetLarge(), &out)) {
852
0
        return *this;
853
0
      }
854
0
      return StringValue::From(std::move(out), arena);
855
0
    }
856
0
  }
857
0
}
858
859
namespace {
860
861
0
bool UpperAsciiImpl(absl::string_view in, std::string* absl_nonnull out) {
862
0
  if (in.empty()) {
863
0
    return false;
864
0
  }
865
0
  bool needs_conversion = false;
866
0
  for (char c : in) {
867
0
    if (absl::ascii_islower(c)) {
868
0
      needs_conversion = true;
869
0
      break;
870
0
    }
871
0
  }
872
873
0
  if (!needs_conversion) {
874
0
    return false;
875
0
  }
876
877
0
  *out = absl::AsciiStrToUpper(in);
878
0
  return true;
879
0
}
880
881
0
bool UpperAsciiImpl(const absl::Cord& in, absl::Cord* absl_nonnull out) {
882
0
  if (in.empty()) {
883
0
    return false;
884
0
  }
885
0
  size_t pos = 0;
886
0
  bool needs_conversion = false;
887
0
  for (char c : in.Chars()) {
888
0
    if (absl::ascii_islower(c)) {
889
0
      needs_conversion = true;
890
0
      break;
891
0
    }
892
0
    pos++;
893
0
  }
894
0
  if (!needs_conversion) {
895
0
    return false;
896
0
  }
897
0
  absl::Cord prefix = in.Subcord(0, pos);
898
0
  absl::Cord rest = in.Subcord(pos, in.size() - pos);
899
0
  std::string suffix;
900
0
  suffix.resize(rest.size());
901
0
  size_t current = 0;
902
0
  for (char c : rest.Chars()) {
903
0
    suffix[current++] = absl::ascii_toupper(c);
904
0
  }
905
0
  prefix.Append(std::move(suffix));
906
0
  *out = std::move(prefix);
907
0
  return true;
908
0
}
909
910
}  // namespace
911
912
0
StringValue StringValue::UpperAscii(google::protobuf::Arena* absl_nonnull arena) const {
913
0
  ABSL_DCHECK(arena != nullptr);
914
0
  switch (value_.GetKind()) {
915
0
    case common_internal::ByteStringKind::kSmall: {
916
0
      std::string out;
917
0
      if (!(UpperAsciiImpl)(value_.GetSmall(), &out)) {
918
0
        return *this;
919
0
      }
920
0
      return StringValue::From(std::move(out), arena);
921
0
    }
922
0
    case common_internal::ByteStringKind::kMedium: {
923
0
      std::string out;
924
0
      if (!(UpperAsciiImpl)(value_.GetMedium(), &out)) {
925
0
        return *this;
926
0
      }
927
0
      return StringValue::From(std::move(out), arena);
928
0
    }
929
0
    case common_internal::ByteStringKind::kLarge: {
930
0
      absl::Cord out;
931
0
      if (!(UpperAsciiImpl)(value_.GetLarge(), &out)) {
932
0
        return *this;
933
0
      }
934
0
      return StringValue::From(std::move(out), arena);
935
0
    }
936
0
  }
937
0
}
938
939
namespace {
940
941
// Per CEL spec, checking for Unicode whitespace.
942
0
bool IsUnicodeWhitespace(char32_t c) {
943
0
  if (c <= 0x0020) {
944
0
    return c == 0x0020 || (c >= 0x0009 && c <= 0x000D);
945
0
  }
946
0
  if (c > 0x3000) return false;
947
0
  if (c == 0x0085 || c == 0x00a0 || c == 0x1680) return true;
948
0
  if (c >= 0x2000 && c <= 0x200a) return true;
949
0
  return c == 0x2028 || c == 0x2029 || c == 0x202f || c == 0x205f ||
950
0
         c == 0x3000;
951
0
}
952
953
0
std::pair<size_t, size_t> TrimImpl(absl::string_view string) {
954
0
  absl::string_view temp_string = string;
955
0
  size_t left_trim_bytes = 0;
956
0
  while (!temp_string.empty()) {
957
0
    char32_t c;
958
0
    size_t char_len = cel::internal::Utf8Decode(temp_string, &c);
959
0
    if (!IsUnicodeWhitespace(c)) {
960
0
      break;
961
0
    }
962
0
    temp_string.remove_prefix(char_len);
963
0
    left_trim_bytes += char_len;
964
0
  }
965
966
0
  if (left_trim_bytes == string.size()) {
967
0
    return {left_trim_bytes, 0};
968
0
  }
969
970
0
  size_t last_non_ws_end_bytes = 0;
971
0
  size_t current_pos_bytes = 0;
972
0
  temp_string = string;
973
0
  while (!temp_string.empty()) {
974
0
    char32_t c;
975
0
    size_t char_len = cel::internal::Utf8Decode(temp_string, &c);
976
0
    if (!IsUnicodeWhitespace(c)) {
977
0
      last_non_ws_end_bytes = current_pos_bytes + char_len;
978
0
    }
979
0
    current_pos_bytes += char_len;
980
0
    temp_string.remove_prefix(char_len);
981
0
  }
982
983
0
  return {left_trim_bytes, string.size() - last_non_ws_end_bytes};
984
0
}
985
986
0
std::pair<size_t, size_t> TrimImpl(const absl::Cord& cord) {
987
0
  size_t left_trim_bytes = 0;
988
0
  {
989
0
    absl::Cord::CharIterator begin = cord.char_begin();
990
0
    const absl::Cord::CharIterator end = cord.char_end();
991
0
    while (begin != end) {
992
0
      char32_t c;
993
0
      size_t char_len;
994
0
      std::tie(c, char_len) = cel::internal::Utf8Decode(begin);
995
0
      if (!IsUnicodeWhitespace(c)) {
996
0
        break;
997
0
      }
998
0
      absl::Cord::Advance(&begin, char_len);
999
0
      left_trim_bytes += char_len;
1000
0
    }
1001
0
  }
1002
1003
0
  if (left_trim_bytes == cord.size()) {
1004
0
    return {left_trim_bytes, 0};
1005
0
  }
1006
1007
0
  absl::Cord ltrimmed =
1008
0
      cord.Subcord(left_trim_bytes, cord.size() - left_trim_bytes);
1009
1010
0
  size_t last_non_ws_end_bytes = 0;
1011
0
  size_t current_pos_bytes = 0;
1012
0
  {
1013
0
    absl::Cord::CharIterator begin = ltrimmed.char_begin();
1014
0
    const absl::Cord::CharIterator end = ltrimmed.char_end();
1015
0
    while (begin != end) {
1016
0
      char32_t c;
1017
0
      size_t char_len;
1018
0
      std::tie(c, char_len) = cel::internal::Utf8Decode(begin);
1019
0
      if (!IsUnicodeWhitespace(c)) {
1020
0
        last_non_ws_end_bytes = current_pos_bytes + char_len;
1021
0
      }
1022
0
      absl::Cord::Advance(&begin, char_len);
1023
0
      current_pos_bytes += char_len;
1024
0
    }
1025
0
  }
1026
0
  return {left_trim_bytes, ltrimmed.size() - last_non_ws_end_bytes};
1027
0
}
1028
1029
}  // namespace
1030
1031
0
StringValue StringValue::Trim() const {
1032
0
  std::pair<size_t, size_t> trims;
1033
0
  size_t size;
1034
0
  switch (value_.GetKind()) {
1035
0
    case common_internal::ByteStringKind::kSmall:
1036
0
      trims = (TrimImpl)(value_.GetSmall());
1037
0
      size = value_.GetSmall().size();
1038
0
      break;
1039
0
    case common_internal::ByteStringKind::kMedium:
1040
0
      trims = (TrimImpl)(value_.GetMedium());
1041
0
      size = value_.GetMedium().size();
1042
0
      break;
1043
0
    case common_internal::ByteStringKind::kLarge:
1044
0
      trims = (TrimImpl)(value_.GetLarge());
1045
0
      size = value_.rep_.large.size;
1046
0
      break;
1047
0
  }
1048
0
  return StringValue(value_.Substring(trims.first, size - trims.second));
1049
0
}
1050
1051
namespace {
1052
1053
0
void AppendQuoteCodePoint(char32_t code_point, std::string& dst) {
1054
0
  switch (code_point) {
1055
0
    case '\a':
1056
0
      dst.append("\\a");
1057
0
      break;
1058
0
    case '\b':
1059
0
      dst.append("\\b");
1060
0
      break;
1061
0
    case '\f':
1062
0
      dst.append("\\f");
1063
0
      break;
1064
0
    case '\n':
1065
0
      dst.append("\\n");
1066
0
      break;
1067
0
    case '\r':
1068
0
      dst.append("\\r");
1069
0
      break;
1070
0
    case '\t':
1071
0
      dst.append("\\t");
1072
0
      break;
1073
0
    case '\v':
1074
0
      dst.append("\\v");
1075
0
      break;
1076
0
    case '\\':
1077
0
      dst.append("\\\\");
1078
0
      break;
1079
0
    case '\"':
1080
0
      dst.append("\\\"");
1081
0
      break;
1082
0
    default:
1083
0
      cel::internal::Utf8Encode(code_point, &dst);
1084
0
      break;
1085
0
  }
1086
0
}
1087
1088
}  // namespace
1089
1090
0
StringValue StringValue::Quote(google::protobuf::Arena* absl_nonnull arena) const {
1091
0
  return value_.Visit(absl::Overload(
1092
0
      [&](absl::string_view rep) -> StringValue {
1093
0
        std::string result;
1094
0
        result.push_back('\"');
1095
0
        while (!rep.empty()) {
1096
0
          char32_t code_point;
1097
0
          size_t code_units;
1098
0
          std::tie(code_point, code_units) = cel::internal::Utf8Decode(rep);
1099
0
          AppendQuoteCodePoint(code_point, result);
1100
0
          rep.remove_prefix(code_units);
1101
0
        }
1102
0
        result.push_back('\"');
1103
0
        return StringValue::From(std::move(result), arena);
1104
0
      },
1105
0
      [&](const absl::Cord& rep) -> StringValue {
1106
0
        absl::Cord::CharIterator begin = rep.char_begin();
1107
0
        absl::Cord::CharIterator end = rep.char_end();
1108
0
        std::string result;
1109
0
        result.push_back('\"');
1110
0
        while (begin != end) {
1111
0
          char32_t code_point;
1112
0
          size_t code_units;
1113
0
          std::tie(code_point, code_units) = cel::internal::Utf8Decode(begin);
1114
0
          AppendQuoteCodePoint(code_point, result);
1115
0
          absl::Cord::Advance(&begin, code_units);
1116
0
        }
1117
0
        result.push_back('\"');
1118
0
        return StringValue::From(std::move(result), arena);
1119
0
      }));
1120
0
}
1121
1122
0
StringValue StringValue::Reverse(google::protobuf::Arena* absl_nonnull arena) const {
1123
0
  return value_.Visit(absl::Overload(
1124
0
      [arena](absl::string_view string) -> StringValue {
1125
0
        if (string.empty()) {
1126
0
          return StringValue();
1127
0
        }
1128
0
        std::string reversed;
1129
0
        reversed.reserve(string.size());
1130
0
        const char* ptr = string.data() + string.size();
1131
0
        const char* begin = string.data();
1132
0
        while (ptr > begin) {
1133
0
          const char* char_end = ptr;
1134
0
          --ptr;
1135
          // Back up to beginning of encoded UTF-8 code point.
1136
0
          while (ptr > begin && (*ptr & 0xC0) == 0x80) {
1137
0
            --ptr;
1138
0
          }
1139
0
          reversed.append(ptr, char_end - ptr);
1140
0
        }
1141
0
        return StringValue::From(std::move(reversed), arena);
1142
0
      },
1143
0
      [arena](const absl::Cord& cord) -> StringValue {
1144
0
        if (cord.empty()) {
1145
0
          return StringValue();
1146
0
        }
1147
0
        std::vector<char32_t> code_points;
1148
0
        absl::Cord::CharIterator char_begin = cord.char_begin();
1149
0
        absl::Cord::CharIterator char_end = cord.char_end();
1150
0
        while (char_begin != char_end) {
1151
0
          char32_t code_point;
1152
0
          size_t code_units =
1153
0
              cel::internal::Utf8Decode(char_begin, &code_point);
1154
0
          code_points.push_back(code_point);
1155
0
          absl::Cord::Advance(&char_begin, code_units);
1156
0
        }
1157
0
        std::string reversed;
1158
0
        reversed.reserve(cord.size());
1159
0
        for (auto it = code_points.rbegin(); it != code_points.rend(); ++it) {
1160
0
          cel::internal::Utf8Encode(*it, &reversed);
1161
0
        }
1162
0
        return StringValue::From(std::move(reversed), arena);
1163
0
      }));
1164
0
}
1165
1166
absl::StatusOr<Value> StringValue::Join(
1167
    const ListValue& list,
1168
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
1169
    google::protobuf::MessageFactory* absl_nonnull message_factory,
1170
0
    google::protobuf::Arena* absl_nonnull arena) const {
1171
0
  Value result;
1172
0
  CEL_RETURN_IF_ERROR(
1173
0
      Join(list, descriptor_pool, message_factory, arena, &result));
1174
0
  return result;
1175
0
}
1176
1177
absl::Status StringValue::Join(
1178
    const ListValue& list,
1179
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
1180
    google::protobuf::MessageFactory* absl_nonnull message_factory,
1181
0
    google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const {
1182
0
  ABSL_DCHECK(descriptor_pool != nullptr);
1183
0
  ABSL_DCHECK(message_factory != nullptr);
1184
0
  ABSL_DCHECK(arena != nullptr);
1185
0
  ABSL_DCHECK(result != nullptr);
1186
1187
0
  std::string joined;
1188
1189
0
  CEL_ASSIGN_OR_RETURN(auto iterator, list.NewIterator());
1190
1191
0
  CEL_ASSIGN_OR_RETURN(
1192
0
      absl::optional<Value> element,
1193
0
      iterator->Next1(descriptor_pool, message_factory, arena));
1194
0
  if (element) {
1195
0
    if (auto string_element = element->AsString(); string_element) {
1196
0
      string_element->AppendToString(&joined);
1197
0
    } else {
1198
0
      ABSL_DCHECK(!element->Is<ErrorValue>());
1199
0
      *result = ErrorValue::From(
1200
0
          runtime_internal::CreateNoMatchingOverloadError("join"), arena);
1201
0
      return absl::OkStatus();
1202
0
    }
1203
0
    while (true) {
1204
0
      CEL_ASSIGN_OR_RETURN(
1205
0
          element, iterator->Next1(descriptor_pool, message_factory, arena));
1206
0
      if (!element) {
1207
0
        break;
1208
0
      }
1209
0
      AppendToString(&joined);
1210
0
      if (auto string_element = element->AsString(); string_element) {
1211
0
        string_element->AppendToString(&joined);
1212
0
      } else {
1213
0
        ABSL_DCHECK(!element->Is<ErrorValue>());
1214
0
        *result = ErrorValue::From(
1215
0
            runtime_internal::CreateNoMatchingOverloadError("join"), arena);
1216
0
        return absl::OkStatus();
1217
0
      }
1218
0
    }
1219
0
  }
1220
1221
0
  if (joined.size() > common_internal::kSmallByteStringCapacity) {
1222
0
    joined.shrink_to_fit();
1223
0
  }
1224
1225
0
  *result = StringValue::From(std::move(joined), arena);
1226
0
  return absl::OkStatus();
1227
0
}
1228
1229
absl::StatusOr<Value> StringValue::Split(
1230
    const StringValue& delimiter, int64_t limit,
1231
0
    google::protobuf::Arena* absl_nonnull arena) const {
1232
0
  Value result;
1233
0
  CEL_RETURN_IF_ERROR(Split(delimiter, limit, arena, &result));
1234
0
  return result;
1235
0
}
1236
1237
absl::Status StringValue::Split(const StringValue& delimiter,
1238
                                google::protobuf::Arena* absl_nonnull arena,
1239
0
                                Value* absl_nonnull result) const {
1240
0
  return Split(delimiter, -1, arena, result);
1241
0
}
1242
1243
absl::StatusOr<Value> StringValue::Split(
1244
0
    const StringValue& delimiter, google::protobuf::Arena* absl_nonnull arena) const {
1245
0
  Value result;
1246
0
  CEL_RETURN_IF_ERROR(Split(delimiter, -1, arena, &result));
1247
0
  return result;
1248
0
}
1249
1250
absl::Status StringValue::Split(const StringValue& delimiter, int64_t limit,
1251
                                google::protobuf::Arena* absl_nonnull arena,
1252
0
                                Value* absl_nonnull result) const {
1253
0
  ABSL_DCHECK(arena != nullptr);
1254
0
  ABSL_DCHECK(result != nullptr);
1255
1256
0
  if (limit == 0) {
1257
    // Per spec, when limit is 0 return an empty list.
1258
0
    *result = ListValue();
1259
0
    return absl::OkStatus();
1260
0
  }
1261
0
  if (limit < 0) {
1262
    // Per spec, when limit is negative treat it as unlimited splits.
1263
0
    limit = std::numeric_limits<int64_t>::max();
1264
0
  }
1265
1266
0
  std::vector<std::pair<size_t, size_t>> splits;
1267
0
  size_t pos = 0;
1268
0
  const size_t len = value_.size();
1269
1270
0
  if (delimiter.IsEmpty()) {
1271
0
    value_.Visit(absl::Overload(
1272
0
        [&](absl::string_view s) {
1273
0
          while (pos < len && limit > 1) {
1274
0
            size_t char_len = cel::internal::Utf8Decode(s.substr(pos), nullptr);
1275
0
            splits.push_back({pos, pos + char_len});
1276
0
            pos += char_len;
1277
0
            --limit;
1278
0
          }
1279
0
        },
1280
0
        [&](const absl::Cord& s) {
1281
0
          while (pos < len && limit > 1) {
1282
0
            size_t char_len = cel::internal::Utf8Decode(
1283
0
                s.Subcord(pos, len - pos).char_begin(), nullptr);
1284
0
            splits.push_back({pos, pos + char_len});
1285
0
            pos += char_len;
1286
0
            --limit;
1287
0
          }
1288
0
        }));
1289
0
  } else {
1290
0
    while (pos < len && limit > 1) {
1291
0
      absl::optional<size_t> next = value_.Find(delimiter.value_, pos);
1292
0
      if (!next) {
1293
0
        break;
1294
0
      }
1295
0
      splits.push_back(std::pair{pos, *next});
1296
0
      pos = *next + delimiter.value_.size();
1297
0
      --limit;
1298
0
      ABSL_DCHECK_LE(pos, len);
1299
0
    }
1300
0
  }
1301
1302
0
  if (splits.empty() || !delimiter.IsEmpty() || pos < len) {
1303
0
    splits.push_back(std::pair{pos, len});
1304
0
  }
1305
1306
0
  auto builder = NewListValueBuilder(arena);
1307
0
  builder->Reserve(splits.size());
1308
0
  for (const std::pair<size_t, size_t>& split : splits) {
1309
0
    builder->UnsafeAdd(
1310
0
        StringValue(value_.Substring(split.first, split.second)));
1311
0
  }
1312
0
  *result = std::move(*builder).Build();
1313
0
  return absl::OkStatus();
1314
0
}
1315
1316
absl::StatusOr<Value> StringValue::Replace(
1317
    const StringValue& needle, const StringValue& replacement, int64_t limit,
1318
0
    google::protobuf::Arena* absl_nonnull arena) const {
1319
0
  Value result;
1320
0
  CEL_RETURN_IF_ERROR(Replace(needle, replacement, limit, arena, &result));
1321
0
  return result;
1322
0
}
1323
1324
absl::Status StringValue::Replace(const StringValue& needle,
1325
                                  const StringValue& replacement,
1326
                                  google::protobuf::Arena* absl_nonnull arena,
1327
0
                                  Value* absl_nonnull result) const {
1328
0
  return Replace(needle, replacement, -1, arena, result);
1329
0
}
1330
1331
absl::StatusOr<Value> StringValue::Replace(
1332
    const StringValue& needle, const StringValue& replacement,
1333
0
    google::protobuf::Arena* absl_nonnull arena) const {
1334
0
  Value result;
1335
0
  CEL_RETURN_IF_ERROR(Replace(needle, replacement, -1, arena, &result));
1336
0
  return result;
1337
0
}
1338
1339
absl::Status StringValue::Replace(const StringValue& needle,
1340
                                  const StringValue& replacement, int64_t limit,
1341
                                  google::protobuf::Arena* absl_nonnull arena,
1342
0
                                  Value* absl_nonnull result) const {
1343
0
  ABSL_DCHECK(arena != nullptr);
1344
0
  ABSL_DCHECK(result != nullptr);
1345
1346
0
  if (limit == 0) {
1347
    // Per spec, when limit is 0 return the original string.
1348
0
    *result = *this;
1349
0
    return absl::OkStatus();
1350
0
  }
1351
0
  if (limit < 0) {
1352
    // Per spec, when limit is negative treat it as unlimited replacements.
1353
0
    limit = std::numeric_limits<int64_t>::max();
1354
0
  }
1355
1356
0
  size_t pos = 0;
1357
0
  const size_t len = value_.size();
1358
0
  const size_t needle_len = needle.value_.size();
1359
0
  std::string res_str;
1360
1361
0
  if (needle.IsEmpty()) {
1362
0
    value_.Visit(absl::Overload(
1363
0
        [&](absl::string_view s) {
1364
0
          while (pos < len && limit > 0) {
1365
0
            replacement.AppendToString(&res_str);
1366
0
            size_t char_len = cel::internal::Utf8Decode(s.substr(pos), nullptr);
1367
0
            value_.Substring(pos, pos + char_len).AppendToString(&res_str);
1368
0
            pos += char_len;
1369
0
            --limit;
1370
0
          }
1371
0
        },
1372
0
        [&](const absl::Cord& s) {
1373
0
          while (pos < len && limit > 0) {
1374
0
            replacement.AppendToString(&res_str);
1375
0
            size_t char_len = cel::internal::Utf8Decode(
1376
0
                s.Subcord(pos, len - pos).char_begin(), nullptr);
1377
0
            value_.Substring(pos, pos + char_len).AppendToString(&res_str);
1378
0
            pos += char_len;
1379
0
            --limit;
1380
0
          }
1381
0
        }));
1382
0
    if (limit > 0) {
1383
0
      replacement.AppendToString(&res_str);
1384
0
    }
1385
0
  } else {
1386
0
    while (pos < len && limit > 0) {
1387
0
      absl::optional<size_t> next = value_.Find(needle.value_, pos);
1388
0
      if (!next) {
1389
0
        break;
1390
0
      }
1391
1392
0
      value_.Substring(pos, *next).AppendToString(&res_str);
1393
0
      replacement.AppendToString(&res_str);
1394
1395
0
      pos = *next + needle_len;
1396
0
      --limit;
1397
0
    }
1398
0
  }
1399
1400
0
  if (pos < len) {
1401
0
    value_.Substring(pos, len).AppendToString(&res_str);
1402
0
  }
1403
1404
0
  if (res_str.size() > common_internal::kSmallByteStringCapacity) {
1405
0
    res_str.shrink_to_fit();
1406
0
  }
1407
1408
0
  *result = StringValue::From(std::move(res_str), arena);
1409
0
  return absl::OkStatus();
1410
0
}
1411
1412
Value StringValue::CharAt(int64_t pos,
1413
0
                          google::protobuf::Arena* absl_nonnull arena) const {
1414
0
  if (pos < 0) {
1415
0
    return ErrorValue::From(absl::InvalidArgumentError(
1416
0
                                "<string>.charAt(<pos>): <pos> is less than 0"),
1417
0
                            arena);
1418
0
  }
1419
0
  return value_.Visit(absl::Overload(
1420
0
      [this, pos, arena](absl::string_view rep) mutable -> Value {
1421
0
        while (!rep.empty()) {
1422
0
          char32_t code_point;
1423
0
          size_t code_units;
1424
0
          std::tie(code_point, code_units) = cel::internal::Utf8Decode(rep);
1425
0
          if (pos == 0) {
1426
0
            StringValue result;
1427
0
            result.value_.rep_.header.kind =
1428
0
                common_internal::ByteStringKind::kSmall;
1429
0
            result.value_.rep_.small.size = cel::internal::Utf8Encode(
1430
0
                code_point, result.value_.rep_.small.data);
1431
0
            return result;
1432
0
          }
1433
0
          rep.remove_prefix(code_units);
1434
0
          --pos;
1435
0
        }
1436
        // If we exit the loop, we iterated through all the code points in
1437
        // `rep`. `pos == 0` means we were looking for a character at index
1438
        // `size()`, which is defined to return an empty string.
1439
0
        if (pos == 0) {
1440
0
          return StringValue();
1441
0
        }
1442
0
        return ErrorValue::From(
1443
0
            absl::InvalidArgumentError("<string>.charAt(<pos>): <pos> is "
1444
0
                                       "greater than <string>.size()"),
1445
0
            arena);
1446
0
      },
1447
0
      [pos, arena](const absl::Cord& rep) mutable -> Value {
1448
0
        absl::Cord::CharIterator begin = rep.char_begin();
1449
0
        absl::Cord::CharIterator end = rep.char_end();
1450
0
        while (begin != end) {
1451
0
          char32_t code_point;
1452
0
          size_t code_units;
1453
0
          std::tie(code_point, code_units) = cel::internal::Utf8Decode(begin);
1454
0
          if (pos == 0) {
1455
0
            StringValue result;
1456
0
            result.value_.rep_.header.kind =
1457
0
                common_internal::ByteStringKind::kSmall;
1458
0
            result.value_.rep_.small.size = cel::internal::Utf8Encode(
1459
0
                code_point, result.value_.rep_.small.data);
1460
0
            return result;
1461
0
          }
1462
0
          absl::Cord::Advance(&begin, code_units);
1463
0
          --pos;
1464
0
        }
1465
        // If we exit the loop, we iterated through all the code points in
1466
        // `rep`. `pos == 0` means we were looking for a character at index
1467
        // `size()`, which is defined to return an empty string.
1468
0
        if (pos == 0) {
1469
0
          return StringValue();
1470
0
        }
1471
0
        return ErrorValue::From(
1472
0
            absl::InvalidArgumentError("<string>.charAt(<pos>): <pos> is "
1473
0
                                       "greater than <string>.size()"),
1474
0
            arena);
1475
0
      }));
1476
0
}
1477
1478
}  // namespace cel