Coverage Report

Created: 2026-09-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/values/custom_map_value.h
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
// IWYU pragma: private, include "common/value.h"
16
// IWYU pragma: friend "common/value.h"
17
18
// `CustomMapValue` represents values of the primitive `map` type.
19
// `CustomMapValueView` is a non-owning view of `CustomMapValue`.
20
// `CustomMapValueInterface` is the abstract base class of implementations.
21
// `CustomMapValue` and `CustomMapValueView` act as smart pointers to
22
// `CustomMapValueInterface`.
23
24
#ifndef THIRD_PARTY_CEL_CPP_COMMON_VALUES_PARSED_MAP_VALUE_H_
25
#define THIRD_PARTY_CEL_CPP_COMMON_VALUES_PARSED_MAP_VALUE_H_
26
27
#include <cstddef>
28
#include <ostream>
29
#include <string>
30
#include <utility>
31
32
#include "absl/base/attributes.h"
33
#include "absl/base/nullability.h"
34
#include "absl/functional/function_ref.h"
35
#include "absl/log/absl_check.h"
36
#include "absl/status/status.h"
37
#include "absl/status/statusor.h"
38
#include "absl/strings/string_view.h"
39
#include "common/native_type.h"
40
#include "common/value_kind.h"
41
#include "common/values/custom_value.h"
42
#include "common/values/values.h"
43
#include "google/protobuf/arena.h"
44
#include "google/protobuf/descriptor.h"
45
#include "google/protobuf/io/zero_copy_stream.h"
46
#include "google/protobuf/message.h"
47
48
namespace cel {
49
50
class Value;
51
class ListValue;
52
class CustomMapValueInterface;
53
class CustomMapValueInterfaceKeysIterator;
54
class CustomMapValue;
55
using CustomMapValueContent = CustomValueContent;
56
57
// Dispatch table for `CustomMapValue`.
58
//
59
// See the documentation for `CustomMapValueInterface` for more details on
60
// composite functions.
61
//
62
// See documentation for `UnsafeCustomMapValue` on how to use this class.
63
struct CustomMapValueDispatcher {
64
  using GetTypeId =
65
      NativeTypeId (*)(const CustomMapValueDispatcher* absl_nonnull dispatcher,
66
                       CustomMapValueContent content);
67
68
  using GetArena = google::protobuf::Arena* absl_nullable (*)(
69
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
70
      CustomMapValueContent content);
71
72
  using DebugString =
73
      std::string (*)(const CustomMapValueDispatcher* absl_nonnull dispatcher,
74
                      CustomMapValueContent content);
75
76
  using SerializeTo = absl::Status (*)(
77
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
78
      CustomMapValueContent content,
79
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
80
      google::protobuf::MessageFactory* absl_nonnull message_factory,
81
      google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output);
82
83
  using ConvertToJsonObject = absl::Status (*)(
84
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
85
      CustomMapValueContent content,
86
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
87
      google::protobuf::MessageFactory* absl_nonnull message_factory,
88
      google::protobuf::Message* absl_nonnull json);
89
90
  using Equal = absl::Status (*)(
91
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
92
      CustomMapValueContent content, const MapValue& other,
93
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
94
      google::protobuf::MessageFactory* absl_nonnull message_factory,
95
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result);
96
97
  using IsZeroValue =
98
      bool (*)(const CustomMapValueDispatcher* absl_nonnull dispatcher,
99
               CustomMapValueContent content);
100
101
  using IsEmpty =
102
      bool (*)(const CustomMapValueDispatcher* absl_nonnull dispatcher,
103
               CustomMapValueContent content);
104
105
  using Size =
106
      size_t (*)(const CustomMapValueDispatcher* absl_nonnull dispatcher,
107
                 CustomMapValueContent content);
108
109
  using Find = absl::StatusOr<bool> (*)(
110
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
111
      CustomMapValueContent content, const Value& key,
112
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
113
      google::protobuf::MessageFactory* absl_nonnull message_factory,
114
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result);
115
116
  using Has = absl::StatusOr<bool> (*)(
117
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
118
      CustomMapValueContent content, const Value& key,
119
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
120
      google::protobuf::MessageFactory* absl_nonnull message_factory,
121
      google::protobuf::Arena* absl_nonnull arena);
122
123
  using ListKeys = absl::Status (*)(
124
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
125
      CustomMapValueContent content,
126
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
127
      google::protobuf::MessageFactory* absl_nonnull message_factory,
128
      google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result);
129
130
  using ForEach = absl::Status (*)(
131
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
132
      CustomMapValueContent content,
133
      absl::FunctionRef<absl::StatusOr<bool>(const Value&, const Value&)>
134
          callback,
135
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
136
      google::protobuf::MessageFactory* absl_nonnull message_factory,
137
      google::protobuf::Arena* absl_nonnull arena);
138
139
  using NewIterator = absl::StatusOr<absl_nonnull ValueIteratorPtr> (*)(
140
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
141
      CustomMapValueContent content);
142
143
  using Clone = CustomMapValue (*)(
144
      const CustomMapValueDispatcher* absl_nonnull dispatcher,
145
      CustomMapValueContent content, google::protobuf::Arena* absl_nonnull arena);
146
147
  absl_nonnull GetTypeId get_type_id;
148
149
  absl_nonnull GetArena get_arena;
150
151
  // If null, simply returns "map".
152
  absl_nullable DebugString debug_string = nullptr;
153
154
  // If null, attempts to serialize results in an UNIMPLEMENTED error.
155
  absl_nullable SerializeTo serialize_to = nullptr;
156
157
  // If null, attempts to convert to JSON results in an UNIMPLEMENTED error.
158
  absl_nullable ConvertToJsonObject convert_to_json_object = nullptr;
159
160
  // If null, an nonoptimal fallback implementation for equality is used.
161
  absl_nullable Equal equal = nullptr;
162
163
  absl_nonnull IsZeroValue is_zero_value;
164
165
  // If null, `size(...) == 0` is used.
166
  absl_nullable IsEmpty is_empty = nullptr;
167
168
  absl_nonnull Size size;
169
170
  absl_nonnull Find find;
171
172
  absl_nonnull Has has;
173
174
  absl_nonnull ListKeys list_keys;
175
176
  // If null, a fallback implementation based on `list_keys` is used.
177
  absl_nullable ForEach for_each = nullptr;
178
179
  // If null, a fallback implementation based on `list_keys` is used.
180
  absl_nullable NewIterator new_iterator = nullptr;
181
182
  absl_nonnull Clone clone;
183
};
184
185
class CustomMapValueInterface {
186
 public:
187
189k
  CustomMapValueInterface() = default;
188
  CustomMapValueInterface(const CustomMapValueInterface&) = delete;
189
  CustomMapValueInterface(CustomMapValueInterface&&) = delete;
190
191
86.8k
  virtual ~CustomMapValueInterface() = default;
192
193
  CustomMapValueInterface& operator=(const CustomMapValueInterface&) = delete;
194
  CustomMapValueInterface& operator=(CustomMapValueInterface&&) = delete;
195
196
  using ForEachCallback =
197
      absl::FunctionRef<absl::StatusOr<bool>(const Value&, const Value&)>;
198
199
 private:
200
  friend class CustomMapValueInterfaceIterator;
201
  friend class CustomMapValue;
202
  friend absl::Status common_internal::MapValueEqual(
203
      const CustomMapValueInterface& lhs, const MapValue& rhs,
204
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
205
      google::protobuf::MessageFactory* absl_nonnull message_factory,
206
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result);
207
208
  virtual std::string DebugString() const = 0;
209
210
  virtual absl::Status SerializeTo(
211
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
212
      google::protobuf::MessageFactory* absl_nonnull message_factory,
213
      google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const;
214
215
  virtual absl::Status ConvertToJsonObject(
216
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
217
      google::protobuf::MessageFactory* absl_nonnull message_factory,
218
      google::protobuf::Message* absl_nonnull json) const = 0;
219
220
  virtual absl::Status Equal(
221
      const MapValue& other,
222
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
223
      google::protobuf::MessageFactory* absl_nonnull message_factory,
224
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const;
225
226
0
  virtual bool IsZeroValue() const { return IsEmpty(); }
227
228
  // Returns `true` if this map contains no entries, `false` otherwise.
229
0
  virtual bool IsEmpty() const { return Size() == 0; }
230
231
  // Returns the number of entries in this map.
232
  virtual size_t Size() const = 0;
233
234
  // See the corresponding member function of `MapValue` for
235
  // documentation.
236
  virtual absl::Status ListKeys(
237
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
238
      google::protobuf::MessageFactory* absl_nonnull message_factory,
239
      google::protobuf::Arena* absl_nonnull arena,
240
      ListValue* absl_nonnull result) const = 0;
241
242
  // See the corresponding member function of `MapValue` for
243
  // documentation.
244
  virtual absl::Status ForEach(
245
      ForEachCallback callback,
246
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
247
      google::protobuf::MessageFactory* absl_nonnull message_factory,
248
      google::protobuf::Arena* absl_nonnull arena) const;
249
250
  // By default, implementations do not guarantee any iteration order. Unless
251
  // specified otherwise, assume the iteration order is random.
252
  virtual absl::StatusOr<absl_nonnull ValueIteratorPtr> NewIterator() const;
253
254
  virtual CustomMapValue Clone(google::protobuf::Arena* absl_nonnull arena) const = 0;
255
256
  // Tests whether the map contains the given key. If it does, the value
257
  // associated with the key is written to `result` and the function returns
258
  // true. Otherwise, the function returns false and `result` is set to
259
  // `NullValue`.
260
  //
261
  // A non-ok status is converted to an ErrorValue (e.g. wrong key type).
262
  virtual absl::StatusOr<bool> Find(
263
      const Value& key,
264
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
265
      google::protobuf::MessageFactory* absl_nonnull message_factory,
266
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const = 0;
267
268
  // Whether the map has the given key.
269
  //
270
  // A non-ok status is converted to an ErrorValue (e.g. wrong key type).
271
  virtual absl::StatusOr<bool> Has(
272
      const Value& key,
273
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
274
      google::protobuf::MessageFactory* absl_nonnull message_factory,
275
      google::protobuf::Arena* absl_nonnull arena) const = 0;
276
277
  virtual NativeTypeId GetNativeTypeId() const = 0;
278
279
  struct Content {
280
    const CustomMapValueInterface* absl_nonnull interface;
281
    google::protobuf::Arena* absl_nullable arena;
282
  };
283
};
284
285
// Creates a custom map value from a manual dispatch table `dispatcher` and
286
// opaque data `content` whose format is only know to functions in the manual
287
// dispatch table. The dispatch table should probably be valid for the lifetime
288
// of the process, but at a minimum must outlive all instances of the resulting
289
// value.
290
//
291
// IMPORTANT: This approach to implementing CustomMapValue should only be
292
// used when you know exactly what you are doing. When in doubt, just implement
293
// CustomMapValueInterface.
294
CustomMapValue UnsafeCustomMapValue(const CustomMapValueDispatcher* absl_nonnull
295
                                    dispatcher ABSL_ATTRIBUTE_LIFETIME_BOUND,
296
                                    CustomMapValueContent content);
297
298
class CustomMapValue final
299
    : private common_internal::MapValueMixin<CustomMapValue> {
300
 public:
301
  static constexpr ValueKind kKind = ValueKind::kMap;
302
303
  // Constructs a custom map value from an implementation of
304
  // `CustomMapValueInterface` `interface` whose lifetime is tied to that of
305
  // the arena `arena`.
306
  CustomMapValue(const CustomMapValueInterface* absl_nonnull
307
                 interface ABSL_ATTRIBUTE_LIFETIME_BOUND,
308
                 google::protobuf::Arena* absl_nonnull arena
309
189k
                     ABSL_ATTRIBUTE_LIFETIME_BOUND) {
310
189k
    ABSL_DCHECK(interface != nullptr);
311
189k
    ABSL_DCHECK(arena != nullptr);
312
189k
    content_ = CustomMapValueContent::From(CustomMapValueInterface::Content{
313
189k
        .interface = interface, .arena = arena});
314
189k
  }
315
316
  // By default, this creates an empty map whose type is `map(dyn, dyn)`. Unless
317
  // you can help it, you should use a more specific typed map value.
318
  CustomMapValue();
319
  CustomMapValue(const CustomMapValue&) = default;
320
  CustomMapValue(CustomMapValue&&) = default;
321
  CustomMapValue& operator=(const CustomMapValue&) = default;
322
  CustomMapValue& operator=(CustomMapValue&&) = default;
323
324
0
  static constexpr ValueKind kind() { return kKind; }
325
326
  NativeTypeId GetTypeId() const;
327
328
  absl::string_view GetTypeName() const;
329
330
  std::string DebugString() const;
331
332
  // See Value::SerializeTo().
333
  absl::Status SerializeTo(
334
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
335
      google::protobuf::MessageFactory* absl_nonnull message_factory,
336
      google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const;
337
338
  // See Value::ConvertToJson().
339
  absl::Status ConvertToJson(
340
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
341
      google::protobuf::MessageFactory* absl_nonnull message_factory,
342
      google::protobuf::Message* absl_nonnull json) const;
343
344
  // See Value::ConvertToJsonObject().
345
  absl::Status ConvertToJsonObject(
346
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
347
      google::protobuf::MessageFactory* absl_nonnull message_factory,
348
      google::protobuf::Message* absl_nonnull json) const;
349
350
  absl::Status Equal(const Value& other,
351
                     const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
352
                     google::protobuf::MessageFactory* absl_nonnull message_factory,
353
                     google::protobuf::Arena* absl_nonnull arena,
354
                     Value* absl_nonnull result) const;
355
  using MapValueMixin::Equal;
356
357
  bool IsZeroValue() const;
358
359
  CustomMapValue Clone(google::protobuf::Arena* absl_nonnull arena) const;
360
361
  bool IsEmpty() const;
362
363
  size_t Size() const;
364
365
  // See the corresponding member function of `MapValue` for
366
  // documentation.
367
  absl::Status Get(const Value& key,
368
                   const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
369
                   google::protobuf::MessageFactory* absl_nonnull message_factory,
370
                   google::protobuf::Arena* absl_nonnull arena,
371
                   Value* absl_nonnull result) const;
372
  using MapValueMixin::Get;
373
374
  // See the corresponding member function of `MapValue` for
375
  // documentation.
376
  absl::StatusOr<bool> Find(
377
      const Value& key,
378
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
379
      google::protobuf::MessageFactory* absl_nonnull message_factory,
380
      google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const;
381
  using MapValueMixin::Find;
382
383
  // See the corresponding member function of `MapValue` for
384
  // documentation.
385
  absl::Status Has(const Value& key,
386
                   const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
387
                   google::protobuf::MessageFactory* absl_nonnull message_factory,
388
                   google::protobuf::Arena* absl_nonnull arena,
389
                   Value* absl_nonnull result) const;
390
  using MapValueMixin::Has;
391
392
  // See the corresponding member function of `MapValue` for
393
  // documentation.
394
  absl::Status ListKeys(
395
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
396
      google::protobuf::MessageFactory* absl_nonnull message_factory,
397
      google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result) const;
398
  using MapValueMixin::ListKeys;
399
400
  // See the corresponding type declaration of `MapValueInterface` for
401
  // documentation.
402
  using ForEachCallback = typename CustomMapValueInterface::ForEachCallback;
403
404
  // See the corresponding member function of `MapValue` for
405
  // documentation.
406
  absl::Status ForEach(
407
      ForEachCallback callback,
408
      const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
409
      google::protobuf::MessageFactory* absl_nonnull message_factory,
410
      google::protobuf::Arena* absl_nonnull arena) const;
411
412
  // See the corresponding member function of `MapValue` for
413
  // documentation.
414
  absl::StatusOr<absl_nonnull ValueIteratorPtr> NewIterator() const;
415
416
0
  const CustomMapValueDispatcher* absl_nullable dispatcher() const {
417
0
    return dispatcher_;
418
0
  }
419
420
0
  CustomMapValueContent content() const {
421
0
    ABSL_DCHECK(dispatcher_ != nullptr);
422
0
    return content_;
423
0
  }
424
425
28
  const CustomMapValueInterface* absl_nullable interface() const {
426
28
    if (dispatcher_ == nullptr) {
427
28
      return content_.To<CustomMapValueInterface::Content>().interface;
428
28
    }
429
0
    return nullptr;
430
28
  }
431
432
0
  friend void swap(CustomMapValue& lhs, CustomMapValue& rhs) noexcept {
433
0
    using std::swap;
434
0
    swap(lhs.dispatcher_, rhs.dispatcher_);
435
0
    swap(lhs.content_, rhs.content_);
436
0
  }
437
438
 private:
439
  friend class common_internal::ValueMixin<CustomMapValue>;
440
  friend class common_internal::MapValueMixin<CustomMapValue>;
441
  friend CustomMapValue UnsafeCustomMapValue(
442
      const CustomMapValueDispatcher* absl_nonnull dispatcher
443
          ABSL_ATTRIBUTE_LIFETIME_BOUND,
444
      CustomMapValueContent content);
445
446
  CustomMapValue(const CustomMapValueDispatcher* absl_nonnull dispatcher,
447
                 CustomMapValueContent content)
448
0
      : dispatcher_(dispatcher), content_(content) {
449
0
    ABSL_DCHECK(dispatcher != nullptr);
450
0
    ABSL_DCHECK(dispatcher->get_type_id != nullptr);
451
0
    ABSL_DCHECK(dispatcher->get_arena != nullptr);
452
0
    ABSL_DCHECK(dispatcher->is_zero_value != nullptr);
453
0
    ABSL_DCHECK(dispatcher->size != nullptr);
454
0
    ABSL_DCHECK(dispatcher->find != nullptr);
455
0
    ABSL_DCHECK(dispatcher->has != nullptr);
456
0
    ABSL_DCHECK(dispatcher->list_keys != nullptr);
457
0
    ABSL_DCHECK(dispatcher->clone != nullptr);
458
0
  }
459
460
  const CustomMapValueDispatcher* absl_nullable dispatcher_ = nullptr;
461
  CustomMapValueContent content_ = CustomMapValueContent::Zero();
462
};
463
464
0
inline std::ostream& operator<<(std::ostream& out, const CustomMapValue& type) {
465
0
  return out << type.DebugString();
466
0
}
467
468
template <>
469
struct NativeTypeTraits<CustomMapValue> final {
470
28
  static NativeTypeId Id(const CustomMapValue& type) {
471
28
    return type.GetTypeId();
472
28
  }
473
};
474
475
inline CustomMapValue UnsafeCustomMapValue(
476
    const CustomMapValueDispatcher* absl_nonnull dispatcher
477
        ABSL_ATTRIBUTE_LIFETIME_BOUND,
478
0
    CustomMapValueContent content) {
479
0
  return CustomMapValue(dispatcher, content);
480
0
}
481
482
}  // namespace cel
483
484
#endif  // THIRD_PARTY_CEL_CPP_COMMON_VALUES_PARSED_MAP_VALUE_H_