@@ -88,19 +88,27 @@ enum class ProtoWireType : uint32_t {
8888 kFixed32 = 5 ,
8989};
9090
91+ inline constexpr uint32_t kProtoWireTypeMask = uint32_t {0x7 };
92+ inline constexpr int kFieldNumberShift = 3 ;
93+
9194class ProtoWireTag final {
9295 public:
96+ static constexpr uint32_t kTypeMask = uint32_t {0x7 };
97+ static constexpr int kFieldNumberShift = 3 ;
98+
9399 constexpr explicit ProtoWireTag (uint32_t tag) : tag_(tag) {}
94100
95101 constexpr ProtoWireTag (uint32_t field_number, ProtoWireType type)
96- : ProtoWireTag((field_number << 3 ) | static_cast<uint32_t>(type)) {
97- ABSL_ASSERT (((field_number << 3 ) >> 3 ) == field_number);
102+ : ProtoWireTag((field_number << kFieldNumberShift ) |
103+ static_cast<uint32_t>(type)) {
104+ ABSL_ASSERT (((field_number << kFieldNumberShift ) >> kFieldNumberShift ) ==
105+ field_number);
98106 }
99107
100- constexpr uint32_t field_number () const { return tag_ >> 3 ; }
108+ constexpr uint32_t field_number () const { return tag_ >> kFieldNumberShift ; }
101109
102110 constexpr ProtoWireType type () const {
103- return static_cast <ProtoWireType>(tag_ & uint32_t { 0x7 } );
111+ return static_cast <ProtoWireType>(tag_ & kTypeMask );
104112 }
105113
106114 // NOLINTNEXTLINE(google-explicit-constructor)
@@ -110,6 +118,14 @@ class ProtoWireTag final {
110118 uint32_t tag_;
111119};
112120
121+ inline constexpr bool ProtoWireTypeIsValid (ProtoWireType type) {
122+ // Ensure `type` is only [0-5]. The bitmask for `type` is 0x7 which allows 6
123+ // to exist, but that is not used and invalid. We detect that here.
124+ return (static_cast <uint32_t >(type) & uint32_t {0x7 }) ==
125+ static_cast <uint32_t >(type) &&
126+ static_cast <uint32_t >(type) != uint32_t {0x6 };
127+ }
128+
113129// Creates the "tag" of a record, see
114130// https://protobuf.dev/programming-guides/encoding/#structure.
115131inline constexpr uint32_t MakeProtoWireTag (uint32_t field_number,
@@ -168,6 +184,30 @@ inline void VarintEncode(bool value, absl::Cord& buffer) {
168184 buffer.Append (absl::string_view (&scratch, 1 ));
169185}
170186
187+ inline void Fixed32EncodeUnsafe (uint64_t value, char * buffer) {
188+ buffer[0 ] = static_cast <char >(static_cast <uint8_t >(value));
189+ buffer[1 ] = static_cast <char >(static_cast <uint8_t >(value >> 8 ));
190+ buffer[2 ] = static_cast <char >(static_cast <uint8_t >(value >> 16 ));
191+ buffer[3 ] = static_cast <char >(static_cast <uint8_t >(value >> 24 ));
192+ }
193+
194+ // Encodes `value` as a fixed-size number, see
195+ // https://protobuf.dev/programming-guides/encoding/#non-varint-numbers.
196+ inline void Fixed32Encode (uint32_t value, absl::Cord& buffer) {
197+ // `absl::Cord::GetAppendBuffer` will allocate a block regardless of whether
198+ // `buffer` has enough inline storage space left. To take advantage of inline
199+ // storage space, we need to just do a plain append.
200+ char scratch[4 ];
201+ Fixed32EncodeUnsafe (value, scratch);
202+ buffer.Append (absl::string_view (scratch, ABSL_ARRAYSIZE (scratch)));
203+ }
204+
205+ // Encodes `value` as a fixed-size number, see
206+ // https://protobuf.dev/programming-guides/encoding/#non-varint-numbers.
207+ inline void Fixed32Encode (float value, absl::Cord& buffer) {
208+ Fixed32Encode (absl::bit_cast<uint32_t >(value), buffer);
209+ }
210+
171211inline void Fixed64EncodeUnsafe (uint64_t value, char * buffer) {
172212 buffer[0 ] = static_cast <char >(static_cast <uint8_t >(value));
173213 buffer[1 ] = static_cast <char >(static_cast <uint8_t >(value >> 8 ));
@@ -308,11 +348,12 @@ Fixed32Decode(const absl::Cord& data) {
308348}
309349
310350inline absl::optional<ProtoWireTag> DecodeProtoWireTag (uint32_t value) {
311- if (ABSL_PREDICT_FALSE ((value >> 3 ) == 0 )) {
351+ if (ABSL_PREDICT_FALSE ((value >> ProtoWireTag:: kFieldNumberShift ) == 0 )) {
312352 // Field number is 0.
313353 return absl::nullopt ;
314354 }
315- if (ABSL_PREDICT_FALSE ((value & uint32_t {0x7 }) == uint32_t {0x6 })) {
355+ if (ABSL_PREDICT_FALSE (!ProtoWireTypeIsValid (
356+ static_cast <ProtoWireType>(value & ProtoWireTag::kTypeMask )))) {
316357 // Wire type is 6, only 0-5 are used.
317358 return absl::nullopt ;
318359 }
@@ -409,6 +450,60 @@ class ProtoWireDecoder {
409450 absl::optional<ProtoWireTag> tag_;
410451};
411452
453+ class ProtoWireEncoder final {
454+ public:
455+ explicit ProtoWireEncoder (absl::string_view message
456+ ABSL_ATTRIBUTE_LIFETIME_BOUND ,
457+ absl::Cord& data ABSL_ATTRIBUTE_LIFETIME_BOUND )
458+ : message_(message), data_(data), original_data_size_(data_.size()) {}
459+
460+ bool empty () const { return size () == 0 ; }
461+
462+ size_t size () const { return data_.size () - original_data_size_; }
463+
464+ absl::Status WriteTag (ProtoWireTag tag);
465+
466+ template <typename T>
467+ std::enable_if_t <std::is_integral_v<T>, absl::Status> WriteVarint (T value) {
468+ ABSL_DCHECK (tag_.has_value () && tag_->type () == ProtoWireType::kVarint );
469+ VarintEncode (value, data_);
470+ tag_.reset ();
471+ return absl::OkStatus ();
472+ }
473+
474+ template <typename T>
475+ std::enable_if_t <sizeof (T) == 4 &&
476+ (std::is_integral_v<T> || std::is_floating_point_v<T>),
477+ absl::Status>
478+ WriteFixed32 (T value) {
479+ ABSL_DCHECK (tag_.has_value () && tag_->type () == ProtoWireType::kFixed32 );
480+ Fixed32Encode (value, data_);
481+ tag_.reset ();
482+ return absl::OkStatus ();
483+ }
484+
485+ template <typename T>
486+ std::enable_if_t <sizeof (T) == 8 &&
487+ (std::is_integral_v<T> || std::is_floating_point_v<T>),
488+ absl::Status>
489+ WriteFixed64 (T value) {
490+ ABSL_DCHECK (tag_.has_value () && tag_->type () == ProtoWireType::kFixed64 );
491+ Fixed64Encode (value, data_);
492+ tag_.reset ();
493+ return absl::OkStatus ();
494+ }
495+
496+ absl::Status WriteLengthDelimited (absl::Cord data);
497+
498+ void EnsureFullyEncoded () { ABSL_DCHECK (!tag_.has_value ()); }
499+
500+ private:
501+ absl::string_view message_;
502+ absl::Cord& data_;
503+ const size_t original_data_size_;
504+ absl::optional<ProtoWireTag> tag_;
505+ };
506+
412507} // namespace cel::internal
413508
414509#endif // THIRD_PARTY_CEL_CPP_INTERNAL_PROTO_WIRE_H_
0 commit comments