diff --git a/.changes/unreleased/breaking-changes-20260702-200419.yaml b/.changes/unreleased/breaking-changes-20260702-200419.yaml new file mode 100644 index 00000000..78936c28 --- /dev/null +++ b/.changes/unreleased/breaking-changes-20260702-200419.yaml @@ -0,0 +1,25 @@ +kind: Breaking changes +body: |- + The u64 size-arithmetic discipline changes three public signatures: + `buffa::types::put_len_delimited_header` takes `len: u64` (was `u32`), + and `buffa::map_codec::field_len` / `message_field_len` take and return + `u64` (`MapCodec::encoded_len` and `FIXED_LEN` likewise — that trait is + sealed, so only the signatures are visible). Bare integer literals still + infer; a `u32` variable widens with `u64::from(...)`. + **Checked-in generated code must be regenerated**: code emitted by + earlier `buffa-codegen` versions passes `__cache.consume_next()` (a + `u32`) to `put_len_delimited_header` and adds `field_len` results into a + `u32` accumulator, so it fails to compile against this runtime. Regenerate + with your build pipeline (or `buffa-build`) after updating. + `ExtensionCodec` and `extension::codecs::SingularCodec` swap their + required encode method: `try_encode` / `try_encode_one` (fallible) are + now required, and the panicking `encode` / `encode_one` are provided + wrappers — so a codec whose encode can fail cannot accidentally leave + the fallible `try_set_extension` path panicking. All in-tree codecs are + updated; an external codec impl (if any exist) renames its method and + wraps the result in `Ok`. + Runtime behavior also changes: the existing encode entry points now + **panic** on messages whose encoded size exceeds the 2 GiB protobuf + limit — see the Fixed entry for the full list and the `try_encode*` + escape hatch. +time: 2026-07-02T20:04:19.442227418Z diff --git a/.changes/unreleased/fixed-20260702-174601.yaml b/.changes/unreleased/fixed-20260702-174601.yaml new file mode 100644 index 00000000..d23a09d1 --- /dev/null +++ b/.changes/unreleased/fixed-20260702-174601.yaml @@ -0,0 +1,26 @@ +kind: Fixed +body: |- + **Encode now enforces the protobuf 2 GiB message-size limit.** Previously + encoding was infallible: a message whose encoded size crossed 2^31-1 bytes + serialized silently into a blob that no conforming decoder — including + buffa's own (`DecodeError::MessageTooLarge`) — would read back, and sizes + past 4 GiB wrapped `u32` arithmetic into corrupt output. Generated + `compute_size` now accumulates in `u64` and saturates at each node's return + (`buffa::saturate_size`), and every provided encode entry point on + `Message`, `ViewEncode`, generated lazy views, and `DynamicMessage` checks + the total against the new `buffa::MAX_MESSAGE_BYTES` constant. + **Behavior change:** the existing entry points (`encode`, `encode_to_vec`, + `encode_to_bytes`, `encode_length_delimited`, `encoded_len`, + `encode_with_cache`) now panic on over-limit messages (previously they + returned decoder-rejected or corrupt bytes); new `try_encode`, + `try_encode_with_cache`, `try_encoded_len`, `try_encode_length_delimited`, + `try_encode_to_vec`, and `try_encode_to_bytes` twins return + `Err(EncodeError::MessageTooLarge)` instead — `EncodeError` gains its first + variant. Fallible variants also cover the eager re-encode paths: + `ExtensionSet::try_set_extension` and `Any::try_pack` (message-typed + extension values and `Any::pack` encode their payload on the spot, so the + panicking originals document the new panic and these twins return the + error instead). In debug builds `SizeCache::consume_next` additionally + rejects over-limit slots as a backstop for callers driving + `compute_size`/`write_to` directly. +time: 2026-07-02T17:46:01.12802197Z diff --git a/SECURITY.md b/SECURITY.md index e0beaaf6..361db240 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,12 +6,12 @@ Thank you for helping us keep this action and the systems they interact with sec This repository is maintained by [Anthropic](https://www.anthropic.com/). -The security of our systems and user data is Anthropic’s top priority. We appreciate -the work of security researchers acting in good faith in identifying and reporting potential +The security of our systems and user data is Anthropic’s top priority. We appreciate +the work of security researchers acting in good faith in identifying and reporting potential vulnerabilities. -Our security program is managed on HackerOne and we ask that any validated vulnerability in -this functionality be reported through their +Our security program is managed on HackerOne and we ask that any validated vulnerability in +this functionality be reported through their [submission form](https://hackerone.com/4f1f16ba-10d3-4d09-9ecc-c721aad90f24/embedded_submissions/new). ## Anthropic Bug Bounty diff --git a/buffa-codegen/src/impl_message.rs b/buffa-codegen/src/impl_message.rs index acea629b..6a9c4d35 100644 --- a/buffa-codegen/src/impl_message.rs +++ b/buffa-codegen/src/impl_message.rs @@ -494,14 +494,14 @@ pub fn generate_message_impl( quote! { for f in self.__buffa_unknown_fields.iter() { if let ::buffa::UnknownFieldData::LengthDelimited(ref bytes) = f.data { - size += ::buffa::message_set::item_encoded_len(f.number, bytes.len()) as u32; + size += ::buffa::message_set::item_encoded_len(f.number, bytes.len()) as u64; } else { - size += f.encoded_len() as u32; + size += f.encoded_len() as u64; } } } } else if preserve_unknown_fields { - quote! { size += self.__buffa_unknown_fields.encoded_len() as u32; } + quote! { size += self.__buffa_unknown_fields.encoded_len() as u64; } } else { quote! {} }; @@ -571,9 +571,9 @@ pub fn generate_message_impl( // Suppress lint warnings that fire on generated code for empty messages. let has_body = !fields.is_empty() || preserve_unknown_fields; let size_decl = if has_body { - quote! { let mut size = 0u32; } + quote! { let mut size = 0u64; } } else { - quote! { let size = 0u32; } + quote! { let size = 0u64; } }; let buf_param = if has_body { quote! { buf: &mut impl ::buffa::bytes::BufMut } @@ -662,9 +662,11 @@ pub fn generate_message_impl( impl ::buffa::Message for #name_ident { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, #cache_ident: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] @@ -672,7 +674,7 @@ pub fn generate_message_impl( #size_decl #(#compute_stmts)* #unknown_fields_size_stmt - size + ::buffa::saturate_size(size) } fn write_to( @@ -737,8 +739,8 @@ fn lazy_fragment_encode_stmts( let compute = quote! { for __frag in self.#ident.#accessor() { size += #ld_tag_len - + ::buffa::encoding::varint_len(__frag.len() as u64) as u32 - + __frag.len() as u32; + + ::buffa::encoding::varint_len(__frag.len() as u64) as u64 + + __frag.len() as u64; } }; let write = quote! { @@ -864,7 +866,7 @@ pub(crate) fn build_view_encode_methods( } let unknown_fields_size_stmt = if preserve_unknown_fields { - quote! { size += self.__buffa_unknown_fields.encoded_len() as u32; } + quote! { size += self.__buffa_unknown_fields.encoded_len() as u64; } } else { quote! {} }; @@ -892,9 +894,9 @@ pub(crate) fn build_view_encode_methods( }; let has_body = !fields.is_empty() || preserve_unknown_fields; let size_decl = if has_body { - quote! { let mut size = 0u32; } + quote! { let mut size = 0u64; } } else { - quote! { let size = 0u32; } + quote! { let size = 0u64; } }; let buf_param = if has_body { quote! { buf: &mut impl ::buffa::bytes::BufMut } @@ -940,7 +942,7 @@ pub(crate) fn build_view_encode_methods( #size_decl #(#compute_stmts)* #unknown_fields_size_stmt - size + ::buffa::saturate_size(size) } #write_to_doc @@ -1224,19 +1226,19 @@ fn scalar_clear_stmt( /// string/bytes/enum are handled inline in the callers. fn type_encoded_size_expr(ty: Type, val: &TokenStream) -> TokenStream { match ty { - Type::TYPE_INT32 => quote! { ::buffa::types::int32_encoded_len(#val) as u32 }, - Type::TYPE_INT64 => quote! { ::buffa::types::int64_encoded_len(#val) as u32 }, - Type::TYPE_UINT32 => quote! { ::buffa::types::uint32_encoded_len(#val) as u32 }, - Type::TYPE_UINT64 => quote! { ::buffa::types::uint64_encoded_len(#val) as u32 }, - Type::TYPE_SINT32 => quote! { ::buffa::types::sint32_encoded_len(#val) as u32 }, - Type::TYPE_SINT64 => quote! { ::buffa::types::sint64_encoded_len(#val) as u32 }, + Type::TYPE_INT32 => quote! { ::buffa::types::int32_encoded_len(#val) as u64 }, + Type::TYPE_INT64 => quote! { ::buffa::types::int64_encoded_len(#val) as u64 }, + Type::TYPE_UINT32 => quote! { ::buffa::types::uint32_encoded_len(#val) as u64 }, + Type::TYPE_UINT64 => quote! { ::buffa::types::uint64_encoded_len(#val) as u64 }, + Type::TYPE_SINT32 => quote! { ::buffa::types::sint32_encoded_len(#val) as u64 }, + Type::TYPE_SINT64 => quote! { ::buffa::types::sint64_encoded_len(#val) as u64 }, Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => { - quote! { ::buffa::types::FIXED32_ENCODED_LEN as u32 } + quote! { ::buffa::types::FIXED32_ENCODED_LEN as u64 } } Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => { - quote! { ::buffa::types::FIXED64_ENCODED_LEN as u32 } + quote! { ::buffa::types::FIXED64_ENCODED_LEN as u64 } } - Type::TYPE_BOOL => quote! { ::buffa::types::BOOL_ENCODED_LEN as u32 }, + Type::TYPE_BOOL => quote! { ::buffa::types::BOOL_ENCODED_LEN as u64 }, Type::TYPE_STRING | Type::TYPE_BYTES | Type::TYPE_ENUM @@ -1486,11 +1488,18 @@ pub(crate) fn wire_type_check(tag_expr: &TokenStream, wire_type: &TokenStream) - /// /// A tag encodes `(field_number << 3) | wire_type_byte` as a varint. /// The result is always at least 1 byte since field numbers start at 1. -const fn tag_encoded_len(field_number: u32, wire_type: u8) -> u32 { +/// +/// Returns `u64` so interpolating the value into `quote!` emits a `u64` +/// literal: all generated size arithmetic — `compute_size` accumulators and +/// the transient packed-payload / map-entry lengths in `write_to` — is done +/// in `u64` so it cannot wrap for any in-memory message (the encoded size +/// of in-RAM data is far below `u64::MAX`). `compute_size` saturates its +/// total to `u32` once at return via `::buffa::saturate_size`. +const fn tag_encoded_len(field_number: u32, wire_type: u8) -> u64 { let tag_value = ((field_number as u64) << 3) | wire_type as u64; // tag_value >= 8 (field_number >= 1), so leading_zeros <= 60 and bits >= 4. let bits = 64 - tag_value.leading_zeros(); - bits.div_ceil(7) + bits.div_ceil(7) as u64 } fn scalar_compute_size_stmt( @@ -1517,17 +1526,17 @@ fn scalar_compute_size_stmt( return match ty { Type::TYPE_STRING => Ok(quote! { if let Some(ref v) = self.#ident { - size += #tag_len + ::buffa::types::string_encoded_len(v) as u32; + size += #tag_len + ::buffa::types::string_encoded_len(v) as u64; } }), Type::TYPE_BYTES => Ok(quote! { if let Some(ref v) = self.#ident { - size += #tag_len + ::buffa::types::bytes_encoded_len(v) as u32; + size += #tag_len + ::buffa::types::bytes_encoded_len(v) as u64; } }), Type::TYPE_ENUM => Ok(quote! { if let Some(ref v) = self.#ident { - size += #tag_len + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += #tag_len + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } }), _ => { @@ -1566,22 +1575,22 @@ fn scalar_compute_size_stmt( match ty { Type::TYPE_STRING => { return Ok(if is_proto2_required { - quote! { size += #tag_len + ::buffa::types::string_encoded_len(&self.#ident) as u32; } + quote! { size += #tag_len + ::buffa::types::string_encoded_len(&self.#ident) as u64; } } else { quote! { if !self.#ident.is_empty() { - size += #tag_len + ::buffa::types::string_encoded_len(&self.#ident) as u32; + size += #tag_len + ::buffa::types::string_encoded_len(&self.#ident) as u64; } } }); } Type::TYPE_BYTES => { return Ok(if is_proto2_required { - quote! { size += #tag_len + ::buffa::types::bytes_encoded_len(&self.#ident) as u32; } + quote! { size += #tag_len + ::buffa::types::bytes_encoded_len(&self.#ident) as u64; } } else { quote! { if !self.#ident.is_empty() { - size += #tag_len + ::buffa::types::bytes_encoded_len(&self.#ident) as u32; + size += #tag_len + ::buffa::types::bytes_encoded_len(&self.#ident) as u64; } } }); @@ -1591,7 +1600,7 @@ fn scalar_compute_size_stmt( quote! { { let val = self.#ident.to_i32(); - size += #tag_len + ::buffa::types::int32_encoded_len(val) as u32; + size += #tag_len + ::buffa::types::int32_encoded_len(val) as u64; } } } else { @@ -1599,7 +1608,7 @@ fn scalar_compute_size_stmt( { let val = self.#ident.to_i32(); if val != 0 { - size += #tag_len + ::buffa::types::int32_encoded_len(val) as u32; + size += #tag_len + ::buffa::types::int32_encoded_len(val) as u64; } } } @@ -1612,8 +1621,8 @@ fn scalar_compute_size_stmt( let inner_size = self.#ident.compute_size(__cache); __cache.set(__slot, inner_size); size += #tag_len - + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } }); } @@ -1622,7 +1631,7 @@ fn scalar_compute_size_stmt( return Ok(quote! { if self.#ident.is_set() { let inner_size = self.#ident.compute_size(__cache); - size += #tag_len + inner_size + #tag_len; + size += #tag_len + inner_size as u64 + #tag_len; } }); } @@ -1736,7 +1745,7 @@ fn scalar_write_to_stmt( if self.#ident.is_set() { ::buffa::types::put_len_delimited_header( #field_number, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.#ident.write_to(__cache, buf); @@ -2079,24 +2088,26 @@ fn repeated_for_iter(ident: &Ident, repr: &crate::RepeatedRepr) -> TokenStream { } /// Generate the payload-size expression for a packed repeated field. -/// The expression evaluates to a `u32` at runtime. +/// The expression evaluates to a `u64` at runtime — like all generated size +/// arithmetic, so a huge field (e.g. 700M `double`s) yields an exact +/// over-limit value for the entry-point size check instead of wrapping. fn repeated_payload_size_expr(ty: Type, ident: &Ident) -> TokenStream { match ty { Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => { - quote! { self.#ident.len() as u32 * ::buffa::types::FIXED32_ENCODED_LEN as u32 } + quote! { self.#ident.len() as u64 * ::buffa::types::FIXED32_ENCODED_LEN as u64 } } Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => { - quote! { self.#ident.len() as u32 * ::buffa::types::FIXED64_ENCODED_LEN as u32 } + quote! { self.#ident.len() as u64 * ::buffa::types::FIXED64_ENCODED_LEN as u64 } } Type::TYPE_BOOL => { - quote! { self.#ident.len() as u32 * ::buffa::types::BOOL_ENCODED_LEN as u32 } + quote! { self.#ident.len() as u64 * ::buffa::types::BOOL_ENCODED_LEN as u64 } } Type::TYPE_ENUM => { quote! { self.#ident .iter() - .map(|v| ::buffa::types::int32_encoded_len(v.to_i32()) as u32) - .sum::() + .map(|v| ::buffa::types::int32_encoded_len(v.to_i32()) as u64) + .sum::() } } _ => { @@ -2104,7 +2115,7 @@ fn repeated_payload_size_expr(ty: Type, ident: &Ident) -> TokenStream { // element size depends on the encoded value, so compute per-element via map. let v = quote! { v }; let size_expr = type_encoded_size_expr(ty, &v); - quote! { self.#ident.iter().map(|&v| #size_expr).sum::() } + quote! { self.#ident.iter().map(|&v| #size_expr).sum::() } } } } @@ -2136,8 +2147,8 @@ fn repeated_compute_size_stmt( let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size += #ld_tag_len - + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } }); } @@ -2146,7 +2157,7 @@ fn repeated_compute_size_stmt( return Ok(quote! { for v in #elems { let inner_size = v.compute_size(__cache); - size += #elem_tag_len + inner_size + #elem_tag_len; + size += #elem_tag_len + inner_size as u64 + #elem_tag_len; } }); } @@ -2159,33 +2170,33 @@ fn repeated_compute_size_stmt( match ty { Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => { return Ok(quote! { - size += self.#ident.len() as u32 - * (#elem_tag_len + ::buffa::types::FIXED32_ENCODED_LEN as u32); + size += self.#ident.len() as u64 + * (#elem_tag_len + ::buffa::types::FIXED32_ENCODED_LEN as u64); }); } Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => { return Ok(quote! { - size += self.#ident.len() as u32 - * (#elem_tag_len + ::buffa::types::FIXED64_ENCODED_LEN as u32); + size += self.#ident.len() as u64 + * (#elem_tag_len + ::buffa::types::FIXED64_ENCODED_LEN as u64); }); } Type::TYPE_BOOL => { return Ok(quote! { - size += self.#ident.len() as u32 - * (#elem_tag_len + ::buffa::types::BOOL_ENCODED_LEN as u32); + size += self.#ident.len() as u64 + * (#elem_tag_len + ::buffa::types::BOOL_ENCODED_LEN as u64); }); } _ => {} } let per_elem_size = match ty { Type::TYPE_STRING => { - quote! { size += #ld_tag_len + ::buffa::types::string_encoded_len(v) as u32; } + quote! { size += #ld_tag_len + ::buffa::types::string_encoded_len(v) as u64; } } Type::TYPE_BYTES => { - quote! { size += #ld_tag_len + ::buffa::types::bytes_encoded_len(v) as u32; } + quote! { size += #ld_tag_len + ::buffa::types::bytes_encoded_len(v) as u64; } } Type::TYPE_ENUM => { - quote! { size += #elem_tag_len + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; } + quote! { size += #elem_tag_len + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } } _ => { let deref_v = quote! { *v }; @@ -2201,8 +2212,8 @@ fn repeated_compute_size_stmt( let payload_expr = repeated_payload_size_expr(ty, &ident); Ok(quote! { if !self.#ident.is_empty() { - let payload: u32 = #payload_expr; - size += #ld_tag_len + ::buffa::encoding::varint_len(payload as u64) as u32 + payload; + let payload: u64 = #payload_expr; + size += #ld_tag_len + ::buffa::encoding::varint_len(payload) as u64 + payload; } }) } @@ -2227,7 +2238,7 @@ fn repeated_write_to_stmt( for v in #elems { ::buffa::types::put_len_delimited_header( #field_number, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -2276,7 +2287,7 @@ fn repeated_write_to_stmt( }; Ok(quote! { if !self.#ident.is_empty() { - let payload: u32 = #payload_expr; + let payload: u64 = #payload_expr; ::buffa::types::put_len_delimited_header(#field_number, payload, buf); #encode_loop } @@ -2492,23 +2503,23 @@ fn repeated_merge_arm( fn oneof_size_arm( enum_ident: &TokenStream, variant_ident: &Ident, - tag_len: u32, + tag_len: u64, ty: Type, ) -> TokenStream { match ty { Type::TYPE_STRING => quote! { #enum_ident::#variant_ident(x) => { - size += #tag_len + ::buffa::types::string_encoded_len(x) as u32; + size += #tag_len + ::buffa::types::string_encoded_len(x) as u64; } }, Type::TYPE_BYTES => quote! { #enum_ident::#variant_ident(x) => { - size += #tag_len + ::buffa::types::bytes_encoded_len(x) as u32; + size += #tag_len + ::buffa::types::bytes_encoded_len(x) as u64; } }, Type::TYPE_ENUM => quote! { #enum_ident::#variant_ident(x) => { - size += #tag_len + ::buffa::types::int32_encoded_len(x.to_i32()) as u32; + size += #tag_len + ::buffa::types::int32_encoded_len(x.to_i32()) as u64; } }, Type::TYPE_MESSAGE => quote! { @@ -2517,29 +2528,29 @@ fn oneof_size_arm( let inner = x.compute_size(__cache); __cache.set(__slot, inner); size += #tag_len - + ::buffa::encoding::varint_len(inner as u64) as u32 - + inner; + + ::buffa::encoding::varint_len(inner as u64) as u64 + + inner as u64; } }, Type::TYPE_GROUP => quote! { #enum_ident::#variant_ident(x) => { let inner = x.compute_size(__cache); - size += #tag_len + inner + #tag_len; + size += #tag_len + inner as u64 + #tag_len; } }, Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => quote! { #enum_ident::#variant_ident(_x) => { - size += #tag_len + ::buffa::types::FIXED32_ENCODED_LEN as u32; + size += #tag_len + ::buffa::types::FIXED32_ENCODED_LEN as u64; } }, Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => quote! { #enum_ident::#variant_ident(_x) => { - size += #tag_len + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += #tag_len + ::buffa::types::FIXED64_ENCODED_LEN as u64; } }, Type::TYPE_BOOL => quote! { #enum_ident::#variant_ident(_x) => { - size += #tag_len + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += #tag_len + ::buffa::types::BOOL_ENCODED_LEN as u64; } }, _ => { @@ -2586,7 +2597,7 @@ fn oneof_write_arm( #enum_ident::#variant_ident(x) => { ::buffa::types::put_len_delimited_header( #field_number, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); x.write_to(__cache, buf); @@ -2948,7 +2959,7 @@ fn map_codec_token( struct MapEntryCtx { field_number: u32, ident: Ident, - outer_tag_len: u32, + outer_tag_len: u64, val_ty: Type, val_is_closed_enum: bool, key_codec: TokenStream, @@ -3016,9 +3027,9 @@ fn map_entry_ctx( /// scalars, `var` for string/bytes/enum, and `var.compute_size()` for messages. fn map_element_size_expr(ty: Type, var: &Ident) -> TokenStream { match ty { - Type::TYPE_STRING => quote! { ::buffa::types::string_encoded_len(#var) as u32 }, - Type::TYPE_BYTES => quote! { ::buffa::types::bytes_encoded_len(#var) as u32 }, - Type::TYPE_ENUM => quote! { ::buffa::types::int32_encoded_len(#var.to_i32()) as u32 }, + Type::TYPE_STRING => quote! { ::buffa::types::string_encoded_len(#var) as u64 }, + Type::TYPE_BYTES => quote! { ::buffa::types::bytes_encoded_len(#var) as u64 }, + Type::TYPE_ENUM => quote! { ::buffa::types::int32_encoded_len(#var.to_i32()) as u64 }, // Message values are phase-dependent (compute reserves a SizeCache // slot, write reads it) so callers handle them explicitly. Keys // cannot be message-typed per the proto spec. @@ -3026,12 +3037,12 @@ fn map_element_size_expr(ty: Type, var: &Ident) -> TokenStream { unreachable!("message map values are handled per-phase by callers") } Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => { - quote! { ::buffa::types::FIXED32_ENCODED_LEN as u32 } + quote! { ::buffa::types::FIXED32_ENCODED_LEN as u64 } } Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => { - quote! { ::buffa::types::FIXED64_ENCODED_LEN as u32 } + quote! { ::buffa::types::FIXED64_ENCODED_LEN as u64 } } - Type::TYPE_BOOL => quote! { ::buffa::types::BOOL_ENCODED_LEN as u32 }, + Type::TYPE_BOOL => quote! { ::buffa::types::BOOL_ENCODED_LEN as u64 }, _ => { let deref_var = quote! { *#var }; let size_expr = type_encoded_size_expr(ty, &deref_var); @@ -3103,7 +3114,7 @@ fn map_view_compute_size_stmt( let __slot = __cache.reserve(); let inner = #v.compute_size(__cache); __cache.set(__slot, inner); - ::buffa::encoding::varint_len(inner as u64) as u32 + inner + ::buffa::encoding::varint_len(inner as u64) as u64 + inner as u64 } } } else { @@ -3116,9 +3127,9 @@ fn map_view_compute_size_stmt( if map_element_size_is_constant(key_ty) && map_element_size_is_constant(val_ty) { return Ok(quote! { { - let entry_size: u32 = #key_tag_len + #key_size + #val_tag_len + #val_size; - size += self.#ident.len() as u32 * (#outer_tag_len - + ::buffa::encoding::varint_len(entry_size as u64) as u32 + let entry_size: u64 = #key_tag_len + #key_size + #val_tag_len + #val_size; + size += self.#ident.len() as u64 * (#outer_tag_len + + ::buffa::encoding::varint_len(entry_size) as u64 + entry_size); } }); @@ -3136,9 +3147,9 @@ fn map_view_compute_size_stmt( Ok(quote! { #[allow(clippy::for_kv_map)] for (#k_bind, #v_bind) in &self.#ident { - let entry_size: u32 = #key_tag_len + #key_size + #val_tag_len + #val_size; + let entry_size: u64 = #key_tag_len + #key_size + #val_tag_len + #val_size; size += #outer_tag_len - + ::buffa::encoding::varint_len(entry_size as u64) as u32 + + ::buffa::encoding::varint_len(entry_size) as u64 + entry_size; } }) @@ -3167,7 +3178,7 @@ fn map_view_write_to_stmt( let (val_len_bind, val_size) = if val_ty == Type::TYPE_MESSAGE { ( quote! { let __v_len = __cache.consume_next(); }, - quote! { (::buffa::encoding::varint_len(__v_len as u64) as u32 + __v_len) }, + quote! { (::buffa::encoding::varint_len(__v_len as u64) as u64 + __v_len as u64) }, ) } else { (quote! {}, map_element_size_expr(val_ty, &v)) @@ -3177,12 +3188,12 @@ fn map_view_write_to_stmt( Ok(quote! { for (#k, #v) in &self.#ident { #val_len_bind - let entry_size: u32 = #key_tag_len + #key_size + #val_tag_len + #val_size; + let entry_size: u64 = #key_tag_len + #key_size + #val_tag_len + #val_size; ::buffa::encoding::Tag::new( #field_number, ::buffa::encoding::WireType::LengthDelimited, ).encode(buf); - ::buffa::encoding::encode_varint(entry_size as u64, buf); + ::buffa::encoding::encode_varint(entry_size, buf); #encode_key #encode_val } diff --git a/buffa-codegen/src/lazy_view.rs b/buffa-codegen/src/lazy_view.rs index c9a4edde..49426f3f 100644 --- a/buffa-codegen/src/lazy_view.rs +++ b/buffa-codegen/src/lazy_view.rs @@ -381,37 +381,158 @@ pub(crate) fn generate_lazy_view_with_nesting( #view_encode_methods /// Compute size, then write. Primary encode entry point. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]) — see + /// [`try_encode`](Self::try_encode) for the error-returning + /// variant. + #[inline] pub fn encode(&self, buf: &mut impl ::buffa::bytes::BufMut) { + self.try_encode(buf) + .unwrap_or_else(|_| ::buffa::encode_size_overflow()) + } + + /// Encode, returning an error instead of panicking if the + /// encoded size exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`::buffa::EncodeError::MessageTooLarge`] if the + /// encoded size exceeds the limit. + pub fn try_encode( + &self, + buf: &mut impl ::buffa::bytes::BufMut, + ) -> ::core::result::Result<(), ::buffa::EncodeError> { let mut __cache = ::buffa::SizeCache::new(); - self.compute_size(&mut __cache); + ::buffa::checked_encode_size(self.compute_size(&mut __cache))?; self.write_to(&mut __cache, buf); + ::core::result::Result::Ok(()) } /// Encoded byte size of this view. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]) — see + /// [`try_encoded_len`](Self::try_encoded_len) for the + /// error-returning variant. + #[inline] #[must_use] pub fn encoded_len(&self) -> u32 { - self.compute_size(&mut ::buffa::SizeCache::new()) + self.try_encoded_len() + .unwrap_or_else(|_| ::buffa::encode_size_overflow()) + } + + /// Encoded byte size, returning an error instead of panicking + /// if it exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`::buffa::EncodeError::MessageTooLarge`] if the + /// encoded size exceeds the limit. + pub fn try_encoded_len( + &self, + ) -> ::core::result::Result { + ::buffa::checked_encode_size( + self.compute_size(&mut ::buffa::SizeCache::new()), + ) } /// Encode this view to a new `Vec`. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]) — see + /// [`try_encode_to_vec`](Self::try_encode_to_vec) for the + /// error-returning variant. + #[inline] #[must_use] pub fn encode_to_vec(&self) -> ::buffa::alloc::vec::Vec { let mut __cache = ::buffa::SizeCache::new(); - let __size = self.compute_size(&mut __cache) as usize; + let __size = match ::buffa::checked_encode_size( + self.compute_size(&mut __cache), + ) { + ::core::result::Result::Ok(__size) => __size as usize, + ::core::result::Result::Err(_) => ::buffa::encode_size_overflow(), + }; let mut __buf = ::buffa::alloc::vec::Vec::with_capacity(__size); self.write_to(&mut __cache, &mut __buf); + ::buffa::debug_assert_two_pass(__buf.len(), __size); __buf } + /// Encode to a new `Vec`, returning an error instead of + /// panicking if the encoded size exceeds the 2 GiB protobuf + /// limit ([`::buffa::MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`::buffa::EncodeError::MessageTooLarge`] if the + /// encoded size exceeds the limit. + pub fn try_encode_to_vec( + &self, + ) -> ::core::result::Result<::buffa::alloc::vec::Vec, ::buffa::EncodeError> + { + let mut __cache = ::buffa::SizeCache::new(); + let __size = + ::buffa::checked_encode_size(self.compute_size(&mut __cache))? as usize; + let mut __buf = ::buffa::alloc::vec::Vec::with_capacity(__size); + self.write_to(&mut __cache, &mut __buf); + ::buffa::debug_assert_two_pass(__buf.len(), __size); + ::core::result::Result::Ok(__buf) + } + /// Encode this view to a new [`::buffa::bytes::Bytes`]. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`::buffa::MAX_MESSAGE_BYTES`]) — see + /// [`try_encode_to_bytes`](Self::try_encode_to_bytes) for the + /// error-returning variant. + #[inline] #[must_use] pub fn encode_to_bytes(&self) -> ::buffa::bytes::Bytes { let mut __cache = ::buffa::SizeCache::new(); - let __size = self.compute_size(&mut __cache) as usize; + let __size = match ::buffa::checked_encode_size( + self.compute_size(&mut __cache), + ) { + ::core::result::Result::Ok(__size) => __size as usize, + ::core::result::Result::Err(_) => ::buffa::encode_size_overflow(), + }; let mut __buf = ::buffa::bytes::BytesMut::with_capacity(__size); self.write_to(&mut __cache, &mut __buf); + ::buffa::debug_assert_two_pass(__buf.len(), __size); __buf.freeze() } + + /// Encode to a new [`::buffa::bytes::Bytes`], returning an + /// error instead of panicking if the encoded size exceeds the + /// 2 GiB protobuf limit ([`::buffa::MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`::buffa::EncodeError::MessageTooLarge`] if the + /// encoded size exceeds the limit. + pub fn try_encode_to_bytes( + &self, + ) -> ::core::result::Result<::buffa::bytes::Bytes, ::buffa::EncodeError> { + let mut __cache = ::buffa::SizeCache::new(); + let __size = + ::buffa::checked_encode_size(self.compute_size(&mut __cache))? as usize; + let mut __buf = ::buffa::bytes::BytesMut::with_capacity(__size); + self.write_to(&mut __cache, &mut __buf); + ::buffa::debug_assert_two_pass(__buf.len(), __size); + ::core::result::Result::Ok(__buf.freeze()) + } } #serialize_impl diff --git a/buffa-codegen/src/owned_view.rs b/buffa-codegen/src/owned_view.rs index 8be495c1..bc1b2f17 100644 --- a/buffa-codegen/src/owned_view.rs +++ b/buffa-codegen/src/owned_view.rs @@ -267,7 +267,9 @@ pub(crate) fn generate_owned_view_wrapper( /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &#owned_path, diff --git a/buffa-codegen/src/tests/mod.rs b/buffa-codegen/src/tests/mod.rs index fe3136cc..941f84c2 100644 --- a/buffa-codegen/src/tests/mod.rs +++ b/buffa-codegen/src/tests/mod.rs @@ -66,4 +66,5 @@ mod proto2; mod reexports; mod reflect_view; mod repeated_type; +mod size_arithmetic; mod view_codegen; diff --git a/buffa-codegen/src/tests/size_arithmetic.rs b/buffa-codegen/src/tests/size_arithmetic.rs new file mode 100644 index 00000000..e8259464 --- /dev/null +++ b/buffa-codegen/src/tests/size_arithmetic.rs @@ -0,0 +1,251 @@ +//! Locks the u64 size-arithmetic discipline in generated encode code. +//! +//! Generated `compute_size` bodies must accumulate in `u64` and saturate to +//! `u32` once at return (`::buffa::saturate_size`), and the transient +//! lengths recomputed in `write_to` (packed payloads, map entries) must be +//! `u64` as well. Any plain `u32` arithmetic in a size path can wrap for +//! over-limit messages — three 1.5 GiB sub-trees wrap `u32` to an +//! *under-limit* value that the encode entry points' 2 GiB check cannot +//! distinguish from a legitimate size, silently producing corrupt output. +//! These tests fail if a future codegen change reintroduces `u32` size +//! arithmetic anywhere in the emitted encode code. + +use super::*; + +/// A proto3 file exercising every size-emitting codegen shape: scalar +/// string/bytes/enum/varint, explicit presence, message fields, repeated +/// (message / packed fixed / packed varint / unpacked fixed / string), +/// maps (message-valued, const-folded fixed/fixed), and a oneof with +/// message/string/fixed arms. Views are generated too, so the view +/// builder's output is covered by the same assertions. +fn size_corpus_file() -> FileDescriptorProto { + let mut file = proto3_file("size_corpus.proto"); + file.package = Some("corpus".to_string()); + + file.enum_type.push(EnumDescriptorProto { + name: Some("Color".to_string()), + value: vec![enum_value("COLOR_UNSPECIFIED", 0), enum_value("RED", 1)], + ..Default::default() + }); + + file.message_type.push(DescriptorProto { + name: Some("Inner".to_string()), + field: vec![make_field("n", 1, Label::LABEL_OPTIONAL, Type::TYPE_INT32)], + ..Default::default() + }); + + let msg_field = |name: &str, number: i32, label: Label| FieldDescriptorProto { + type_name: Some(".corpus.Inner".to_string()), + ..make_field(name, number, label, Type::TYPE_MESSAGE) + }; + let unpacked = |f: FieldDescriptorProto| FieldDescriptorProto { + options: Some(crate::generated::descriptor::FieldOptions { + packed: Some(false), + ..Default::default() + }) + .into(), + ..f + }; + let oneof_member = |f: FieldDescriptorProto| FieldDescriptorProto { + oneof_index: Some(0), + ..f + }; + + // map and map synthetic entry messages. + let map_entry = + |name: &str, key: FieldDescriptorProto, val: FieldDescriptorProto| DescriptorProto { + name: Some(name.to_string()), + field: vec![key, val], + options: Some(MessageOptions { + map_entry: Some(true), + ..Default::default() + }) + .into(), + ..Default::default() + }; + + file.message_type.push(DescriptorProto { + name: Some("Outer".to_string()), + field: vec![ + make_field("name", 1, Label::LABEL_OPTIONAL, Type::TYPE_STRING), + make_field("data", 2, Label::LABEL_OPTIONAL, Type::TYPE_BYTES), + make_field("count", 3, Label::LABEL_OPTIONAL, Type::TYPE_INT64), + FieldDescriptorProto { + type_name: Some(".corpus.Color".to_string()), + ..make_field("color", 4, Label::LABEL_OPTIONAL, Type::TYPE_ENUM) + }, + FieldDescriptorProto { + proto3_optional: Some(true), + oneof_index: Some(1), + ..make_field("maybe", 5, Label::LABEL_OPTIONAL, Type::TYPE_UINT32) + }, + msg_field("child", 6, Label::LABEL_OPTIONAL), + msg_field("children", 7, Label::LABEL_REPEATED), + make_field("weights", 8, Label::LABEL_REPEATED, Type::TYPE_DOUBLE), + make_field("ids", 9, Label::LABEL_REPEATED, Type::TYPE_SINT64), + unpacked(make_field( + "loose", + 10, + Label::LABEL_REPEATED, + Type::TYPE_FIXED32, + )), + make_field("tags", 11, Label::LABEL_REPEATED, Type::TYPE_STRING), + FieldDescriptorProto { + type_name: Some(".corpus.Outer.ByNameEntry".to_string()), + ..make_field("by_name", 12, Label::LABEL_REPEATED, Type::TYPE_MESSAGE) + }, + FieldDescriptorProto { + type_name: Some(".corpus.Outer.FlagsEntry".to_string()), + ..make_field("flags", 13, Label::LABEL_REPEATED, Type::TYPE_MESSAGE) + }, + oneof_member(make_field( + "as_text", + 14, + Label::LABEL_OPTIONAL, + Type::TYPE_STRING, + )), + oneof_member(msg_field("as_msg", 15, Label::LABEL_OPTIONAL)), + oneof_member(make_field( + "as_num", + 16, + Label::LABEL_OPTIONAL, + Type::TYPE_DOUBLE, + )), + ], + nested_type: vec![ + map_entry( + "ByNameEntry", + make_field("key", 1, Label::LABEL_OPTIONAL, Type::TYPE_STRING), + FieldDescriptorProto { + type_name: Some(".corpus.Inner".to_string()), + ..make_field("value", 2, Label::LABEL_OPTIONAL, Type::TYPE_MESSAGE) + }, + ), + map_entry( + "FlagsEntry", + make_field("key", 1, Label::LABEL_OPTIONAL, Type::TYPE_INT32), + make_field("value", 2, Label::LABEL_OPTIONAL, Type::TYPE_FIXED64), + ), + ], + oneof_decl: vec![ + OneofDescriptorProto { + name: Some("payload".to_string()), + ..Default::default() + }, + // Synthetic oneof for the proto3-optional `maybe` field. + OneofDescriptorProto { + name: Some("_maybe".to_string()), + ..Default::default() + }, + ], + ..Default::default() + }); + + file +} + +fn corpus_output() -> &'static str { + static OUTPUT: std::sync::OnceLock = std::sync::OnceLock::new(); + OUTPUT.get_or_init(|| { + let files = generate( + &[size_corpus_file()], + &["size_corpus.proto".to_string()], + &CodeGenConfig::default(), + ) + .expect("corpus should generate"); + joined(&files) + }) +} + +/// Extract the bodies of every `fn` whose name starts with `prefix` +/// (matching `fn compute_size` and `fn write_to` in owned, view, and lazy +/// impls) by brace counting from the signature. +fn fn_bodies<'a>(content: &'a str, prefix: &str) -> Vec<&'a str> { + let needle = format!("fn {prefix}"); + let mut bodies = Vec::new(); + let mut from = 0; + while let Some(pos) = content[from..].find(&needle) { + let start = from + pos; + let open = start + content[start..].find('{').expect("fn body opens"); + let mut depth = 0usize; + let mut end = open; + for (i, ch) in content[open..].char_indices() { + match ch { + '{' => depth += 1, + '}' => { + depth -= 1; + if depth == 0 { + end = open + i + 1; + break; + } + } + _ => {} + } + } + bodies.push(&content[start..end]); + from = end; + } + bodies +} + +#[test] +fn compute_size_accumulates_in_u64_and_saturates() { + let content = corpus_output(); + assert!( + content.contains("let mut size = 0u64;"), + "compute_size must accumulate in u64: {content}" + ); + assert!( + content.contains("::buffa::saturate_size(size)"), + "compute_size must saturate its u64 total at return: {content}" + ); + assert!( + !content.contains("let mut size = 0u32;"), + "u32 size accumulator reintroduced: {content}" + ); +} + +#[test] +fn write_to_transient_lengths_are_u64() { + let content = corpus_output(); + assert!( + content.contains("let payload: u64 ="), + "packed payload length must be u64: {content}" + ); + assert!( + content.contains("let entry_size: u64 ="), + "map entry length must be u64: {content}" + ); +} + +#[test] +fn no_u32_arithmetic_in_size_paths() { + // Scoped lock: every `compute_size` and `write_to` body in the corpus + // is free of u32 casts and u32 sums. Size helpers return usize and are + // widened `as u64`; the only u32 values in encode paths are + // `compute_size` return values, widened at their use sites. Any u32 + // arithmetic inside these bodies is a wrap bug (see module docs); + // u32 uses elsewhere in generated code (decode paths, accessors) are + // out of scope by construction. + let content = corpus_output(); + let bodies: Vec<&str> = fn_bodies(content, "compute_size") + .into_iter() + .chain(fn_bodies(content, "write_to")) + .collect(); + assert!( + bodies.len() >= 6, + "corpus should produce compute_size/write_to bodies for owned and \ + view impls, got {}", + bodies.len() + ); + for body in bodies { + assert!( + !body.contains("as u32"), + "u32 cast reintroduced in a size path: {body}" + ); + assert!( + !body.contains("sum::()"), + "u32 sum reintroduced in a size path: {body}" + ); + } +} diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs index dc04a62c..9860807f 100644 --- a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs @@ -108,21 +108,21 @@ impl<'a> ::buffa::ViewEncode<'a> for VersionView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.major { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.minor { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.patch { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.suffix { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -230,7 +230,9 @@ impl VersionOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Version, @@ -530,39 +532,39 @@ impl<'a> ::buffa::ViewEncode<'a> for CodeGeneratorRequestView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.file_to_generate { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.parameter { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.compiler_version.is_set() { let __slot = __cache.reserve(); let inner_size = self.compiler_version.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.proto_file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.source_file_descriptors { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -579,15 +581,27 @@ impl<'a> ::buffa::ViewEncode<'a> for CodeGeneratorRequestView<'a> { ::buffa::types::put_string_field(2u32, v, buf); } if self.compiler_version.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.compiler_version.write_to(__cache, buf); } for v in &self.proto_file { - ::buffa::types::put_len_delimited_header(15u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 15u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.source_file_descriptors { - ::buffa::types::put_len_delimited_header(17u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 17u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -692,7 +706,9 @@ impl CodeGeneratorRequestOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::CodeGeneratorRequest, @@ -985,29 +1001,29 @@ impl<'a> ::buffa::ViewEncode<'a> for CodeGeneratorResponseView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.error { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.supported_features { - size += 1u32 + ::buffa::types::uint64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(v) as u64; } if let Some(v) = self.minimum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.maximum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } for v in &self.file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1030,7 +1046,11 @@ impl<'a> ::buffa::ViewEncode<'a> for CodeGeneratorResponseView<'a> { ::buffa::types::put_int32_field(4u32, v, buf); } for v in &self.file { - ::buffa::types::put_len_delimited_header(15u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 15u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -1140,7 +1160,9 @@ impl CodeGeneratorResponseOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::CodeGeneratorResponse, @@ -1460,26 +1482,26 @@ pub mod code_generator_response { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.insertion_point { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.content { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.generated_code_info.is_set() { let __slot = __cache.reserve(); let inner_size = self.generated_code_info.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1501,7 +1523,7 @@ pub mod code_generator_response { if self.generated_code_info.is_set() { ::buffa::types::put_len_delimited_header( 16u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.generated_code_info.write_to(__cache, buf); @@ -1597,7 +1619,9 @@ pub mod code_generator_response { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::code_generator_response::File, diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs index 33339f24..eb96c6e7 100644 --- a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs @@ -110,28 +110,30 @@ impl ::buffa::MessageName for Version { impl ::buffa::Message for Version { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.major { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.minor { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.patch { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.suffix { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -438,46 +440,48 @@ impl ::buffa::MessageName for CodeGeneratorRequest { impl ::buffa::Message for CodeGeneratorRequest { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.file_to_generate { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.parameter { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.compiler_version.is_set() { let __slot = __cache.reserve(); let inner_size = self.compiler_version.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.proto_file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.source_file_descriptors { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -493,15 +497,27 @@ impl ::buffa::Message for CodeGeneratorRequest { ::buffa::types::put_string_field(2u32, v, buf); } if self.compiler_version.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.compiler_version.write_to(__cache, buf); } for v in &self.proto_file { - ::buffa::types::put_len_delimited_header(15u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 15u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.source_file_descriptors { - ::buffa::types::put_len_delimited_header(17u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 17u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -840,36 +856,38 @@ impl ::buffa::MessageName for CodeGeneratorResponse { impl ::buffa::Message for CodeGeneratorResponse { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.error { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.supported_features { - size += 1u32 + ::buffa::types::uint64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(v) as u64; } if let Some(v) = self.minimum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.maximum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } for v in &self.file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -891,7 +909,11 @@ impl ::buffa::Message for CodeGeneratorResponse { ::buffa::types::put_int32_field(4u32, v, buf); } for v in &self.file { - ::buffa::types::put_len_delimited_header(15u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 15u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -1404,33 +1426,35 @@ pub mod code_generator_response { impl ::buffa::Message for File { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.insertion_point { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.content { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.generated_code_info.is_set() { let __slot = __cache.reserve(); let inner_size = self.generated_code_info.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1451,7 +1475,7 @@ pub mod code_generator_response { if self.generated_code_info.is_set() { ::buffa::types::put_len_delimited_header( 16u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.generated_code_info.write_to(__cache, buf); diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs index 1d9fde9e..a71ebe4d 100644 --- a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs @@ -91,17 +91,17 @@ impl<'a> ::buffa::ViewEncode<'a> for FileDescriptorSetView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -112,7 +112,11 @@ impl<'a> ::buffa::ViewEncode<'a> for FileDescriptorSetView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.file { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -198,7 +202,9 @@ impl FileDescriptorSetOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FileDescriptorSet, @@ -668,81 +674,81 @@ impl<'a> ::buffa::ViewEncode<'a> for FileDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.dependency { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.message_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.enum_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.service { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.source_code_info.is_set() { let __slot = __cache.reserve(); let inner_size = self.source_code_info.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.public_dependency { - size += 1u32 + ::buffa::types::int32_encoded_len(*v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(*v) as u64; } for v in &self.weak_dependency { - size += 1u32 + ::buffa::types::int32_encoded_len(*v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(*v) as u64; } if let Some(ref v) = self.syntax { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.option_dependency { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -762,27 +768,51 @@ impl<'a> ::buffa::ViewEncode<'a> for FileDescriptorProtoView<'a> { ::buffa::types::put_string_field(3u32, v, buf); } for v in &self.message_type { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.enum_type { - ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 5u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.service { - ::buffa::types::put_len_delimited_header(6u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 6u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if self.source_code_info.is_set() { - ::buffa::types::put_len_delimited_header(9u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 9u32, + u64::from(__cache.consume_next()), + buf, + ); self.source_code_info.write_to(__cache, buf); } for v in &self.public_dependency { @@ -939,7 +969,9 @@ impl FileDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FileDescriptorProto, @@ -1450,82 +1482,82 @@ impl<'a> ::buffa::ViewEncode<'a> for DescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.field { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.nested_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.enum_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.oneof_decl { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1539,35 +1571,67 @@ impl<'a> ::buffa::ViewEncode<'a> for DescriptorProtoView<'a> { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.field { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.nested_type { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.enum_type { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension_range { - ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 5u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension { - ::buffa::types::put_len_delimited_header(6u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 6u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } for v in &self.oneof_decl { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_range { - ::buffa::types::put_len_delimited_header(9u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 9u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_name { @@ -1693,7 +1757,9 @@ impl DescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::DescriptorProto, @@ -1995,23 +2061,23 @@ pub mod descriptor_proto { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -2030,7 +2096,7 @@ pub mod descriptor_proto { if self.options.is_set() { ::buffa::types::put_len_delimited_header( 3u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.options.write_to(__cache, buf); @@ -2124,7 +2190,9 @@ pub mod descriptor_proto { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::descriptor_proto::ExtensionRange, @@ -2315,15 +2383,15 @@ pub mod descriptor_proto { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -2423,7 +2491,9 @@ pub mod descriptor_proto { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::descriptor_proto::ReservedRange, @@ -2694,36 +2764,36 @@ impl<'a> ::buffa::ViewEncode<'a> for ExtensionRangeOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.declaration { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.verification { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -2734,20 +2804,28 @@ impl<'a> ::buffa::ViewEncode<'a> for ExtensionRangeOptionsView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.declaration { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.verification { ::buffa::types::put_int32_field(3u32, v.to_i32(), buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(50u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 50u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -2850,7 +2928,9 @@ impl ExtensionRangeOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::ExtensionRangeOptions, @@ -3105,24 +3185,24 @@ pub mod extension_range_options { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.full_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.r#type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.reserved.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.repeated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -3241,7 +3321,9 @@ pub mod extension_range_options { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::extension_range_options::Declaration, @@ -3622,47 +3704,47 @@ impl<'a> ::buffa::ViewEncode<'a> for FieldDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.extendee { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.label { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.r#type { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.type_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.default_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(v) = self.oneof_index { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.json_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.proto3_optional.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -3694,7 +3776,11 @@ impl<'a> ::buffa::ViewEncode<'a> for FieldDescriptorProtoView<'a> { ::buffa::types::put_string_field(7u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if let Some(v) = self.oneof_index { @@ -3823,7 +3909,9 @@ impl FieldDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FieldDescriptorProto, @@ -4112,20 +4200,20 @@ impl<'a> ::buffa::ViewEncode<'a> for OneofDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -4139,7 +4227,11 @@ impl<'a> ::buffa::ViewEncode<'a> for OneofDescriptorProtoView<'a> { ::buffa::types::put_string_field(1u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -4230,7 +4322,9 @@ impl OneofDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::OneofDescriptorProto, @@ -4511,42 +4605,42 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.value { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -4560,15 +4654,27 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumDescriptorProtoView<'a> { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.value { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } for v in &self.reserved_range { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_name { @@ -4681,7 +4787,9 @@ impl EnumDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::EnumDescriptorProto, @@ -4903,15 +5011,15 @@ pub mod enum_descriptor_proto { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -5013,7 +5121,9 @@ pub mod enum_descriptor_proto { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::enum_descriptor_proto::EnumReservedRange, @@ -5222,23 +5332,23 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumValueDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -5255,7 +5365,11 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumValueDescriptorProtoView<'a> { ::buffa::types::put_int32_field(2u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -5349,7 +5463,9 @@ impl EnumValueDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::EnumValueDescriptorProto, @@ -5574,28 +5690,28 @@ impl<'a> ::buffa::ViewEncode<'a> for ServiceDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.method { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -5609,11 +5725,19 @@ impl<'a> ::buffa::ViewEncode<'a> for ServiceDescriptorProtoView<'a> { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.method { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -5707,7 +5831,9 @@ impl ServiceDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::ServiceDescriptorProto, @@ -5959,32 +6085,32 @@ impl<'a> ::buffa::ViewEncode<'a> for MethodDescriptorProtoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.input_type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.output_type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.client_streaming.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.server_streaming.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -6004,7 +6130,11 @@ impl<'a> ::buffa::ViewEncode<'a> for MethodDescriptorProtoView<'a> { ::buffa::types::put_string_field(3u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if let Some(v) = self.client_streaming { @@ -6113,7 +6243,9 @@ impl MethodDescriptorProtoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::MethodDescriptorProto, @@ -6668,82 +6800,82 @@ impl<'a> ::buffa::ViewEncode<'a> for FileOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.java_package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.java_outer_classname { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.optimize_for { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.java_multiple_files.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.go_package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.cc_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.py_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_generate_equals_and_hash.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_string_check_utf8.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.cc_enable_arenas.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.objc_class_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.csharp_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.swift_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_class_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_metadata_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.ruby_package { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -6811,13 +6943,17 @@ impl<'a> ::buffa::ViewEncode<'a> for FileOptionsView<'a> { ::buffa::types::put_string_field(45u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(50u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 50u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -6967,7 +7103,9 @@ impl FileOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FileOptions, @@ -7490,40 +7628,40 @@ impl<'a> ::buffa::ViewEncode<'a> for MessageOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.message_set_wire_format.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.no_standard_descriptor_accessor.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.map_entry.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated_legacy_json_field_conflicts.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -7549,13 +7687,17 @@ impl<'a> ::buffa::ViewEncode<'a> for MessageOptionsView<'a> { ::buffa::types::put_bool_field(11u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(12u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 12u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -7663,7 +7805,9 @@ impl MessageOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::MessageOptions, @@ -8258,71 +8402,71 @@ impl<'a> ::buffa::ViewEncode<'a> for FieldOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.ctype { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.packed.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.lazy.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.jstype { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.weak.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.unverified_lazy.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.debug_redact.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.retention { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.targets { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.edition_defaults { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.feature_support.is_set() { let __slot = __cache.reserve(); let inner_size = self.feature_support.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -8363,21 +8507,33 @@ impl<'a> ::buffa::ViewEncode<'a> for FieldOptionsView<'a> { ::buffa::types::put_int32_field(19u32, v.to_i32(), buf); } for v in &self.edition_defaults { - ::buffa::types::put_len_delimited_header(20u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 20u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(21u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 21u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } if self.feature_support.is_set() { - ::buffa::types::put_len_delimited_header(22u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 22u32, + u64::from(__cache.consume_next()), + buf, + ); self.feature_support.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -8517,7 +8673,9 @@ impl FieldOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FieldOptions, @@ -8852,15 +9010,15 @@ pub mod field_options { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -8964,7 +9122,9 @@ pub mod field_options { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::field_options::EditionDefault, @@ -9200,21 +9360,21 @@ pub mod field_options { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.edition_introduced { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.edition_deprecated { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.deprecation_warning { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition_removed { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -9338,7 +9498,9 @@ pub mod field_options { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::field_options::FeatureSupport, @@ -9577,25 +9739,25 @@ impl<'a> ::buffa::ViewEncode<'a> for OneofOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -9606,13 +9768,17 @@ impl<'a> ::buffa::ViewEncode<'a> for OneofOptionsView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; if self.features.is_set() { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -9701,7 +9867,9 @@ impl OneofOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::OneofOptions, @@ -9969,34 +10137,34 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.allow_alias.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated_legacy_json_field_conflicts.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -10016,13 +10184,17 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumOptionsView<'a> { ::buffa::types::put_bool_field(6u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -10122,7 +10294,9 @@ impl EnumOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::EnumOptions, @@ -10439,39 +10613,39 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumValueOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.debug_redact.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.feature_support.is_set() { let __slot = __cache.reserve(); let inner_size = self.feature_support.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -10485,20 +10659,28 @@ impl<'a> ::buffa::ViewEncode<'a> for EnumValueOptionsView<'a> { ::buffa::types::put_bool_field(1u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } if let Some(v) = self.debug_redact { ::buffa::types::put_bool_field(3u32, v, buf); } if self.feature_support.is_set() { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); self.feature_support.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -10600,7 +10782,9 @@ impl EnumValueOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::EnumValueOptions, @@ -10870,28 +11054,28 @@ impl<'a> ::buffa::ViewEncode<'a> for ServiceOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -10905,13 +11089,17 @@ impl<'a> ::buffa::ViewEncode<'a> for ServiceOptionsView<'a> { ::buffa::types::put_bool_field(33u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(34u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 34u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -11005,7 +11193,9 @@ impl ServiceOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::ServiceOptions, @@ -11281,31 +11471,31 @@ impl<'a> ::buffa::ViewEncode<'a> for MethodOptionsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.idempotency_level { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -11322,13 +11512,17 @@ impl<'a> ::buffa::ViewEncode<'a> for MethodOptionsView<'a> { ::buffa::types::put_int32_field(34u32, v.to_i32(), buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(35u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 35u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -11427,7 +11621,9 @@ impl MethodOptionsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::MethodOptions, @@ -11703,35 +11899,35 @@ impl<'a> ::buffa::ViewEncode<'a> for UninterpretedOptionView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.name { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.identifier_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.positive_int_value { - size += 1u32 + ::buffa::types::uint64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(v) as u64; } if let Some(v) = self.negative_int_value { - size += 1u32 + ::buffa::types::int64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(v) as u64; } if self.double_value.is_some() { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } if let Some(ref v) = self.string_value { - size += 1u32 + ::buffa::types::bytes_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(v) as u64; } if let Some(ref v) = self.aggregate_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -11742,7 +11938,11 @@ impl<'a> ::buffa::ViewEncode<'a> for UninterpretedOptionView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.name { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.identifier_value { @@ -11877,7 +12077,9 @@ impl UninterpretedOptionOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::UninterpretedOption, @@ -12108,11 +12310,11 @@ Distinguishes a field that was absent from one explicitly encoded with its defau fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += 1u32 + ::buffa::types::string_encoded_len(&self.name_part) as u32; - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += 1u64 + ::buffa::types::string_encoded_len(&self.name_part) as u64; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -12206,7 +12408,9 @@ Distinguishes a field that was absent from one explicitly encoded with its defau /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::uninterpreted_option::NamePart, @@ -12521,33 +12725,33 @@ impl<'a> ::buffa::ViewEncode<'a> for FeatureSetView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.field_presence { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.enum_type { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.repeated_field_encoding { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.utf8_validation { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.message_encoding { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.json_format { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.enforce_naming_style { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.default_symbol_visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -12713,7 +12917,9 @@ impl FeatureSetOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FeatureSet, @@ -12912,9 +13118,9 @@ pub mod feature_set { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -13004,7 +13210,9 @@ pub mod feature_set { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::feature_set::VisibilityFeature, @@ -13213,23 +13421,23 @@ impl<'a> ::buffa::ViewEncode<'a> for FeatureSetDefaultsView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.defaults { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.minimum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.maximum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -13240,7 +13448,11 @@ impl<'a> ::buffa::ViewEncode<'a> for FeatureSetDefaultsView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.defaults { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.minimum_edition { @@ -13346,7 +13558,9 @@ impl FeatureSetDefaultsOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FeatureSetDefaults, @@ -13618,28 +13832,28 @@ pub mod feature_set_defaults { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.overridable_features.is_set() { let __slot = __cache.reserve(); let inner_size = self.overridable_features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.fixed_features.is_set() { let __slot = __cache.reserve(); let inner_size = self.fixed_features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -13655,7 +13869,7 @@ pub mod feature_set_defaults { if self.overridable_features.is_set() { ::buffa::types::put_len_delimited_header( 4u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.overridable_features.write_to(__cache, buf); @@ -13663,7 +13877,7 @@ pub mod feature_set_defaults { if self.fixed_features.is_set() { ::buffa::types::put_len_delimited_header( 5u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.fixed_features.write_to(__cache, buf); @@ -13771,7 +13985,9 @@ pub mod feature_set_defaults { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::feature_set_defaults::FeatureSetEditionDefault, @@ -14011,17 +14227,17 @@ impl<'a> ::buffa::ViewEncode<'a> for SourceCodeInfoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.location { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -14032,7 +14248,11 @@ impl<'a> ::buffa::ViewEncode<'a> for SourceCodeInfoView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.location { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -14116,7 +14336,9 @@ impl SourceCodeInfoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::SourceCodeInfo, @@ -14478,38 +14700,34 @@ pub mod source_code_info { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if !self.span.is_empty() { - let payload: u32 = self + let payload: u64 = self .span .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if let Some(ref v) = self.leading_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.trailing_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.leading_detached_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -14520,22 +14738,22 @@ pub mod source_code_info { #[allow(unused_imports)] use ::buffa::Enumeration as _; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(1u32, payload, buf); for &v in &self.path { ::buffa::types::encode_int32(v, buf); } } if !self.span.is_empty() { - let payload: u32 = self + let payload: u64 = self .span .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(2u32, payload, buf); for &v in &self.span { ::buffa::types::encode_int32(v, buf); @@ -14653,7 +14871,9 @@ pub mod source_code_info { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::source_code_info::Location, @@ -14926,17 +15146,17 @@ impl<'a> ::buffa::ViewEncode<'a> for GeneratedCodeInfoView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.annotation { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -14947,7 +15167,11 @@ impl<'a> ::buffa::ViewEncode<'a> for GeneratedCodeInfoView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.annotation { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -15033,7 +15257,9 @@ impl GeneratedCodeInfoOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::GeneratedCodeInfo, @@ -15273,31 +15499,29 @@ pub mod generated_code_info { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if let Some(ref v) = self.source_file { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.begin { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.semantic { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -15308,11 +15532,11 @@ pub mod generated_code_info { #[allow(unused_imports)] use ::buffa::Enumeration as _; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(1u32, payload, buf); for &v in &self.path { ::buffa::types::encode_int32(v, buf); @@ -15431,7 +15655,9 @@ pub mod generated_code_info { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::super::generated_code_info::Annotation, diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs index 7732a947..486f501f 100644 --- a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs @@ -451,24 +451,26 @@ impl ::buffa::MessageName for FileDescriptorSet { impl ::buffa::Message for FileDescriptorSet { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.file { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -478,7 +480,11 @@ impl ::buffa::Message for FileDescriptorSet { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.file { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -989,88 +995,90 @@ impl ::buffa::MessageName for FileDescriptorProto { impl ::buffa::Message for FileDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.dependency { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.message_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.enum_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.service { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.source_code_info.is_set() { let __slot = __cache.reserve(); let inner_size = self.source_code_info.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.public_dependency { - size += 1u32 + ::buffa::types::int32_encoded_len(*v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(*v) as u64; } for v in &self.weak_dependency { - size += 1u32 + ::buffa::types::int32_encoded_len(*v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(*v) as u64; } if let Some(ref v) = self.syntax { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.option_dependency { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1089,27 +1097,51 @@ impl ::buffa::Message for FileDescriptorProto { ::buffa::types::put_string_field(3u32, v, buf); } for v in &self.message_type { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.enum_type { - ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 5u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.service { - ::buffa::types::put_len_delimited_header(6u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 6u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if self.source_code_info.is_set() { - ::buffa::types::put_len_delimited_header(9u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 9u32, + u64::from(__cache.consume_next()), + buf, + ); self.source_code_info.write_to(__cache, buf); } for v in &self.public_dependency { @@ -1733,89 +1765,91 @@ impl ::buffa::MessageName for DescriptorProto { impl ::buffa::Message for DescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.field { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.nested_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.enum_type { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.extension { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.oneof_decl { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1828,35 +1862,67 @@ impl ::buffa::Message for DescriptorProto { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.field { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.nested_type { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.enum_type { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension_range { - ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 5u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.extension { - ::buffa::types::put_len_delimited_header(6u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 6u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } for v in &self.oneof_decl { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_range { - ::buffa::types::put_len_delimited_header(9u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 9u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_name { @@ -2293,30 +2359,32 @@ pub mod descriptor_proto { impl ::buffa::Message for ExtensionRange { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -2334,7 +2402,7 @@ pub mod descriptor_proto { if self.options.is_set() { ::buffa::types::put_len_delimited_header( 3u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.options.write_to(__cache, buf); @@ -2550,22 +2618,24 @@ pub mod descriptor_proto { impl ::buffa::Message for ReservedRange { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -2810,43 +2880,45 @@ impl ::buffa::MessageName for ExtensionRangeOptions { impl ::buffa::Message for ExtensionRangeOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.declaration { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.verification { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -2856,20 +2928,28 @@ impl ::buffa::Message for ExtensionRangeOptions { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.declaration { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.verification { ::buffa::types::put_int32_field(3u32, v.to_i32(), buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(50u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 50u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -3559,31 +3639,33 @@ pub mod extension_range_options { impl ::buffa::Message for Declaration { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.full_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.r#type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.reserved.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.repeated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -4082,54 +4164,56 @@ impl ::buffa::MessageName for FieldDescriptorProto { impl ::buffa::Message for FieldDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.extendee { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.label { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.r#type { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.type_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.default_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(v) = self.oneof_index { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.json_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.proto3_optional.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -4160,7 +4244,11 @@ impl ::buffa::Message for FieldDescriptorProto { ::buffa::types::put_string_field(7u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(8u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 8u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if let Some(v) = self.oneof_index { @@ -4990,27 +5078,29 @@ impl ::buffa::MessageName for OneofDescriptorProto { impl ::buffa::Message for OneofDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -5023,7 +5113,11 @@ impl ::buffa::Message for OneofDescriptorProto { ::buffa::types::put_string_field(1u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -5276,49 +5370,51 @@ impl ::buffa::MessageName for EnumDescriptorProto { impl ::buffa::Message for EnumDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.value { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_range { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.reserved_name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -5331,15 +5427,27 @@ impl ::buffa::Message for EnumDescriptorProto { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.value { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } for v in &self.reserved_range { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } for v in &self.reserved_name { @@ -5649,22 +5757,24 @@ pub mod enum_descriptor_proto { impl ::buffa::Message for EnumReservedRange { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(v) = self.start { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -5886,30 +5996,32 @@ impl ::buffa::MessageName for EnumValueDescriptorProto { impl ::buffa::Message for EnumValueDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.number { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -5925,7 +6037,11 @@ impl ::buffa::Message for EnumValueDescriptorProto { ::buffa::types::put_int32_field(2u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -6140,35 +6256,37 @@ impl ::buffa::MessageName for ServiceDescriptorProto { impl ::buffa::Message for ServiceDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.method { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -6181,11 +6299,19 @@ impl ::buffa::Message for ServiceDescriptorProto { ::buffa::types::put_string_field(1u32, v, buf); } for v in &self.method { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 3u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -6483,39 +6609,41 @@ impl ::buffa::MessageName for MethodDescriptorProto { impl ::buffa::Message for MethodDescriptorProto { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.name { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.input_type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.output_type { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.options.is_set() { let __slot = __cache.reserve(); let inner_size = self.options.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.client_streaming.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.server_streaming.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -6534,7 +6662,11 @@ impl ::buffa::Message for MethodDescriptorProto { ::buffa::types::put_string_field(3u32, v, buf); } if self.options.is_set() { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); self.options.write_to(__cache, buf); } if let Some(v) = self.client_streaming { @@ -7300,89 +7432,91 @@ impl ::buffa::MessageName for FileOptions { impl ::buffa::Message for FileOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.java_package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.java_outer_classname { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.optimize_for { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.java_multiple_files.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.go_package { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.cc_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.py_generic_services.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_generate_equals_and_hash.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.java_string_check_utf8.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.cc_enable_arenas.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.objc_class_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.csharp_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.swift_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_class_prefix { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.php_metadata_namespace { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.ruby_package { - size += 2u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 2u64 + ::buffa::types::string_encoded_len(v) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -7449,13 +7583,17 @@ impl ::buffa::Message for FileOptions { ::buffa::types::put_string_field(45u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(50u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 50u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -8780,47 +8918,49 @@ impl ::buffa::MessageName for MessageOptions { impl ::buffa::Message for MessageOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.message_set_wire_format.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.no_standard_descriptor_accessor.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.map_entry.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated_legacy_json_field_conflicts.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -8845,13 +8985,17 @@ impl ::buffa::Message for MessageOptions { ::buffa::types::put_bool_field(11u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(12u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 12u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -9610,78 +9754,80 @@ impl ::buffa::MessageName for FieldOptions { impl ::buffa::Message for FieldOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.ctype { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.packed.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.lazy.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.jstype { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.weak.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.unverified_lazy.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.debug_redact.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.retention { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.targets { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } for v in &self.edition_defaults { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.feature_support.is_set() { let __slot = __cache.reserve(); let inner_size = self.feature_support.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -9721,21 +9867,33 @@ impl ::buffa::Message for FieldOptions { ::buffa::types::put_int32_field(19u32, v.to_i32(), buf); } for v in &self.edition_defaults { - ::buffa::types::put_len_delimited_header(20u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 20u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(21u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 21u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } if self.feature_support.is_set() { - ::buffa::types::put_len_delimited_header(22u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 22u32, + u64::from(__cache.consume_next()), + buf, + ); self.feature_support.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -11292,22 +11450,24 @@ pub mod field_options { impl ::buffa::Message for EditionDefault { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -11592,28 +11752,30 @@ pub mod field_options { impl ::buffa::Message for FeatureSupport { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.edition_introduced { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.edition_deprecated { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.deprecation_warning { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.edition_removed { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -11901,32 +12063,34 @@ impl ::buffa::MessageName for OneofOptions { impl ::buffa::Message for OneofOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -11936,13 +12100,17 @@ impl ::buffa::Message for OneofOptions { #[allow(unused_imports)] use ::buffa::Enumeration as _; if self.features.is_set() { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -12365,41 +12533,43 @@ impl ::buffa::MessageName for EnumOptions { impl ::buffa::Message for EnumOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.allow_alias.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.deprecated_legacy_json_field_conflicts.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -12418,13 +12588,17 @@ impl ::buffa::Message for EnumOptions { ::buffa::types::put_bool_field(6u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 7u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -12924,46 +13098,48 @@ impl ::buffa::MessageName for EnumValueOptions { impl ::buffa::Message for EnumValueOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.debug_redact.is_some() { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.feature_support.is_set() { let __slot = __cache.reserve(); let inner_size = self.feature_support.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -12976,20 +13152,28 @@ impl ::buffa::Message for EnumValueOptions { ::buffa::types::put_bool_field(1u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } if let Some(v) = self.debug_redact { ::buffa::types::put_bool_field(3u32, v, buf); } if self.feature_support.is_set() { - ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 4u32, + u64::from(__cache.consume_next()), + buf, + ); self.feature_support.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -13463,35 +13647,37 @@ impl ::buffa::MessageName for ServiceOptions { impl ::buffa::Message for ServiceOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -13504,13 +13690,17 @@ impl ::buffa::Message for ServiceOptions { ::buffa::types::put_bool_field(33u32, v, buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(34u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 34u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -13939,38 +14129,40 @@ impl ::buffa::MessageName for MethodOptions { impl ::buffa::Message for MethodOptions { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.deprecated.is_some() { - size += 2u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 2u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } if let Some(ref v) = self.idempotency_level { - size += 2u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 2u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.features.is_set() { let __slot = __cache.reserve(); let inner_size = self.features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } for v in &self.uninterpreted_option { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 2u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 2u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -13986,13 +14178,17 @@ impl ::buffa::Message for MethodOptions { ::buffa::types::put_int32_field(34u32, v.to_i32(), buf); } if self.features.is_set() { - ::buffa::types::put_len_delimited_header(35u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 35u32, + u64::from(__cache.consume_next()), + buf, + ); self.features.write_to(__cache, buf); } for v in &self.uninterpreted_option { ::buffa::types::put_len_delimited_header( 999u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); v.write_to(__cache, buf); @@ -14701,42 +14897,44 @@ impl ::buffa::MessageName for UninterpretedOption { impl ::buffa::Message for UninterpretedOption { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.name { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.identifier_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.positive_int_value { - size += 1u32 + ::buffa::types::uint64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(v) as u64; } if let Some(v) = self.negative_int_value { - size += 1u32 + ::buffa::types::int64_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(v) as u64; } if self.double_value.is_some() { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } if let Some(ref v) = self.string_value { - size += 1u32 + ::buffa::types::bytes_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(v) as u64; } if let Some(ref v) = self.aggregate_value { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -14746,7 +14944,11 @@ impl ::buffa::Message for UninterpretedOption { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.name { - ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 2u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.identifier_value { @@ -15057,18 +15259,20 @@ pub mod uninterpreted_option { impl ::buffa::Message for NamePart { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += 1u32 + ::buffa::types::string_encoded_len(&self.name_part) as u32; - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += 1u64 + ::buffa::types::string_encoded_len(&self.name_part) as u64; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -15415,40 +15619,42 @@ impl ::buffa::MessageName for FeatureSet { impl ::buffa::Message for FeatureSet { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.field_presence { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.enum_type { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.repeated_field_encoding { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.utf8_validation { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.message_encoding { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.json_format { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.enforce_naming_style { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.default_symbol_visibility { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -17233,16 +17439,18 @@ pub mod feature_set { impl ::buffa::Message for VisibilityFeature { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -17639,30 +17847,32 @@ impl ::buffa::MessageName for FeatureSetDefaults { impl ::buffa::Message for FeatureSetDefaults { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.defaults { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if let Some(ref v) = self.minimum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if let Some(ref v) = self.maximum_edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -17672,7 +17882,11 @@ impl ::buffa::Message for FeatureSetDefaults { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.defaults { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } if let Some(ref v) = self.minimum_edition { @@ -17939,35 +18153,37 @@ pub mod feature_set_defaults { impl ::buffa::Message for FeatureSetEditionDefault { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let Some(ref v) = self.edition { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } if self.overridable_features.is_set() { let __slot = __cache.reserve(); let inner_size = self.overridable_features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } if self.fixed_features.is_set() { let __slot = __cache.reserve(); let inner_size = self.fixed_features.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -17982,7 +18198,7 @@ pub mod feature_set_defaults { if self.overridable_features.is_set() { ::buffa::types::put_len_delimited_header( 4u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.overridable_features.write_to(__cache, buf); @@ -17990,7 +18206,7 @@ pub mod feature_set_defaults { if self.fixed_features.is_set() { ::buffa::types::put_len_delimited_header( 5u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); self.fixed_features.write_to(__cache, buf); @@ -18249,24 +18465,26 @@ impl ::buffa::MessageName for SourceCodeInfo { impl ::buffa::Message for SourceCodeInfo { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.location { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -18276,7 +18494,11 @@ impl ::buffa::Message for SourceCodeInfo { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.location { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -18730,45 +18952,43 @@ pub mod source_code_info { impl ::buffa::Message for Location { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if !self.span.is_empty() { - let payload: u32 = self + let payload: u64 = self .span .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if let Some(ref v) = self.leading_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(ref v) = self.trailing_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } for v in &self.leading_detached_comments { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -18778,22 +18998,22 @@ pub mod source_code_info { #[allow(unused_imports)] use ::buffa::Enumeration as _; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(1u32, payload, buf); for &v in &self.path { ::buffa::types::encode_int32(v, buf); } } if !self.span.is_empty() { - let payload: u32 = self + let payload: u64 = self .span .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(2u32, payload, buf); for &v in &self.span { ::buffa::types::encode_int32(v, buf); @@ -19094,24 +19314,26 @@ impl ::buffa::MessageName for GeneratedCodeInfo { impl ::buffa::Message for GeneratedCodeInfo { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.annotation { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -19121,7 +19343,11 @@ impl ::buffa::Message for GeneratedCodeInfo { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.annotation { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -19375,38 +19601,38 @@ pub mod generated_code_info { impl ::buffa::Message for Annotation { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); - size - += 1u32 + ::buffa::encoding::varint_len(payload as u64) as u32 - + payload; + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); + size += 1u64 + ::buffa::encoding::varint_len(payload) as u64 + payload; } if let Some(ref v) = self.source_file { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } if let Some(v) = self.begin { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(v) = self.end { - size += 1u32 + ::buffa::types::int32_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v) as u64; } if let Some(ref v) = self.semantic { - size += 1u32 + ::buffa::types::int32_encoded_len(v.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(v.to_i32()) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -19416,11 +19642,11 @@ pub mod generated_code_info { #[allow(unused_imports)] use ::buffa::Enumeration as _; if !self.path.is_empty() { - let payload: u32 = self + let payload: u64 = self .path .iter() - .map(|&v| ::buffa::types::int32_encoded_len(v) as u32) - .sum::(); + .map(|&v| ::buffa::types::int32_encoded_len(v) as u64) + .sum::(); ::buffa::types::put_len_delimited_header(1u32, payload, buf); for &v in &self.path { ::buffa::types::encode_int32(v, buf); diff --git a/buffa-descriptor/src/reflect/dynamic.rs b/buffa-descriptor/src/reflect/dynamic.rs index 7dd8e03e..cbf8c1df 100644 --- a/buffa-descriptor/src/reflect/dynamic.rs +++ b/buffa-descriptor/src/reflect/dynamic.rs @@ -547,7 +547,41 @@ impl DynamicMessage { // ── Encode ────────────────────────────────────────────────────────────── /// Encode this message into `buf`. + /// + /// The size check costs a full [`encoded_len`](Self::encoded_len) walk + /// before the write pass (the single-pass descriptor-driven codec has no + /// size cache to reuse). [`encode_to_vec`](Self::encode_to_vec) pays no + /// extra walk — it needs the length for its allocation anyway. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]): no conforming decoder — including + /// buffa's own — would accept the output. See + /// [`try_encode`](Self::try_encode) for the error-returning variant. pub fn encode(&self, buf: &mut impl BufMut) { + self.try_encode(buf) + .unwrap_or_else(|_| buffa::encode_size_overflow()) + } + + /// Encode, returning an error instead of panicking if the encoded size + /// exceeds the 2 GiB protobuf limit ([`buffa::MAX_MESSAGE_BYTES`]). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`buffa::EncodeError::MessageTooLarge`] if the encoded size + /// exceeds the limit. + pub fn try_encode(&self, buf: &mut impl BufMut) -> Result<(), buffa::EncodeError> { + Self::checked_encode_size(self.encoded_len())?; + self.encode_unchecked(buf); + Ok(()) + } + + /// Encode without the size check; callers must have validated + /// `encoded_len()` against the 2 GiB limit already. + fn encode_unchecked(&self, buf: &mut impl BufMut) { for (&number, value) in &self.fields { // Skip if neither the descriptor nor the extension index // recognizes this number anymore (defensive — the fields map @@ -563,14 +597,56 @@ impl DynamicMessage { } /// Encode this message to a fresh `Vec`. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]). See + /// [`try_encode_to_vec`](Self::try_encode_to_vec) for the + /// error-returning variant. #[must_use] pub fn encode_to_vec(&self) -> Vec { - let mut buf = Vec::with_capacity(self.encoded_len()); - self.encode(&mut buf); - buf + self.try_encode_to_vec() + .unwrap_or_else(|_| buffa::encode_size_overflow()) + } + + /// Encode to a fresh `Vec`, returning an error instead of panicking + /// if the encoded size exceeds the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`buffa::EncodeError::MessageTooLarge`] if the encoded size + /// exceeds the limit. + pub fn try_encode_to_vec(&self) -> Result, buffa::EncodeError> { + let len = Self::checked_encode_size(self.encoded_len())?; + let mut buf = Vec::with_capacity(len); + self.encode_unchecked(&mut buf); + Ok(buf) + } + + /// Validate an encoded size against the 2 GiB protobuf limit. + /// + /// The single-pass descriptor-driven codec computes sizes in `usize`, + /// which is exact on 64-bit hosts — unlike the generated two-pass codec + /// there is no `u32` arithmetic to saturate. This adapts the `usize` + /// domain onto the shared [`buffa::checked_encode_size`] guard (via + /// [`buffa::saturate_size`], so a value past `u32::MAX` still trips the + /// check) rather than restating the comparison here. + fn checked_encode_size(len: usize) -> Result { + buffa::checked_encode_size(buffa::saturate_size(len as u64))?; + Ok(len) } /// Compute the encoded length. + /// + /// Unlike [`Message::encoded_len`](buffa::Message::encoded_len), this + /// does **not** enforce the 2 GiB limit: the `usize` result is exact + /// (there is no `u32` saturation that could turn an over-limit size + /// into a lie), so the check lives only in the encode entry points. + /// Use [`try_encode_to_vec`](Self::try_encode_to_vec) or compare + /// against [`buffa::MAX_MESSAGE_BYTES`] if you need the guard before + /// writing. #[must_use] pub fn encoded_len(&self) -> usize { let mut len = self.unknown.encoded_len(); @@ -593,7 +669,9 @@ impl DynamicMessage { /// /// Panics if `msg.encode_to_vec()` produces bytes that fail to decode /// against `msg_idx`'s descriptor. This indicates a mismatch between the - /// descriptor in the pool and the generated `Message` impl. + /// descriptor in the pool and the generated `Message` impl. Also panics + /// (inside `encode_to_vec`) if `msg`'s encoded size exceeds the 2 GiB + /// protobuf limit ([`buffa::MAX_MESSAGE_BYTES`]). #[must_use] pub fn from_message( msg: &M, @@ -613,7 +691,11 @@ impl DynamicMessage { /// `M` — typically a descriptor mismatch between the pool and the /// generated type. pub fn to_message(&self) -> Result { - let bytes = self.encode_to_vec(); + // An over-limit snapshot cannot become valid wire bytes; surface the + // mirror decode error instead of panicking inside a fallible API. + let bytes = self + .try_encode_to_vec() + .map_err(|_| DecodeError::MessageTooLarge)?; let mut m = M::default(); m.merge_from_slice(&bytes)?; Ok(m) @@ -772,7 +854,9 @@ impl DynamicMessage { /// /// [`AnyError::AnyNotRegistered`] if the pool does not contain /// `google.protobuf.Any` (e.g. a `FileDescriptorSet` built without the - /// well-known types). + /// well-known types). [`AnyError::MessageTooLarge`] if this message's + /// encoded size exceeds the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]). pub fn pack_any(&self) -> Result { let any_idx = self .pool @@ -783,8 +867,11 @@ impl DynamicMessage { self.message_descriptor().full_name ); let mut any = DynamicMessage::new(Arc::clone(&self.pool), any_idx); + let bytes = self + .try_encode_to_vec() + .map_err(|_| AnyError::MessageTooLarge)?; any.insert_value(1, Value::String(type_url)); - any.insert_value(2, Value::Bytes(self.encode_to_vec())); + any.insert_value(2, Value::Bytes(bytes)); Ok(any) } @@ -821,7 +908,8 @@ impl DynamicMessage { O: Message + buffa::MessageName, { let idx = pool.message_index(O::FULL_NAME)?; - Self::decode(pool, idx, &options.encode_to_vec()).ok() + let bytes = options.try_encode_to_vec().ok()?; + Self::decode(pool, idx, &bytes).ok() } } @@ -849,6 +937,10 @@ pub enum AnyError { /// The unresolvable type URL. type_url: String, }, + /// The message being packed encodes past the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]), so it cannot become a valid + /// `Any.value` payload. + MessageTooLarge, /// The wrapped `value` bytes failed to decode against the resolved type. Decode { /// The `type_url` that resolved before the decode failed. @@ -868,6 +960,9 @@ impl core::fmt::Display for AnyError { write!(f, "google.protobuf.Any is not registered in the pool") } Self::MissingTypeUrl => write!(f, "Any has no type_url"), + Self::MessageTooLarge => { + write!(f, "packed message exceeds the 2 GiB protobuf limit") + } Self::UnknownType { type_url } => { write!(f, "Any type_url {type_url:?} not registered in the pool") } @@ -1328,13 +1423,17 @@ fn encode_singular_with_tag( (SingularKind::Scalar(s), v) => encode_scalar(s, v, buf), (SingularKind::Enum(_), Value::EnumNumber(n)) => encode_int32(*n, buf), (SingularKind::Message(_), Value::Message(m)) => { + // encode_unchecked: nested sizes are covered by the top-level + // entry point's 2 GiB check (a parent is at least as large as + // any child), and re-checking here would add a size walk per + // nesting level. if delimited { - m.encode(buf); + m.encode_unchecked(buf); Tag::new(number, WireType::EndGroup).encode(buf); } else { let len = m.encoded_len(); encode_varint(len as u64, buf); - m.encode(buf); + m.encode_unchecked(buf); } } _ => {} // shape mismatch — already wrote a tag, but no payload follows; corrupt output is the consumer's problem @@ -1403,10 +1502,11 @@ fn encode_packed_element(kind: SingularKind, v: &Value, buf: &mut impl BufMut) { (SingularKind::Scalar(s), v) => encode_scalar(s, v, buf), (SingularKind::Enum(_), Value::EnumNumber(n)) => encode_int32(*n, buf), (SingularKind::Message(_), Value::Message(m)) => { - // Map values are length-prefixed messages. + // Map values are length-prefixed messages. encode_unchecked: + // covered by the top-level entry point's 2 GiB check. let len = m.encoded_len(); encode_varint(len as u64, buf); - m.encode(buf); + m.encode_unchecked(buf); } _ => {} } @@ -1480,3 +1580,31 @@ const _: fn(EnumIndex) = |_| {}; fn tag_len(field_number: u32, wt: WireType) -> usize { uint64_encoded_len(((field_number as u64) << 3) | (wt as u64)) } + +#[cfg(test)] +mod tests { + use super::DynamicMessage; + + // The encode entry points funnel through `checked_encode_size`; + // exercising the over-limit path end-to-end would require materializing + // >2 GiB of field data, so the usize adapter over the shared guard is + // tested directly (the e2e encode paths are covered in + // `tests/dynamic_e2e.rs`, and the shared guard's own boundary in + // `buffa::message`). + + #[test] + fn checked_encode_size_boundary() { + let max = buffa::MAX_MESSAGE_BYTES as usize; + assert_eq!(DynamicMessage::checked_encode_size(max), Ok(max)); + assert_eq!( + DynamicMessage::checked_encode_size(max + 1), + Err(buffa::EncodeError::MessageTooLarge) + ); + // Past u32::MAX the adapter saturates rather than wrapping. + #[cfg(target_pointer_width = "64")] + assert_eq!( + DynamicMessage::checked_encode_size(u32::MAX as usize + 1), + Err(buffa::EncodeError::MessageTooLarge) + ); + } +} diff --git a/buffa-descriptor/src/reflect/json_wkt.rs b/buffa-descriptor/src/reflect/json_wkt.rs index 68da72e8..601548fa 100644 --- a/buffa-descriptor/src/reflect/json_wkt.rs +++ b/buffa-descriptor/src/reflect/json_wkt.rs @@ -520,8 +520,11 @@ fn deserialize_any<'de, D: Deserializer<'de>>( .ignore_unknown_fields(ignore_unknown) .deserialize(inner_json) .map_err(|e| D::Error::custom(format!("Any inner deserialize failed: {e}")))?; + let inner_bytes = inner + .try_encode_to_vec() + .map_err(|e| D::Error::custom(format!("Any inner re-encode failed: {e}")))?; any.set_by_number(1, Value::String(type_url)); - any.set_by_number(2, Value::Bytes(inner.encode_to_vec())); + any.set_by_number(2, Value::Bytes(inner_bytes)); Ok(any) } diff --git a/buffa-types/src/any_ext.rs b/buffa-types/src/any_ext.rs index 98e4cd07..0b7d46d1 100644 --- a/buffa-types/src/any_ext.rs +++ b/buffa-types/src/any_ext.rs @@ -10,6 +10,12 @@ impl Any { /// The type URL is conventionally of the form /// `type.googleapis.com/fully.qualified.TypeName`, but this method does /// not enforce that convention — any string is accepted. + /// + /// # Panics + /// + /// Panics if `msg`'s encoded size exceeds the 2 GiB protobuf limit + /// ([`buffa::MAX_MESSAGE_BYTES`]) — see [`try_pack`](Self::try_pack) + /// for the error-returning variant. pub fn pack(msg: &impl buffa::Message, type_url: impl Into) -> Self { Self { type_url: type_url.into(), @@ -18,6 +24,25 @@ impl Any { } } + /// Pack a message into an [`Any`], returning an error instead of + /// panicking if the message's encoded size exceeds the 2 GiB protobuf + /// limit ([`buffa::MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`buffa::EncodeError::MessageTooLarge`] if the encoded size + /// exceeds the limit. + pub fn try_pack( + msg: &impl buffa::Message, + type_url: impl Into, + ) -> Result { + Ok(Self { + type_url: type_url.into(), + value: msg.try_encode_to_bytes()?, + ..Default::default() + }) + } + /// Unpack the contained message, decoding its bytes as `T`, **without /// checking the `type_url`**. /// @@ -105,7 +130,7 @@ pub fn register_wkt_types(reg: &mut buffa::type_registry::TypeRegistry) { from_json: |value| { let msg: $type = serde_json::from_value(value).map_err(|e| e.to_string())?; - Ok(buffa::Message::encode_to_vec(&msg)) + buffa::Message::try_encode_to_vec(&msg).map_err(|e| e.to_string()) }, is_wkt: $wkt, }); @@ -399,6 +424,62 @@ mod tests { let _ = any.value.slice(..); } + /// Test double whose `compute_size` reports over the 2 GiB limit and + /// whose `write_to` writes nothing — exercises `pack`'s guard without + /// materializing gigabytes. Mirrors buffa's crate-internal + /// `test_doubles::SizedMsg` (`#[cfg(test)]` items don't cross the crate + /// boundary). + #[derive(Clone, Default, PartialEq, Debug)] + struct HugeMsg; + + impl buffa::DefaultInstance for HugeMsg { + fn default_instance() -> &'static Self { + static INST: buffa::__private::OnceBox = buffa::__private::OnceBox::new(); + INST.get_or_init(|| alloc::boxed::Box::new(HugeMsg)) + } + } + + impl buffa::Message for HugeMsg { + fn compute_size(&self, _cache: &mut buffa::SizeCache) -> u32 { + buffa::MAX_MESSAGE_BYTES + 1 + } + fn write_to(&self, _cache: &mut buffa::SizeCache, _buf: &mut impl bytes::BufMut) {} + fn merge_field( + &mut self, + tag: buffa::encoding::Tag, + buf: &mut impl bytes::Buf, + _ctx: buffa::DecodeContext<'_>, + ) -> Result<(), buffa::DecodeError> { + buffa::encoding::skip_field(tag, buf)?; + Ok(()) + } + fn clear(&mut self) {} + } + + #[test] + fn try_pack_over_limit_errs() { + assert_eq!( + Any::try_pack(&HugeMsg, "type.googleapis.com/x"), + Err(buffa::EncodeError::MessageTooLarge) + ); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn pack_over_limit_panics() { + let _ = Any::pack(&HugeMsg, "type.googleapis.com/x"); + } + + #[test] + fn try_pack_matches_pack_for_normal_messages() { + let ts = Timestamp { + seconds: 42, + ..Default::default() + }; + let url = "type.googleapis.com/google.protobuf.Timestamp"; + assert_eq!(Any::try_pack(&ts, url).unwrap(), Any::pack(&ts, url)); + } + #[test] fn pack_and_unpack() { let ts = Timestamp { diff --git a/buffa-types/src/generated/google.protobuf.any.__view.rs b/buffa-types/src/generated/google.protobuf.any.__view.rs index 77fd6412..1b56616f 100644 --- a/buffa-types/src/generated/google.protobuf.any.__view.rs +++ b/buffa-types/src/generated/google.protobuf.any.__view.rs @@ -212,15 +212,15 @@ impl<'a> ::buffa::ViewEncode<'a> for AnyView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.type_url.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.type_url) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.type_url) as u64; } if !self.value.is_empty() { - size += 1u32 + ::buffa::types::bytes_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -288,7 +288,9 @@ impl AnyOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Any, diff --git a/buffa-types/src/generated/google.protobuf.any.rs b/buffa-types/src/generated/google.protobuf.any.rs index 30eda240..328b856e 100644 --- a/buffa-types/src/generated/google.protobuf.any.rs +++ b/buffa-types/src/generated/google.protobuf.any.rs @@ -267,22 +267,24 @@ impl ::buffa::MessageName for Any { impl ::buffa::Message for Any { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.type_url.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.type_url) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.type_url) as u64; } if !self.value.is_empty() { - size += 1u32 + ::buffa::types::bytes_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa-types/src/generated/google.protobuf.duration.__view.rs b/buffa-types/src/generated/google.protobuf.duration.__view.rs index ff22a398..8b36dc58 100644 --- a/buffa-types/src/generated/google.protobuf.duration.__view.rs +++ b/buffa-types/src/generated/google.protobuf.duration.__view.rs @@ -159,15 +159,15 @@ impl<'a> ::buffa::ViewEncode<'a> for DurationView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.seconds != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.seconds) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.seconds) as u64; } if self.nanos != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.nanos) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.nanos) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -235,7 +235,9 @@ impl DurationOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Duration, diff --git a/buffa-types/src/generated/google.protobuf.duration.rs b/buffa-types/src/generated/google.protobuf.duration.rs index 2f016288..09e1b8c6 100644 --- a/buffa-types/src/generated/google.protobuf.duration.rs +++ b/buffa-types/src/generated/google.protobuf.duration.rs @@ -210,22 +210,24 @@ impl ::buffa::MessageName for Duration { impl ::buffa::Message for Duration { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.seconds != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.seconds) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.seconds) as u64; } if self.nanos != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.nanos) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.nanos) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa-types/src/generated/google.protobuf.empty.__view.rs b/buffa-types/src/generated/google.protobuf.empty.__view.rs index 4b403f82..15624e01 100644 --- a/buffa-types/src/generated/google.protobuf.empty.__view.rs +++ b/buffa-types/src/generated/google.protobuf.empty.__view.rs @@ -73,9 +73,9 @@ impl<'a> ::buffa::ViewEncode<'a> for EmptyView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -137,7 +137,9 @@ impl EmptyOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Empty, diff --git a/buffa-types/src/generated/google.protobuf.empty.rs b/buffa-types/src/generated/google.protobuf.empty.rs index 62ada694..89441cbe 100644 --- a/buffa-types/src/generated/google.protobuf.empty.rs +++ b/buffa-types/src/generated/google.protobuf.empty.rs @@ -133,16 +133,18 @@ impl ::buffa::MessageName for Empty { impl ::buffa::Message for Empty { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + let mut size = 0u64; + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__view.rs b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs index 7d16655e..aa170340 100644 --- a/buffa-types/src/generated/google.protobuf.field_mask.__view.rs +++ b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs @@ -299,12 +299,12 @@ impl<'a> ::buffa::ViewEncode<'a> for FieldMaskView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.paths { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -371,7 +371,9 @@ impl FieldMaskOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FieldMask, diff --git a/buffa-types/src/generated/google.protobuf.field_mask.rs b/buffa-types/src/generated/google.protobuf.field_mask.rs index 0d996116..326e1a0d 100644 --- a/buffa-types/src/generated/google.protobuf.field_mask.rs +++ b/buffa-types/src/generated/google.protobuf.field_mask.rs @@ -353,19 +353,21 @@ impl ::buffa::MessageName for FieldMask { impl ::buffa::Message for FieldMask { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.paths { - size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(v) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa-types/src/generated/google.protobuf.struct.__view.rs b/buffa-types/src/generated/google.protobuf.struct.__view.rs index beddae53..54b43988 100644 --- a/buffa-types/src/generated/google.protobuf.struct.__view.rs +++ b/buffa-types/src/generated/google.protobuf.struct.__view.rs @@ -132,23 +132,21 @@ impl<'a> ::buffa::ViewEncode<'a> for StructView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; #[allow(clippy::for_kv_map)] for (k, v) in &self.fields { - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 + let entry_size: u64 = 1u64 + ::buffa::types::string_encoded_len(k) as u64 + + 1u64 + { let __slot = __cache.reserve(); let inner = v.compute_size(__cache); __cache.set(__slot, inner); - ::buffa::encoding::varint_len(inner as u64) as u32 + inner + ::buffa::encoding::varint_len(inner as u64) as u64 + inner as u64 }; - size - += 1u32 + ::buffa::encoding::varint_len(entry_size as u64) as u32 - + entry_size; + size += 1u64 + ::buffa::encoding::varint_len(entry_size) as u64 + entry_size; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -160,15 +158,16 @@ impl<'a> ::buffa::ViewEncode<'a> for StructView<'a> { use ::buffa::Enumeration as _; for (k, v) in &self.fields { let __v_len = __cache.consume_next(); - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 - + (::buffa::encoding::varint_len(__v_len as u64) as u32 + __v_len); + let entry_size: u64 = 1u64 + ::buffa::types::string_encoded_len(k) as u64 + + 1u64 + + (::buffa::encoding::varint_len(__v_len as u64) as u64 + + __v_len as u64); ::buffa::encoding::Tag::new( 1u32, ::buffa::encoding::WireType::LengthDelimited, ) .encode(buf); - ::buffa::encoding::encode_varint(entry_size as u64, buf); + ::buffa::encoding::encode_varint(entry_size, buf); ::buffa::encoding::Tag::new( 1u32, ::buffa::encoding::WireType::LengthDelimited, @@ -235,7 +234,9 @@ impl StructOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Struct, @@ -611,41 +612,41 @@ impl<'a> ::buffa::ViewEncode<'a> for ValueView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let ::core::option::Option::Some(ref v) = self.kind { match v { super::super::__buffa::view::oneof::value::Kind::NullValue(x) => { - size += 1u32 + ::buffa::types::int32_encoded_len(x.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(x.to_i32()) as u64; } super::super::__buffa::view::oneof::value::Kind::NumberValue(_x) => { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } super::super::__buffa::view::oneof::value::Kind::StringValue(x) => { - size += 1u32 + ::buffa::types::string_encoded_len(x) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(x) as u64; } super::super::__buffa::view::oneof::value::Kind::BoolValue(_x) => { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } super::super::__buffa::view::oneof::value::Kind::StructValue(x) => { let __slot = __cache.reserve(); let inner = x.compute_size(__cache); __cache.set(__slot, inner); size - += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 - + inner; + += 1u64 + ::buffa::encoding::varint_len(inner as u64) as u64 + + inner as u64; } super::super::__buffa::view::oneof::value::Kind::ListValue(x) => { let __slot = __cache.reserve(); let inner = x.compute_size(__cache); __cache.set(__slot, inner); size - += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 - + inner; + += 1u64 + ::buffa::encoding::varint_len(inner as u64) as u64 + + inner as u64; } } } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -672,7 +673,7 @@ impl<'a> ::buffa::ViewEncode<'a> for ValueView<'a> { super::super::__buffa::view::oneof::value::Kind::StructValue(x) => { ::buffa::types::put_len_delimited_header( 5u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); x.write_to(__cache, buf); @@ -680,7 +681,7 @@ impl<'a> ::buffa::ViewEncode<'a> for ValueView<'a> { super::super::__buffa::view::oneof::value::Kind::ListValue(x) => { ::buffa::types::put_len_delimited_header( 6u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); x.write_to(__cache, buf); @@ -739,7 +740,9 @@ impl ValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Value, @@ -1093,17 +1096,17 @@ impl<'a> ::buffa::ViewEncode<'a> for ListValueView<'a> { fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.values { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1114,7 +1117,11 @@ impl<'a> ::buffa::ViewEncode<'a> for ListValueView<'a> { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.values { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -1171,7 +1178,9 @@ impl ListValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::ListValue, diff --git a/buffa-types/src/generated/google.protobuf.struct.rs b/buffa-types/src/generated/google.protobuf.struct.rs index c39e25b6..986949ec 100644 --- a/buffa-types/src/generated/google.protobuf.struct.rs +++ b/buffa-types/src/generated/google.protobuf.struct.rs @@ -184,22 +184,24 @@ impl ::buffa::MessageName for Struct { impl ::buffa::Message for Struct { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; size += ::buffa::map_codec::message_field_len::< ::buffa::map_codec::Str, _, _, - >(&self.fields, 1u32, __cache); - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + >(&self.fields, 1u64, __cache); + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -573,48 +575,50 @@ impl ::buffa::MessageName for Value { impl ::buffa::Message for Value { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if let ::core::option::Option::Some(ref v) = self.kind { match v { __buffa::oneof::value::Kind::NullValue(x) => { - size += 1u32 + ::buffa::types::int32_encoded_len(x.to_i32()) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(x.to_i32()) as u64; } __buffa::oneof::value::Kind::NumberValue(_x) => { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } __buffa::oneof::value::Kind::StringValue(x) => { - size += 1u32 + ::buffa::types::string_encoded_len(x) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(x) as u64; } __buffa::oneof::value::Kind::BoolValue(_x) => { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } __buffa::oneof::value::Kind::StructValue(x) => { let __slot = __cache.reserve(); let inner = x.compute_size(__cache); __cache.set(__slot, inner); size - += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 - + inner; + += 1u64 + ::buffa::encoding::varint_len(inner as u64) as u64 + + inner as u64; } __buffa::oneof::value::Kind::ListValue(x) => { let __slot = __cache.reserve(); let inner = x.compute_size(__cache); __cache.set(__slot, inner); size - += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 - + inner; + += 1u64 + ::buffa::encoding::varint_len(inner as u64) as u64 + + inner as u64; } } } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -640,7 +644,7 @@ impl ::buffa::Message for Value { __buffa::oneof::value::Kind::StructValue(x) => { ::buffa::types::put_len_delimited_header( 5u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); x.write_to(__cache, buf); @@ -648,7 +652,7 @@ impl ::buffa::Message for Value { __buffa::oneof::value::Kind::ListValue(x) => { ::buffa::types::put_len_delimited_header( 6u32, - __cache.consume_next(), + u64::from(__cache.consume_next()), buf, ); x.write_to(__cache, buf); @@ -1034,24 +1038,26 @@ impl ::buffa::MessageName for ListValue { impl ::buffa::Message for ListValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.values { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1061,7 +1067,11 @@ impl ::buffa::Message for ListValue { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.values { - ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__view.rs b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs index d42db5af..785f8e2d 100644 --- a/buffa-types/src/generated/google.protobuf.timestamp.__view.rs +++ b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs @@ -194,15 +194,15 @@ impl<'a> ::buffa::ViewEncode<'a> for TimestampView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.seconds != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.seconds) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.seconds) as u64; } if self.nanos != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.nanos) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.nanos) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -272,7 +272,9 @@ impl TimestampOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Timestamp, diff --git a/buffa-types/src/generated/google.protobuf.timestamp.rs b/buffa-types/src/generated/google.protobuf.timestamp.rs index 6d31ccce..78435085 100644 --- a/buffa-types/src/generated/google.protobuf.timestamp.rs +++ b/buffa-types/src/generated/google.protobuf.timestamp.rs @@ -245,22 +245,24 @@ impl ::buffa::MessageName for Timestamp { impl ::buffa::Message for Timestamp { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.seconds != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.seconds) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.seconds) as u64; } if self.nanos != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.nanos) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.nanos) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__view.rs b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs index 292bef2e..7560206c 100644 --- a/buffa-types/src/generated/google.protobuf.wrappers.__view.rs +++ b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs @@ -79,12 +79,12 @@ impl<'a> ::buffa::ViewEncode<'a> for DoubleValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value.to_bits() != 0u64 { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -151,7 +151,9 @@ impl DoubleValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::DoubleValue, @@ -384,12 +386,12 @@ impl<'a> ::buffa::ViewEncode<'a> for FloatValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value.to_bits() != 0u32 { - size += 1u32 + ::buffa::types::FIXED32_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED32_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -456,7 +458,9 @@ impl FloatValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::FloatValue, @@ -689,12 +693,12 @@ impl<'a> ::buffa::ViewEncode<'a> for Int64ValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -761,7 +765,9 @@ impl Int64ValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Int64Value, @@ -994,12 +1000,12 @@ impl<'a> ::buffa::ViewEncode<'a> for UInt64ValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0u64 { - size += 1u32 + ::buffa::types::uint64_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1066,7 +1072,9 @@ impl UInt64ValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::UInt64Value, @@ -1299,12 +1307,12 @@ impl<'a> ::buffa::ViewEncode<'a> for Int32ValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1371,7 +1379,9 @@ impl Int32ValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::Int32Value, @@ -1604,12 +1614,12 @@ impl<'a> ::buffa::ViewEncode<'a> for UInt32ValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0u32 { - size += 1u32 + ::buffa::types::uint32_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::uint32_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1676,7 +1686,9 @@ impl UInt32ValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::UInt32Value, @@ -1909,12 +1921,12 @@ impl<'a> ::buffa::ViewEncode<'a> for BoolValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -1981,7 +1993,9 @@ impl BoolValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::BoolValue, @@ -2214,12 +2228,12 @@ impl<'a> ::buffa::ViewEncode<'a> for StringValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.value.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -2286,7 +2300,9 @@ impl StringValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::StringValue, @@ -2519,12 +2535,12 @@ impl<'a> ::buffa::ViewEncode<'a> for BytesValueView<'a> { fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.value.is_empty() { - size += 1u32 + ::buffa::types::bytes_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } #[allow(clippy::needless_borrow)] fn write_to( @@ -2591,7 +2607,9 @@ impl BytesValueOwnedView { /// /// # Errors /// - /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// Returns [`::buffa::DecodeError::MessageTooLarge`] if the + /// message's encoded size exceeds the 2 GiB protobuf limit, or + /// another [`::buffa::DecodeError`] if the re-encoded bytes are /// somehow invalid (should not happen for well-formed messages). pub fn from_owned( msg: &super::super::BytesValue, diff --git a/buffa-types/src/generated/google.protobuf.wrappers.rs b/buffa-types/src/generated/google.protobuf.wrappers.rs index f350d9a1..fdd7f4ba 100644 --- a/buffa-types/src/generated/google.protobuf.wrappers.rs +++ b/buffa-types/src/generated/google.protobuf.wrappers.rs @@ -133,19 +133,21 @@ impl ::buffa::MessageName for DoubleValue { impl ::buffa::Message for DoubleValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value.to_bits() != 0u64 { - size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED64_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -365,19 +367,21 @@ impl ::buffa::MessageName for FloatValue { impl ::buffa::Message for FloatValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value.to_bits() != 0u32 { - size += 1u32 + ::buffa::types::FIXED32_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::FIXED32_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -597,19 +601,21 @@ impl ::buffa::MessageName for Int64Value { impl ::buffa::Message for Int64Value { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0i64 { - size += 1u32 + ::buffa::types::int64_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::int64_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -829,19 +835,21 @@ impl ::buffa::MessageName for UInt64Value { impl ::buffa::Message for UInt64Value { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0u64 { - size += 1u32 + ::buffa::types::uint64_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::uint64_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1061,19 +1069,21 @@ impl ::buffa::MessageName for Int32Value { impl ::buffa::Message for Int32Value { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0i32 { - size += 1u32 + ::buffa::types::int32_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1293,19 +1303,21 @@ impl ::buffa::MessageName for UInt32Value { impl ::buffa::Message for UInt32Value { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value != 0u32 { - size += 1u32 + ::buffa::types::uint32_encoded_len(self.value) as u32; + size += 1u64 + ::buffa::types::uint32_encoded_len(self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1525,19 +1537,21 @@ impl ::buffa::MessageName for BoolValue { impl ::buffa::Message for BoolValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.value { - size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; + size += 1u64 + ::buffa::types::BOOL_ENCODED_LEN as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1757,19 +1771,21 @@ impl ::buffa::MessageName for StringValue { impl ::buffa::Message for StringValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.value.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -1989,19 +2005,21 @@ impl ::buffa::MessageName for BytesValue { impl ::buffa::Message for BytesValue { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.value.is_empty() { - size += 1u32 + ::buffa::types::bytes_encoded_len(&self.value) as u32; + size += 1u64 + ::buffa::types::bytes_encoded_len(&self.value) as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, diff --git a/buffa/src/error.rs b/buffa/src/error.rs index e8141314..5bf576ef 100644 --- a/buffa/src/error.rs +++ b/buffa/src/error.rs @@ -23,11 +23,15 @@ pub enum DecodeError { #[error("invalid field number")] InvalidFieldNumber, - /// The message or sub-message length exceeded the configured size limit. + /// The message or sub-message length exceeded the size limit. /// - /// By default, the limit is 2 GiB. Use [`DecodeOptions::with_max_message_size`](crate::DecodeOptions::with_max_message_size) - /// to set a lower limit for untrusted input. - #[error("message length exceeds configured size limit")] + /// By default, the limit is the 2 GiB protobuf maximum. Use + /// [`DecodeOptions::with_max_message_size`](crate::DecodeOptions::with_max_message_size) + /// to set a lower limit for untrusted input. Fallible re-encode paths + /// ([`OwnedView::from_owned`](crate::view::OwnedView::from_owned)) also + /// surface an over-limit *encode* through this variant, mirroring + /// [`EncodeError::MessageTooLarge`]. + #[error("message length exceeds the size limit (2 GiB protobuf maximum, or a configured DecodeOptions limit)")] MessageTooLarge, /// The wire type of an incoming field did not match the type expected for @@ -92,11 +96,24 @@ pub enum DecodeError { /// An error that occurred while encoding a protobuf message. /// -/// Currently uninhabited — encoding is infallible with the present -/// implementation. The type is retained and `#[non_exhaustive]` for forward -/// compatibility: if a fallible encode path is added in future (e.g. -/// `try_encode` with a fixed-capacity buffer), new variants will be added -/// here without a breaking change to the type name. +/// Returned by the `try_encode*` family +/// ([`Message::try_encode`](crate::Message::try_encode) and friends). The +/// panicking entry points ([`Message::encode`](crate::Message::encode) and +/// friends) raise the same conditions as panics instead. +/// +/// The enum is `#[non_exhaustive]`: further variants (e.g. for a +/// fixed-capacity buffer encode path) may be added without a breaking +/// change to the type name. #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] #[non_exhaustive] -pub enum EncodeError {} +pub enum EncodeError { + /// The message's encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// Encoding such a message would produce bytes that no conforming + /// protobuf decoder — including buffa's own, which returns the mirror + /// error [`DecodeError::MessageTooLarge`] — will accept. Shrink or + /// split the message instead. + #[error("message encoded size exceeds the 2 GiB protobuf limit")] + MessageTooLarge, +} diff --git a/buffa/src/extension.rs b/buffa/src/extension.rs index 96f99b9d..2a94248a 100644 --- a/buffa/src/extension.rs +++ b/buffa/src/extension.rs @@ -72,6 +72,7 @@ use core::marker::PhantomData; +use crate::error::EncodeError; use crate::unknown_fields::UnknownFields; /// Typed extension descriptor. @@ -196,8 +197,39 @@ pub trait ExtensionCodec { /// Encode `value` into the extendee's unknown fields. /// /// The caller is responsible for clearing any prior occurrences (see - /// [`ExtensionSet::set_extension`]). - fn encode(number: u32, value: Self::Value, fields: &mut UnknownFields); + /// [`ExtensionSet::set_extension`]). This is the one required encode + /// method — the fallible signature is required so that a codec whose + /// encode can fail (the message/group codecs, on an over-limit value) + /// cannot accidentally leave the fallible extension path panicking. + /// Infallible codecs (every scalar) simply end with `Ok(())`. + /// + /// Records pushed before an `Err` may remain in `fields`; + /// [`ExtensionSet::try_set_extension`] stages into a scratch set, so + /// the extendee itself is never affected by a failed set. Do not + /// implement this in terms of [`encode`](Self::encode) — that provided + /// wrapper calls back here. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if a message-typed value's + /// encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + fn try_encode( + number: u32, + value: Self::Value, + fields: &mut UnknownFields, + ) -> Result<(), EncodeError>; + + /// Panicking wrapper over [`try_encode`](Self::try_encode). + /// + /// # Panics + /// + /// Panics if `try_encode` fails — i.e. a message-typed value's encoded + /// size exceeds the 2 GiB protobuf limit. + fn encode(number: u32, value: Self::Value, fields: &mut UnknownFields) { + Self::try_encode(number, value, fields) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } } /// Implemented by codegen on every message that preserves unknown fields. @@ -253,11 +285,53 @@ pub trait ExtensionSet { /// # Panics /// /// Panics if `ext.extendee()` does not match `Self::PROTO_FQN`. + /// + /// Message-typed extension values are encoded to wire bytes on `set`, + /// so this also panics if the value's encoded size exceeds the 2 GiB + /// protobuf limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — + /// see [`try_set_extension`](Self::try_set_extension) for the + /// error-returning variant. #[track_caller] fn set_extension(&mut self, ext: &Extension, value: C::Value) { + self.try_set_extension(ext, value) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Write an extension value, replacing any prior occurrences — + /// returning an error instead of panicking if a message-typed value's + /// encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// On `Err`, the extendee's unknown fields are unchanged — prior + /// occurrences are retained. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if a message-typed value's + /// encoded size exceeds the limit. Scalar-typed extensions cannot fail. + /// + /// # Panics + /// + /// Panics if `ext.extendee()` does not match `Self::PROTO_FQN` — + /// passing an extension for the wrong message is a bug in the caller, + /// not a runtime condition. + #[track_caller] + fn try_set_extension( + &mut self, + ext: &Extension, + value: C::Value, + ) -> Result<(), EncodeError> { assert_extendee(ext, Self::PROTO_FQN); - self.unknown_fields_mut().retain(|f| f.number != ext.number); - C::encode(ext.number, value, self.unknown_fields_mut()); + // Stage into a scratch set first so `Err` leaves the extendee + // untouched (not even cleared). + let mut staged = UnknownFields::default(); + C::try_encode(ext.number, value, &mut staged)?; + let fields = self.unknown_fields_mut(); + fields.retain(|f| f.number != ext.number); + for f in staged { + fields.push(f); + } + Ok(()) } /// Returns `true` if any record at the extension's field number is present. @@ -360,7 +434,30 @@ pub mod codecs { fn decode_packed(bytes: &[u8], out: &mut Vec); /// Encode one value as a single unknown-field record. - fn encode_one(value: &Self::Value) -> UnknownFieldData; + /// + /// The one required encode method — same fallible-required contract + /// as [`ExtensionCodec::try_encode`]: infallible codecs (every + /// scalar) end with `Ok(...)`, and the message/group codecs surface + /// an over-limit value as an error. Do not implement this in terms + /// of [`encode_one`](Self::encode_one) — that provided wrapper + /// calls back here. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if a message-typed value's encoded size exceeds the 2 GiB + /// protobuf limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + fn try_encode_one(value: &Self::Value) -> Result; + + /// Panicking wrapper over [`try_encode_one`](Self::try_encode_one). + /// + /// # Panics + /// + /// Panics if `try_encode_one` fails — i.e. a message-typed value's + /// encoded size exceeds the 2 GiB protobuf limit. + fn encode_one(value: &Self::Value) -> UnknownFieldData { + Self::try_encode_one(value).unwrap_or_else(|_| crate::message::encode_size_overflow()) + } } /// Codecs whose elements can be concatenated into a packed wire form. @@ -392,11 +489,16 @@ pub mod codecs { .filter(|f| f.number == number) .find_map(|f| Self::decode_one(&f.data)) } - fn encode(number: u32, value: $ty, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: $ty, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, - data: Self::encode_one(&value), + data: Self::try_encode_one(&value)?, }); + Ok(()) } } @@ -422,9 +524,9 @@ pub mod codecs { } } } - fn encode_one($e: &$ty) -> UnknownFieldData { + fn try_encode_one($e: &$ty) -> Result { let $e = *$e; - UnknownFieldData::Varint($encode) + Ok(UnknownFieldData::Varint($encode)) } } @@ -469,8 +571,12 @@ pub mod codecs { fn decode(number: u32, fields: &UnknownFields) -> Option { Int32::decode(number, fields) } - fn encode(number: u32, value: i32, fields: &mut UnknownFields) { - Int32::encode(number, value, fields) + fn try_encode( + number: u32, + value: i32, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { + Int32::try_encode(number, value, fields) } } impl SingularCodec for EnumI32 { @@ -481,8 +587,8 @@ pub mod codecs { fn decode_packed(bytes: &[u8], out: &mut Vec) { Int32::decode_packed(bytes, out) } - fn encode_one(value: &i32) -> UnknownFieldData { - Int32::encode_one(value) + fn try_encode_one(value: &i32) -> Result { + Int32::try_encode_one(value) } } impl PackableCodec for EnumI32 { @@ -510,11 +616,16 @@ pub mod codecs { .filter(|f| f.number == number) .find_map(|f| Self::decode_one(&f.data)) } - fn encode(number: u32, value: $ty, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: $ty, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, - data: Self::encode_one(&value), + data: Self::try_encode_one(&value)?, }); + Ok(()) } } @@ -533,9 +644,9 @@ pub mod codecs { out.push($decode); } } - fn encode_one($e: &$ty) -> UnknownFieldData { + fn try_encode_one($e: &$ty) -> Result { let $e = *$e; - UnknownFieldData::Fixed32($encode) + Ok(UnknownFieldData::Fixed32($encode)) } } @@ -564,11 +675,16 @@ pub mod codecs { .filter(|f| f.number == number) .find_map(|f| Self::decode_one(&f.data)) } - fn encode(number: u32, value: $ty, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: $ty, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, - data: Self::encode_one(&value), + data: Self::try_encode_one(&value)?, }); + Ok(()) } } @@ -587,9 +703,9 @@ pub mod codecs { out.push($decode); } } - fn encode_one($e: &$ty) -> UnknownFieldData { + fn try_encode_one($e: &$ty) -> Result { let $e = *$e; - UnknownFieldData::Fixed64($encode) + Ok(UnknownFieldData::Fixed64($encode)) } } @@ -627,11 +743,16 @@ pub mod codecs { .filter(|f| f.number == number) .find_map(|f| Self::decode_one(&f.data)) } - fn encode(number: u32, value: String, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: String, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, data: UnknownFieldData::LengthDelimited(value.into_bytes()), }); + Ok(()) } } @@ -644,8 +765,10 @@ pub mod codecs { } } fn decode_packed(_bytes: &[u8], _out: &mut Vec) {} - fn encode_one(value: &String) -> UnknownFieldData { - UnknownFieldData::LengthDelimited(value.clone().into_bytes()) + fn try_encode_one(value: &String) -> Result { + Ok(UnknownFieldData::LengthDelimited( + value.clone().into_bytes(), + )) } } @@ -662,11 +785,16 @@ pub mod codecs { .filter(|f| f.number == number) .find_map(|f| Self::decode_one(&f.data)) } - fn encode(number: u32, value: Vec, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: Vec, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, data: UnknownFieldData::LengthDelimited(value), }); + Ok(()) } } @@ -679,8 +807,8 @@ pub mod codecs { } } fn decode_packed(_bytes: &[u8], _out: &mut Vec>) {} - fn encode_one(value: &Vec) -> UnknownFieldData { - UnknownFieldData::LengthDelimited(value.clone()) + fn try_encode_one(value: &Vec) -> Result { + Ok(UnknownFieldData::LengthDelimited(value.clone())) } } @@ -706,11 +834,16 @@ pub mod codecs { } msg } - fn encode(number: u32, value: M, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: M, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { fields.push(UnknownField { number, - data: UnknownFieldData::LengthDelimited(value.encode_to_vec()), + data: UnknownFieldData::LengthDelimited(value.try_encode_to_vec()?), }); + Ok(()) } } @@ -727,8 +860,10 @@ pub mod codecs { } } fn decode_packed(_bytes: &[u8], _out: &mut Vec) {} - fn encode_one(value: &M) -> UnknownFieldData { - UnknownFieldData::LengthDelimited(value.encode_to_vec()) + fn try_encode_one(value: &M) -> Result { + Ok(UnknownFieldData::LengthDelimited( + value.try_encode_to_vec()?, + )) } } @@ -767,8 +902,12 @@ pub mod codecs { } msg } - fn encode(number: u32, value: M, fields: &mut UnknownFields) { - let bytes = value.encode_to_vec(); + fn try_encode( + number: u32, + value: M, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { + let bytes = value.try_encode_to_vec()?; // We just encoded `value` — re-decoding its bytes cannot fail // unless there's a bug in the Message encoder itself. let inner = UnknownFields::decode_from_slice(&bytes) @@ -777,6 +916,7 @@ pub mod codecs { number, data: UnknownFieldData::Group(inner), }); + Ok(()) } } @@ -797,12 +937,12 @@ pub mod codecs { // Groups are never packed — leave `out` unmodified (see // `SingularCodec::decode_packed` contract). fn decode_packed(_bytes: &[u8], _out: &mut Vec) {} - fn encode_one(value: &M) -> UnknownFieldData { - let bytes = value.encode_to_vec(); + fn try_encode_one(value: &M) -> Result { + let bytes = value.try_encode_to_vec()?; // We just encoded `value` — re-decoding cannot fail. let inner = UnknownFields::decode_from_slice(&bytes) .expect("BUG: re-decoding freshly-encoded message bytes failed"); - UnknownFieldData::Group(inner) + Ok(UnknownFieldData::Group(inner)) } } @@ -823,13 +963,21 @@ pub mod codecs { fn decode(number: u32, fields: &UnknownFields) -> Vec { decode_repeated::(number, fields) } - fn encode(number: u32, value: Vec, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: Vec, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { + // Records already pushed stay in `fields` if a later element + // errs — `try_set_extension`'s scratch staging is what keeps + // the extendee itself untouched. for v in &value { fields.push(UnknownField { number, - data: C::encode_one(v), + data: C::try_encode_one(v)?, }); } + Ok(()) } } @@ -851,9 +999,13 @@ pub mod codecs { fn decode(number: u32, fields: &UnknownFields) -> Vec { decode_repeated::(number, fields) } - fn encode(number: u32, value: Vec, fields: &mut UnknownFields) { + fn try_encode( + number: u32, + value: Vec, + fields: &mut UnknownFields, + ) -> Result<(), crate::EncodeError> { if value.is_empty() { - return; + return Ok(()); } let mut buf = Vec::new(); for v in &value { @@ -863,6 +1015,7 @@ pub mod codecs { number, data: UnknownFieldData::LengthDelimited(buf), }); + Ok(()) } } @@ -1547,6 +1700,62 @@ mod tests { assert_eq!(got.b, -1); } + // ── Over-limit message values (2 GiB encode guard) ────────────────── + + use crate::test_doubles::SizedMsg; + + fn huge_msg() -> SizedMsg { + SizedMsg { + reported_size: crate::MAX_MESSAGE_BYTES + 1, + } + } + + #[test] + fn try_set_extension_over_limit_errs_and_leaves_priors() { + const E: Extension> = Extension::new(1, CARRIER); + let mut c = Carrier::default(); + c.unknown.push(ld(1, vec![0x08, 0x05])); // prior record at the number + c.unknown.push(varint(7, 9)); // unrelated record + assert_eq!( + c.try_set_extension(&E, huge_msg()), + Err(EncodeError::MessageTooLarge) + ); + // On Err the extendee is untouched: the prior record at the + // extension's number is retained, not cleared. + assert_eq!(c.unknown.iter().count(), 2); + assert!(c.unknown.iter().any(|f| f.number == 1)); + } + + #[test] + fn try_set_extension_scalar_replaces_priors() { + const E: Extension = Extension::new(3, CARRIER); + let mut c = Carrier::default(); + c.unknown.push(varint(3, 1)); + c.unknown.push(varint(3, 2)); + c.try_set_extension(&E, 42).expect("scalars cannot fail"); + assert_eq!(c.extension(&E), Some(42)); + assert_eq!(c.unknown.iter().filter(|f| f.number == 3).count(), 1); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn set_extension_over_limit_panics() { + const E: Extension> = Extension::new(1, CARRIER); + let mut c = Carrier::default(); + c.set_extension(&E, huge_msg()); + } + + #[test] + fn try_set_extension_repeated_message_over_limit_errs() { + const E: Extension>> = Extension::new(2, CARRIER); + let mut c = Carrier::default(); + assert_eq!( + c.try_set_extension(&E, vec![huge_msg()]), + Err(EncodeError::MessageTooLarge) + ); + assert_eq!(c.unknown.iter().count(), 0); + } + #[test] fn message_unknown_fields_survive_roundtrip() { // The extension message's *own* unknown fields survive get→set→get. diff --git a/buffa/src/extension_registry.rs b/buffa/src/extension_registry.rs index 2f1da3a0..fc6f57c5 100644 --- a/buffa/src/extension_registry.rs +++ b/buffa/src/extension_registry.rs @@ -656,9 +656,12 @@ pub mod helpers { M: crate::Message + Default + for<'de> serde::Deserialize<'de>, { let m: M = serde_json::from_value(v).map_err(|e| alloc::format!("field {n}: {e}"))?; + let bytes = m + .try_encode_to_vec() + .map_err(|e| alloc::format!("field {n}: {e}"))?; Ok(alloc::vec![UnknownField { number: n, - data: UnknownFieldData::LengthDelimited(m.encode_to_vec()), + data: UnknownFieldData::LengthDelimited(bytes), }]) } diff --git a/buffa/src/lib.rs b/buffa/src/lib.rs index 0677d018..1e1c16bc 100644 --- a/buffa/src/lib.rs +++ b/buffa/src/lib.rs @@ -225,6 +225,8 @@ pub mod message_field; pub mod message_set; pub mod oneof; mod size_cache; +#[cfg(test)] +pub(crate) mod test_doubles; #[cfg(feature = "text")] pub mod text; #[cfg(any(feature = "json", feature = "text"))] @@ -249,9 +251,11 @@ pub use extension::{Extension, ExtensionCodec, ExtensionSet}; pub use foldhash; pub use map_codec::{Map, MapStorage}; pub use message::{ - DecodeContext, DecodeOptions, Message, MessageName, DEFAULT_UNKNOWN_FIELD_LIMIT, - RECURSION_LIMIT, + checked_encode_size, saturate_size, DecodeContext, DecodeOptions, Message, MessageName, + DEFAULT_UNKNOWN_FIELD_LIMIT, MAX_MESSAGE_BYTES, RECURSION_LIMIT, }; +#[doc(hidden)] +pub use message::{debug_assert_two_pass, encode_size_overflow}; pub use message_field::{DefaultInstance, Inline, MessageField, ProtoBox}; pub use oneof::Oneof; pub use size_cache::{SizeCache, SizeCachePool}; diff --git a/buffa/src/map_codec.rs b/buffa/src/map_codec.rs index 78376c8c..71205d77 100644 --- a/buffa/src/map_codec.rs +++ b/buffa/src/map_codec.rs @@ -322,10 +322,14 @@ pub trait MapCodec: MapValueDecode { /// Must equal the length [`encode`](Self::encode) actually writes for /// every value — [`field_len`] sizes the buffer with it. (One reason the /// traits are sealed.) - const FIXED_LEN: Option = None; + const FIXED_LEN: Option = None; /// Encoded payload length in bytes (no tag). - fn encoded_len(value: &Self::Value) -> u32; + /// + /// `u64`, like all size arithmetic feeding `compute_size` accumulators: + /// a huge `bytes`/`string` payload must surface as an exact over-limit + /// total at the encode entry points' 2 GiB check, never wrap. + fn encoded_len(value: &Self::Value) -> u64; /// Write the payload (no tag) to `buf`. fn encode(value: &Self::Value, buf: &mut impl BufMut); @@ -356,12 +360,12 @@ macro_rules! scalar_codec { } impl MapCodec for $name { - const FIXED_LEN: Option = $fixed; + const FIXED_LEN: Option = $fixed; #[inline] #[allow(clippy::redundant_closure_call)] - fn encoded_len(value: &Self::Value) -> u32 { - ($len)(value) as u32 + fn encoded_len(value: &Self::Value) -> u64 { + ($len)(value) as u64 } #[inline] @@ -417,49 +421,49 @@ scalar_codec!( ); scalar_codec!( /// `bool` codec. - Bool, bool, WireType::Varint, Some(types::BOOL_ENCODED_LEN as u32), + Bool, bool, WireType::Varint, Some(types::BOOL_ENCODED_LEN as u64), len: |_: &bool| types::BOOL_ENCODED_LEN, encode: |v: &bool, buf: &mut _| types::encode_bool(*v, buf), decode: types::decode_bool ); scalar_codec!( /// `fixed32` codec. - Fixed32, u32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u32), + Fixed32, u32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u64), len: |_: &u32| types::FIXED32_ENCODED_LEN, encode: |v: &u32, buf: &mut _| types::encode_fixed32(*v, buf), decode: types::decode_fixed32 ); scalar_codec!( /// `fixed64` codec. - Fixed64, u64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u32), + Fixed64, u64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u64), len: |_: &u64| types::FIXED64_ENCODED_LEN, encode: |v: &u64, buf: &mut _| types::encode_fixed64(*v, buf), decode: types::decode_fixed64 ); scalar_codec!( /// `sfixed32` codec. - Sfixed32, i32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u32), + Sfixed32, i32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u64), len: |_: &i32| types::FIXED32_ENCODED_LEN, encode: |v: &i32, buf: &mut _| types::encode_sfixed32(*v, buf), decode: types::decode_sfixed32 ); scalar_codec!( /// `sfixed64` codec. - Sfixed64, i64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u32), + Sfixed64, i64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u64), len: |_: &i64| types::FIXED64_ENCODED_LEN, encode: |v: &i64, buf: &mut _| types::encode_sfixed64(*v, buf), decode: types::decode_sfixed64 ); scalar_codec!( /// `float` codec. - Float, f32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u32), + Float, f32, WireType::Fixed32, Some(types::FIXED32_ENCODED_LEN as u64), len: |_: &f32| types::FIXED32_ENCODED_LEN, encode: |v: &f32, buf: &mut _| types::encode_float(*v, buf), decode: types::decode_float ); scalar_codec!( /// `double` codec. - Double, f64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u32), + Double, f64, WireType::Fixed64, Some(types::FIXED64_ENCODED_LEN as u64), len: |_: &f64| types::FIXED64_ENCODED_LEN, encode: |v: &f64, buf: &mut _| types::encode_double(*v, buf), decode: types::decode_double @@ -513,8 +517,8 @@ impl MapValueDecode for ProtoBytesMap { impl MapCodec for ProtoBytesMap { #[inline] - fn encoded_len(value: &Self::Value) -> u32 { - types::bytes_encoded_len(value.as_ref()) as u32 + fn encoded_len(value: &Self::Value) -> u64 { + types::bytes_encoded_len(value.as_ref()) as u64 } #[inline] @@ -556,8 +560,8 @@ impl MapValueDecode for ProtoStringMap { impl MapCodec for ProtoStringMap { #[inline] - fn encoded_len(value: &Self::Value) -> u32 { - types::string_encoded_len(value.as_ref()) as u32 + fn encoded_len(value: &Self::Value) -> u64 { + types::string_encoded_len(value.as_ref()) as u64 } #[inline] @@ -589,8 +593,8 @@ impl MapValueDecode for OpenEnum { impl MapCodec for OpenEnum { #[inline] - fn encoded_len(value: &Self::Value) -> u32 { - types::int32_encoded_len(value.to_i32()) as u32 + fn encoded_len(value: &Self::Value) -> u64 { + types::int32_encoded_len(value.to_i32()) as u64 } #[inline] @@ -631,8 +635,8 @@ impl MapValueDecode for ClosedEnum { impl MapCodec for ClosedEnum { #[inline] - fn encoded_len(value: &Self::Value) -> u32 { - types::int32_encoded_len(value.to_i32()) as u32 + fn encoded_len(value: &Self::Value) -> u64 { + types::int32_encoded_len(value.to_i32()) as u64 } #[inline] @@ -664,10 +668,10 @@ impl MapValueDecode for Msg { /// Key tag (field 1) and value tag (field 2) are both single-byte for every /// wire type, so each entry carries exactly two tag bytes. -const ENTRY_TAG_LEN: u32 = 2; +const ENTRY_TAG_LEN: u64 = 2; #[inline] -fn entry_len(k: &KC::Value, v: &VC::Value) -> u32 { +fn entry_len(k: &KC::Value, v: &VC::Value) -> u64 { ENTRY_TAG_LEN + KC::encoded_len(k) + VC::encoded_len(v) } @@ -677,19 +681,18 @@ fn entry_len(k: &KC::Value, v: &VC::Value) -> u32 { /// `outer_tag_len` is the encoded length of the field's outer tag (a codegen /// constant). When both codecs are fixed-width the per-entry size is a /// compile-time constant and the loop folds to `len() * entry`. -pub fn field_len(map: &C, outer_tag_len: u32) -> u32 +pub fn field_len(map: &C, outer_tag_len: u64) -> u64 where C: MapStorage, { if let (Some(kf), Some(vf)) = (KC::FIXED_LEN, VC::FIXED_LEN) { let entry = ENTRY_TAG_LEN + kf + vf; - return map.storage_len() as u32 - * (outer_tag_len + varint_len(entry as u64) as u32 + entry); + return map.storage_len() as u64 * (outer_tag_len + varint_len(entry) as u64 + entry); } - let mut size = 0u32; + let mut size = 0u64; for (k, v) in map.storage_iter() { let entry = entry_len::(k, v); - size += outer_tag_len + varint_len(entry as u64) as u32 + entry; + size += outer_tag_len + varint_len(entry) as u64 + entry; } size } @@ -703,7 +706,7 @@ where for (k, v) in map.storage_iter() { let entry = entry_len::(k, v); Tag::new(field_number, WireType::LengthDelimited).encode(buf); - encode_varint(entry as u64, buf); + encode_varint(entry, buf); Tag::new(1, KC::WIRE_TYPE).encode(buf); KC::encode(k, buf); Tag::new(2, VC::WIRE_TYPE).encode(buf); @@ -718,19 +721,20 @@ where /// helpers iterate the same map, so the orders match by construction. pub fn message_field_len( map: &C, - outer_tag_len: u32, + outer_tag_len: u64, cache: &mut SizeCache, -) -> u32 +) -> u64 where C: MapStorage, { - let mut size = 0u32; + let mut size = 0u64; for (k, v) in map.storage_iter() { let slot = cache.reserve(); let inner = v.compute_size(cache); cache.set(slot, inner); - let entry = ENTRY_TAG_LEN + KC::encoded_len(k) + varint_len(inner as u64) as u32 + inner; - size += outer_tag_len + varint_len(entry as u64) as u32 + entry; + let entry = + ENTRY_TAG_LEN + KC::encoded_len(k) + varint_len(inner as u64) as u64 + inner as u64; + size += outer_tag_len + varint_len(entry) as u64 + entry; } size } @@ -747,9 +751,10 @@ pub fn write_message_field( { for (k, v) in map.storage_iter() { let inner = cache.consume_next(); - let entry = ENTRY_TAG_LEN + KC::encoded_len(k) + varint_len(inner as u64) as u32 + inner; + let entry = + ENTRY_TAG_LEN + KC::encoded_len(k) + varint_len(inner as u64) as u64 + inner as u64; Tag::new(field_number, WireType::LengthDelimited).encode(buf); - encode_varint(entry as u64, buf); + encode_varint(entry, buf); Tag::new(1, KC::WIRE_TYPE).encode(buf); KC::encode(k, buf); Tag::new(2, WireType::LengthDelimited).encode(buf); @@ -932,7 +937,7 @@ mod tests { fn encode_field( map: &Map, field_number: u32, - outer_tag_len: u32, + outer_tag_len: u64, ) -> Vec where KC::Value: Eq + Hash, @@ -940,7 +945,7 @@ mod tests { let len = field_len::(map, outer_tag_len); let mut buf = Vec::new(); write_field::(map, field_number, &mut buf); - assert_eq!(buf.len() as u32, len, "field_len must match written bytes"); + assert_eq!(buf.len() as u64, len, "field_len must match written bytes"); buf } @@ -1138,7 +1143,7 @@ mod tests { let len = message_field_len::(&map, 1, &mut cache); let mut wire = Vec::new(); write_message_field::(&map, 4, &mut cache, &mut wire); - assert_eq!(wire.len() as u32, len, "size pass must match write pass"); + assert_eq!(wire.len() as u64, len, "size pass must match write pass"); let back = decode_field::>(&wire); assert_eq!(back, map); diff --git a/buffa/src/message.rs b/buffa/src/message.rs index f190e015..143fcd73 100644 --- a/buffa/src/message.rs +++ b/buffa/src/message.rs @@ -8,7 +8,7 @@ use bytes::{Buf, BufMut}; -use crate::error::DecodeError; +use crate::error::{DecodeError, EncodeError}; use crate::message_field::DefaultInstance; /// Default recursion depth limit for decoding nested messages. @@ -135,6 +135,98 @@ impl<'a> DecodeContext<'a> { } } +/// Maximum encoded size of a protobuf message: 2 GiB − 1 (`0x7FFF_FFFF`). +/// +/// The protobuf specification limits any message — top-level or nested — to +/// 2 GiB. buffa enforces this symmetrically: +/// +/// - **Decode** rejects length-delimited payloads declared larger than this +/// with [`DecodeError::MessageTooLarge`], matching protobuf C++ and Java. +/// - **Encode** refuses to serialize a message whose encoded size would +/// exceed it: the panicking entry points ([`Message::encode`] and friends) +/// panic, the `try_*` twins ([`Message::try_encode`] and friends) return +/// [`EncodeError::MessageTooLarge`]. Without this check a writer could +/// produce bytes that no conforming decoder — including buffa's own — +/// will read back. +pub const MAX_MESSAGE_BYTES: u32 = 0x7FFF_FFFF; + +/// Saturate a `u64` size accumulator to `u32`. +/// +/// Generated `compute_size` implementations accumulate in `u64` (which +/// cannot overflow for any message that fits in memory) and saturate to +/// `u32` once at each message node's return. A saturated value is +/// necessarily greater than [`MAX_MESSAGE_BYTES`], so any over-limit +/// message — whether from one huge field or from aggregation — surfaces at +/// the encode entry points' size check; the byte-exact value is preserved +/// for every message the wire format can actually represent. +/// +/// Manual [`Message`] implementations should use the same pattern: +/// accumulate the encoded size in a `u64` and `saturate_size` it at return. +#[inline] +#[must_use] +pub fn saturate_size(size: u64) -> u32 { + u32::try_from(size).unwrap_or(u32::MAX) +} + +/// Validate a computed encode size against [`MAX_MESSAGE_BYTES`]. +/// +/// The error-returning half of the encode-size funnel: the `try_encode*` +/// methods (and generated inherent encode entry points, e.g. on lazy view +/// types) validate their [`compute_size`](Message::compute_size) result +/// through this before writing anything. +/// +/// # Errors +/// +/// Returns [`EncodeError::MessageTooLarge`] if `size` exceeds +/// [`MAX_MESSAGE_BYTES`]. +#[inline] +pub fn checked_encode_size(size: u32) -> Result { + if size > MAX_MESSAGE_BYTES { + Err(EncodeError::MessageTooLarge) + } else { + Ok(size) + } +} + +/// Debug-build two-pass coherence ledger: asserts `write_to` produced +/// exactly the byte count `compute_size` declared. +/// +/// Called by the provided `encode_to_vec` / `encode_to_bytes` entry points +/// (and their generated lazy-view counterparts) after the write pass. The +/// write pass is ground truth — leaf writers emit `len as u64` prefixes and +/// full payloads — so any divergence indicates a size-pass bug (wrong +/// presence check, traversal drift) in a generated or manual +/// implementation. Free in release builds. +#[doc(hidden)] +#[inline] +#[track_caller] +pub fn debug_assert_two_pass(written: usize, declared: usize) { + debug_assert_eq!( + written, declared, + "write_to produced a different byte count than compute_size \ + declared (two-pass traversal mismatch)" + ); +} + +/// Panic shim shared by every panicking encode entry point — the provided +/// `Message` / `ViewEncode` methods, `SizeCachePool`, and generated +/// lazy-view inherent methods all delegate to their `try_*` twin and route +/// the `Err` here, so the panicking and fallible paths cannot diverge. +/// +/// # Panics +/// +/// Always — that is its entire job: one cold, out-of-line panic site with +/// the canonical over-limit message. +#[doc(hidden)] +#[cold] +#[inline(never)] +pub fn encode_size_overflow() -> ! { + panic!( + "message encoded size exceeds the 2 GiB protobuf limit \ + (the try_* variant of this method returns this as an error instead)" + ) +} + /// The core trait implemented by all protobuf message types. /// /// This trait is implemented by **generated code** — you write a `.proto` file, @@ -220,10 +312,14 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { /// /// # Size limit /// - /// The protobuf specification limits messages to 2 GiB. The return type - /// is `u32`, so messages whose encoded size exceeds `u32::MAX` (4 GiB) - /// will produce a wrapped (undefined) size and a truncated encoding. - /// Stay well within the 2 GiB spec limit. + /// The protobuf specification limits messages to 2 GiB + /// ([`MAX_MESSAGE_BYTES`]). Generated implementations accumulate in + /// `u64` and saturate the return value via [`saturate_size`], so an + /// over-limit message yields a return greater than [`MAX_MESSAGE_BYTES`] + /// rather than a wrapped value; the provided encode methods check this + /// and refuse to produce over-limit output. Manual implementations must + /// follow the same pattern — if their arithmetic can wrap, over-limit + /// messages may encode corrupt bytes that bypass the check. /// /// [`SizeCache::reserve`]: crate::SizeCache::reserve /// [`SizeCache::set`]: crate::SizeCache::set @@ -233,22 +329,77 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { /// nested-message sizes from `cache` (populated by a prior /// `compute_size` call on the same cache). /// - /// Most callers should use [`encode`](Self::encode) instead. + /// Most callers should use [`encode`](Self::encode) instead. This is a + /// low-level primitive: the 2 GiB size check ([`MAX_MESSAGE_BYTES`]) + /// lives in the provided encode entry points, so callers driving + /// `compute_size` / `write_to` directly must validate the size + /// themselves (via [`checked_encode_size`]). fn write_to(&self, cache: &mut crate::SizeCache, buf: &mut impl BufMut); /// Compute size, then write. This is the primary encoding API. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]) — see [`try_encode`](Self::try_encode) for + /// the error-returning variant. + #[inline] fn encode(&self, buf: &mut impl BufMut) { + self.try_encode(buf) + .unwrap_or_else(|_| encode_size_overflow()) + } + + /// Encode, returning an error instead of panicking if the encoded size + /// exceeds the 2 GiB protobuf limit ([`MAX_MESSAGE_BYTES`]). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + fn try_encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { let mut cache = crate::SizeCache::new(); - self.compute_size(&mut cache); + checked_encode_size(self.compute_size(&mut cache))?; self.write_to(&mut cache, buf); + Ok(()) } /// Encode using a caller-supplied [`SizeCache`](crate::SizeCache), for /// reuse across many encodes in a hot loop. Clears the cache first. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]) — see + /// [`try_encode_with_cache`](Self::try_encode_with_cache) for the + /// error-returning variant. + #[inline] fn encode_with_cache(&self, cache: &mut crate::SizeCache, buf: &mut impl BufMut) { + self.try_encode_with_cache(cache, buf) + .unwrap_or_else(|_| encode_size_overflow()) + } + + /// Encode with a caller-supplied [`SizeCache`](crate::SizeCache), + /// returning an error instead of panicking if the encoded size exceeds + /// the 2 GiB protobuf limit ([`MAX_MESSAGE_BYTES`]). Clears the cache + /// first. + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + fn try_encode_with_cache( + &self, + cache: &mut crate::SizeCache, + buf: &mut impl BufMut, + ) -> Result<(), EncodeError> { cache.clear(); - self.compute_size(cache); + checked_encode_size(self.compute_size(cache))?; self.write_to(cache, buf); + Ok(()) } /// Compute the encoded byte size of this message. @@ -258,30 +409,119 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { /// [`encode_to_vec`](Self::encode_to_vec) — they do a single size pass /// and reuse the cache for the write. /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]) — see [`try_encoded_len`](Self::try_encoded_len) + /// for the error-returning variant. + /// /// [`SizeCache`]: crate::SizeCache + #[inline] #[must_use] fn encoded_len(&self) -> u32 { - self.compute_size(&mut crate::SizeCache::new()) + self.try_encoded_len() + .unwrap_or_else(|_| encode_size_overflow()) + } + + /// Compute the encoded byte size, returning an error instead of + /// panicking if it exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + fn try_encoded_len(&self) -> Result { + checked_encode_size(self.compute_size(&mut crate::SizeCache::new())) } /// Encode this message as a length-delimited byte sequence. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]); the check runs before the length prefix is + /// written, so nothing reaches `buf` on failure. See + /// [`try_encode_length_delimited`](Self::try_encode_length_delimited) + /// for the error-returning variant. + #[inline] fn encode_length_delimited(&self, buf: &mut impl BufMut) { + self.try_encode_length_delimited(buf) + .unwrap_or_else(|_| encode_size_overflow()) + } + + /// Encode as a length-delimited byte sequence, returning an error + /// instead of panicking if the encoded size exceeds the 2 GiB protobuf + /// limit ([`MAX_MESSAGE_BYTES`]). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + fn try_encode_length_delimited(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { let mut cache = crate::SizeCache::new(); - let len = self.compute_size(&mut cache); + let len = checked_encode_size(self.compute_size(&mut cache))?; crate::encoding::encode_varint(len as u64, buf); self.write_to(&mut cache, buf); + Ok(()) } /// Encode this message to a new `Vec`. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]) — see + /// [`try_encode_to_vec`](Self::try_encode_to_vec) for the + /// error-returning variant. In debug builds, also panics if a manual + /// implementation's `write_to` produces a different byte count than + /// its `compute_size` declared. + // Direct body rather than delegating to try_encode_to_vec: LLVM does + // not fold the Result> niche away even under full inlining, so + // the delegating form re-checks the capacity sentinel and round-trips + // the Vec through a Result temp in every caller — measured +7.5% on + // dense-small-message encode (google_message1, quieted metal, + // layout-normalized). Same for encode_to_bytes. The unit- and + // scalar-returning entry points delegate — their Results stay in + // registers and fold cleanly. + #[inline] #[must_use] fn encode_to_vec(&self) -> alloc::vec::Vec { let mut cache = crate::SizeCache::new(); - let size = self.compute_size(&mut cache) as usize; + let size = match checked_encode_size(self.compute_size(&mut cache)) { + Ok(size) => size as usize, + Err(_) => encode_size_overflow(), + }; let mut buf = alloc::vec::Vec::with_capacity(size); self.write_to(&mut cache, &mut buf); + debug_assert_two_pass(buf.len(), size); buf } + /// Encode to a new `Vec`, returning an error instead of panicking + /// if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + /// + /// # Panics + /// + /// In debug builds, panics if a manual implementation's `write_to` + /// produces a different byte count than its `compute_size` declared. + fn try_encode_to_vec(&self) -> Result, EncodeError> { + let mut cache = crate::SizeCache::new(); + let size = checked_encode_size(self.compute_size(&mut cache))? as usize; + let mut buf = alloc::vec::Vec::with_capacity(size); + self.write_to(&mut cache, &mut buf); + debug_assert_two_pass(buf.len(), size); + Ok(buf) + } + /// Encode this message to a new [`bytes::Bytes`]. /// /// Useful when handing off to networking code (hyper, tonic, axum) @@ -290,15 +530,53 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { /// This is equivalent to `Bytes::from(self.encode_to_vec())` — both /// are zero-copy with respect to the encoded bytes — but saves readers /// from having to know that `From> for Bytes` is zero-copy. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]) — see + /// [`try_encode_to_bytes`](Self::try_encode_to_bytes) for the + /// error-returning variant. In debug builds, also panics if a manual + /// implementation's `write_to` produces a different byte count than + /// its `compute_size` declared. + // Direct body — see encode_to_vec for why the fat-payload entry points + // do not delegate to their try_ twins. + #[inline] #[must_use] fn encode_to_bytes(&self) -> bytes::Bytes { let mut cache = crate::SizeCache::new(); - let size = self.compute_size(&mut cache) as usize; + let size = match checked_encode_size(self.compute_size(&mut cache)) { + Ok(size) => size as usize, + Err(_) => encode_size_overflow(), + }; let mut buf = bytes::BytesMut::with_capacity(size); self.write_to(&mut cache, &mut buf); + debug_assert_two_pass(buf.len(), size); buf.freeze() } + /// Encode to a new [`bytes::Bytes`], returning an error instead of + /// panicking if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`]). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`] if the encoded size exceeds + /// [`MAX_MESSAGE_BYTES`]. + /// + /// # Panics + /// + /// In debug builds, panics if a manual implementation's `write_to` + /// produces a different byte count than its `compute_size` declared. + fn try_encode_to_bytes(&self) -> Result { + let mut cache = crate::SizeCache::new(); + let size = checked_encode_size(self.compute_size(&mut cache))? as usize; + let mut buf = bytes::BytesMut::with_capacity(size); + self.write_to(&mut cache, &mut buf); + debug_assert_two_pass(buf.len(), size); + Ok(buf.freeze()) + } + /// Decode a message from a buffer. fn decode(buf: &mut impl Buf) -> Result where @@ -343,9 +621,8 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { { // Refuse messages larger than 2 GiB to prevent allocating attacker- // controlled amounts of memory from a crafted length prefix. - const MAX_MESSAGE_BYTES: u64 = 0x7FFF_FFFF; let len_u64 = crate::encoding::decode_varint(buf)?; - if len_u64 > MAX_MESSAGE_BYTES { + if len_u64 > MAX_MESSAGE_BYTES as u64 { return Err(DecodeError::MessageTooLarge); } // Safe on 32-bit: len_u64 <= 2 GiB - 1 < u32::MAX, so the cast never truncates. @@ -528,9 +805,8 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { ctx: DecodeContext<'_>, ) -> Result<(), DecodeError> { let ctx = ctx.descend()?; - const MAX_SUB_MESSAGE_BYTES: u64 = 0x7FFF_FFFF; let len_u64 = crate::encoding::decode_varint(buf)?; - if len_u64 > MAX_SUB_MESSAGE_BYTES { + if len_u64 > MAX_MESSAGE_BYTES as u64 { return Err(DecodeError::MessageTooLarge); } let len = usize::try_from(len_u64).map_err(|_| DecodeError::MessageTooLarge)?; @@ -681,9 +957,10 @@ pub struct DecodeOptions { unknown_field_limit: usize, } -/// Default maximum message size: 2 GiB - 1 (matches the internal sub-message -/// limit in `merge_length_delimited`). -const DEFAULT_MAX_MESSAGE_SIZE: usize = 0x7FFF_FFFF; +/// Default maximum message size: 2 GiB - 1 (matches the sub-message limit +/// in `merge_length_delimited` and the encode-side limit — see +/// [`MAX_MESSAGE_BYTES`]). +const DEFAULT_MAX_MESSAGE_SIZE: usize = MAX_MESSAGE_BYTES as usize; impl Default for DecodeOptions { fn default() -> Self { @@ -1732,6 +2009,163 @@ mod tests { } } + // ── Encode-side 2 GiB guard tests ────────────────────────────────── + + use crate::test_doubles::SizedMsg; + + const OVER_LIMIT: u32 = MAX_MESSAGE_BYTES + 1; + + #[test] + fn saturate_size_is_exact_below_u32_max_and_saturates_above() { + assert_eq!(saturate_size(0), 0); + assert_eq!(saturate_size(MAX_MESSAGE_BYTES as u64), MAX_MESSAGE_BYTES); + // 2–4 GiB window: exact (this is what makes the guard byte-precise). + assert_eq!(saturate_size(0x8000_0000), 0x8000_0000u32); + assert_eq!(saturate_size(u32::MAX as u64), u32::MAX); + assert_eq!(saturate_size(u32::MAX as u64 + 1), u32::MAX); + assert_eq!(saturate_size(u64::MAX), u32::MAX); + } + + #[test] + fn encode_at_exactly_max_size_is_allowed() { + // The guard is strictly-greater-than: a (fake) message of exactly + // MAX_MESSAGE_BYTES passes. `encode` has no write-count ledger, so + // the no-op write_to is fine here. + let msg = SizedMsg { + reported_size: MAX_MESSAGE_BYTES, + }; + let mut buf = alloc::vec::Vec::new(); + msg.encode(&mut buf); + assert!(msg.try_encode(&mut buf).is_ok()); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encode_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + msg.encode(&mut buf); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encoded_len_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let _ = msg.encoded_len(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encode_with_cache_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let mut cache = SizeCache::new(); + let mut buf = alloc::vec::Vec::new(); + msg.encode_with_cache(&mut cache, &mut buf); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encode_to_vec_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let _ = msg.encode_to_vec(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encode_to_bytes_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let _ = msg.encode_to_bytes(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn encode_length_delimited_over_limit_panics() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + msg.encode_length_delimited(&mut buf); + } + + #[test] + fn try_encode_over_limit_errors_and_writes_nothing() { + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + assert_eq!(msg.try_encode(&mut buf), Err(EncodeError::MessageTooLarge)); + assert!(buf.is_empty(), "no bytes may reach the buffer on Err"); + let mut cache = SizeCache::new(); + assert_eq!( + msg.try_encode_with_cache(&mut cache, &mut buf), + Err(EncodeError::MessageTooLarge) + ); + assert!(buf.is_empty(), "no bytes may reach the buffer on Err"); + assert_eq!( + msg.try_encode_length_delimited(&mut buf), + Err(EncodeError::MessageTooLarge) + ); + assert!(buf.is_empty(), "not even the length prefix"); + assert_eq!(msg.try_encode_to_vec(), Err(EncodeError::MessageTooLarge)); + assert_eq!(msg.try_encode_to_bytes(), Err(EncodeError::MessageTooLarge)); + assert_eq!(msg.try_encoded_len(), Err(EncodeError::MessageTooLarge)); + } + + #[test] + fn try_encode_matches_encode_for_normal_messages() { + let msg = FlatMsg { value: 42 }; + let mut expected = alloc::vec::Vec::new(); + msg.encode(&mut expected); + let mut actual = alloc::vec::Vec::new(); + msg.try_encode(&mut actual).unwrap(); + assert_eq!(actual, expected); + let mut cache = SizeCache::new(); + let mut cached = alloc::vec::Vec::new(); + msg.try_encode_with_cache(&mut cache, &mut cached).unwrap(); + assert_eq!(cached, expected); + assert_eq!(msg.try_encode_to_vec().unwrap(), expected); + assert_eq!(msg.try_encode_to_bytes().unwrap(), expected); + assert_eq!(msg.try_encoded_len().unwrap(), expected.len() as u32); + + let mut ld_expected = alloc::vec::Vec::new(); + msg.encode_length_delimited(&mut ld_expected); + let mut ld_actual = alloc::vec::Vec::new(); + msg.try_encode_length_delimited(&mut ld_actual).unwrap(); + assert_eq!(ld_actual, ld_expected); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn pool_encoded_len_over_limit_panics() { + // SizeCachePool::encoded_len documents itself as the pooled + // equivalent of Message::encoded_len — it must share the guard. + let msg = SizedMsg { + reported_size: OVER_LIMIT, + }; + let mut pool = crate::SizeCachePool::sequential(1024); + let _ = pool.encoded_len(&msg); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic(expected = "two-pass traversal mismatch")] + fn encode_to_vec_ledger_catches_size_write_disagreement() { + // An under-limit reported size with a no-op write_to: the guard + // passes, then the debug ledger flags the two-pass divergence. + let msg = SizedMsg { reported_size: 3 }; + let _ = msg.encode_to_vec(); + } + // ── DecodeOptions std::io::Read tests ───────────────────────────── #[cfg(feature = "std")] diff --git a/buffa/src/size_cache.rs b/buffa/src/size_cache.rs index 225dffc2..a1c3b464 100644 --- a/buffa/src/size_cache.rs +++ b/buffa/src/size_cache.rs @@ -216,6 +216,16 @@ impl SizeCache { /// `write_to` traversal diverges from `compute_size` traversal. For /// generated code this indicates a codegen bug; for manual `Message` /// implementations it indicates a traversal-order mismatch. + /// + /// In debug builds, also panics if the cached size exceeds the 2 GiB + /// protobuf limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)): + /// every slot is a sub-message length about to become a wire length + /// prefix, and no conforming decoder accepts a prefix above the limit. + /// This can only fire when `compute_size`/`write_to` are driven directly + /// with an over-limit sub-message — the provided `Message::encode*` + /// entry points reject over-limit messages before `write_to` runs, so + /// (like the two-pass byte ledger) the check is debug-only rather than a + /// release-mode branch on every nested-message write. #[inline] #[track_caller] pub fn consume_next(&mut self) -> u32 { @@ -224,7 +234,7 @@ impl SizeCache { Self::overrun(idx, self.len); } self.cursor += 1; - if idx < INLINE_CAP { + let size = if idx < INLINE_CAP { // SAFETY: `idx < self.len` (checked above) and, per the type // invariant, every inline slot at an index `< len` was initialized // by `reserve` before `len` advanced past it (and possibly @@ -232,7 +242,13 @@ impl SizeCache { unsafe { self.inline[idx].assume_init() } } else { self.spill[idx - INLINE_CAP] - } + }; + debug_assert!( + size <= crate::MAX_MESSAGE_BYTES, + "SizeCache slot holds a sub-message length of {size} bytes, \ + which exceeds the 2 GiB protobuf limit" + ); + size } #[cold] @@ -394,37 +410,125 @@ impl SizeCachePool { /// Compute a message's encoded length, reusing a pooled spill buffer. /// /// The pooled equivalent of [`Message::encoded_len`](crate::Message::encoded_len). + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)), matching the + /// non-pooled entry point. #[inline] #[must_use] pub fn encoded_len(&mut self, msg: &M) -> u32 { - let mut cache = self.acquire(); - let len = msg.compute_size(&mut cache); - self.release(cache); - len + self.try_encoded_len(msg) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) } /// Encode a message into `buf`, reusing a pooled spill buffer. /// /// The pooled equivalent of [`Message::encode`](crate::Message::encode). + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — inherited from + /// [`Message::encode_with_cache`](crate::Message::encode_with_cache). #[inline] pub fn encode(&mut self, msg: &M, buf: &mut impl bytes::BufMut) { - let mut cache = self.acquire(); - msg.encode_with_cache(&mut cache, buf); - self.release(cache); + self.try_encode(msg, buf) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) } /// Encode a borrowed message view into `buf`, reusing a pooled spill buffer. /// /// The pooled equivalent of [`ViewEncode::encode`](crate::ViewEncode::encode). + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — inherited from + /// [`ViewEncode::encode_with_cache`](crate::ViewEncode::encode_with_cache). #[inline] pub fn encode_view<'a, V: crate::ViewEncode<'a>>( &mut self, view: &V, buf: &mut impl bytes::BufMut, ) { + self.try_encode_view(view, buf) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Compute a message's encoded length, returning an error instead of + /// panicking if it exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// The pooled equivalent of + /// [`Message::try_encoded_len`](crate::Message::try_encoded_len). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + #[inline] + pub fn try_encoded_len( + &mut self, + msg: &M, + ) -> Result { + let mut cache = self.acquire(); + let len = msg.compute_size(&mut cache); + self.release(cache); + crate::message::checked_encode_size(len) + } + + /// Encode a message into `buf`, returning an error instead of panicking + /// if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). Reuses a pooled + /// spill buffer. + /// + /// The pooled equivalent of + /// [`Message::try_encode`](crate::Message::try_encode). On `Err`, + /// nothing is written to `buf` and the pooled buffer is returned to the + /// pool. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + #[inline] + pub fn try_encode( + &mut self, + msg: &M, + buf: &mut impl bytes::BufMut, + ) -> Result<(), crate::EncodeError> { let mut cache = self.acquire(); - view.encode_with_cache(&mut cache, buf); + let result = msg.try_encode_with_cache(&mut cache, buf); self.release(cache); + result + } + + /// Encode a borrowed message view into `buf`, returning an error + /// instead of panicking if the encoded size exceeds the 2 GiB protobuf + /// limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). Reuses a + /// pooled spill buffer. + /// + /// The pooled equivalent of + /// [`ViewEncode::try_encode`](crate::ViewEncode::try_encode). On `Err`, + /// nothing is written to `buf` and the pooled buffer is returned to the + /// pool. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + #[inline] + pub fn try_encode_view<'a, V: crate::ViewEncode<'a>>( + &mut self, + view: &V, + buf: &mut impl bytes::BufMut, + ) -> Result<(), crate::EncodeError> { + let mut cache = self.acquire(); + let result = view.try_encode_with_cache(&mut cache, buf); + self.release(cache); + result } } @@ -697,4 +801,26 @@ mod tests { c.set(s, 9); assert_eq!(c.consume_next(), 9); } + + #[test] + fn consume_next_allows_exactly_max_message_bytes() { + let mut c = SizeCache::new(); + let s = c.reserve(); + c.set(s, crate::MAX_MESSAGE_BYTES); + assert_eq!(c.consume_next(), crate::MAX_MESSAGE_BYTES); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic(expected = "exceeds the 2 GiB protobuf limit")] + fn consume_next_rejects_oversized_slot() { + // A slot above the limit would become an invalid wire length + // prefix. The entry points reject such messages before write_to + // runs; this debug-only backstop covers direct compute_size/write_to + // drivers. + let mut c = SizeCache::new(); + let s = c.reserve(); + c.set(s, crate::MAX_MESSAGE_BYTES + 1); + c.consume_next(); + } } diff --git a/buffa/src/test_doubles.rs b/buffa/src/test_doubles.rs new file mode 100644 index 00000000..7226798e --- /dev/null +++ b/buffa/src/test_doubles.rs @@ -0,0 +1,41 @@ +//! Shared test doubles for the crate's `#[cfg(test)]` modules. + +use bytes::{Buf, BufMut}; + +use crate::error::DecodeError; +use crate::message_field::DefaultInstance; + +/// Test double whose `compute_size` reports a caller-chosen value and whose +/// `write_to` writes nothing — lets over-limit encode paths be exercised +/// without materializing gigabytes. (buffa-types carries its own copy in +/// `any_ext.rs`; `#[cfg(test)]` items don't cross the crate boundary.) +#[derive(Clone, Debug, Default, PartialEq)] +pub(crate) struct SizedMsg { + pub(crate) reported_size: u32, +} + +impl DefaultInstance for SizedMsg { + fn default_instance() -> &'static Self { + static INST: crate::__private::OnceBox = crate::__private::OnceBox::new(); + INST.get_or_init(|| alloc::boxed::Box::new(SizedMsg::default())) + } +} + +impl crate::Message for SizedMsg { + fn compute_size(&self, _cache: &mut crate::SizeCache) -> u32 { + self.reported_size + } + fn write_to(&self, _cache: &mut crate::SizeCache, _buf: &mut impl BufMut) {} + fn merge_field( + &mut self, + tag: crate::encoding::Tag, + buf: &mut impl Buf, + _ctx: crate::DecodeContext<'_>, + ) -> Result<(), DecodeError> { + crate::encoding::skip_field(tag, buf)?; + Ok(()) + } + fn clear(&mut self) { + *self = Self::default(); + } +} diff --git a/buffa/src/type_registry.rs b/buffa/src/type_registry.rs index a634a88e..4297bec9 100644 --- a/buffa/src/type_registry.rs +++ b/buffa/src/type_registry.rs @@ -432,7 +432,7 @@ where { use alloc::format; let m: M = serde_json::from_value(v).map_err(|e| format!("{e}"))?; - Ok(m.encode_to_vec()) + m.try_encode_to_vec().map_err(|e| format!("{e}")) } // ── Text Any-entry converters (codegen points TextAnyEntry fields here) ──── @@ -477,7 +477,20 @@ where { let mut m = M::default(); dec.merge_message(&mut m)?; - Ok(m.encode_to_vec()) + m.try_encode_to_vec().map_err(|_| oversized_message_error()) +} + +/// Merged-message payload exceeded the 2 GiB protobuf limit +/// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — the re-encode step of +/// a text/JSON converter cannot produce valid wire bytes for it, so the +/// parse surfaces an error rather than panicking inside a fallible API. +#[cfg(feature = "text")] +fn oversized_message_error() -> crate::text::ParseError { + crate::text::ParseError::new( + 0, + 0, + crate::text::ParseErrorKind::Internal("merged message exceeds the 2 GiB protobuf limit"), + ) } // ── Text extension converters (codegen points TextExtEntry fields here) ──── @@ -516,9 +529,12 @@ where use crate::unknown_fields::{UnknownField, UnknownFieldData}; let mut m = M::default(); dec.merge_message(&mut m)?; + let bytes = m + .try_encode_to_vec() + .map_err(|_| oversized_message_error())?; Ok(alloc::vec![UnknownField { number: n, - data: UnknownFieldData::LengthDelimited(m.encode_to_vec()), + data: UnknownFieldData::LengthDelimited(bytes), }]) } @@ -560,7 +576,9 @@ where use crate::unknown_fields::{UnknownField, UnknownFieldData, UnknownFields}; let mut m = M::default(); dec.merge_message(&mut m)?; - let bytes = m.encode_to_vec(); + let bytes = m + .try_encode_to_vec() + .map_err(|_| oversized_message_error())?; // Freshly-encoded → re-decode cannot fail short of an encoder bug. // Surface as Internal rather than panicking so a library caller // isn't taken down by it. diff --git a/buffa/src/types.rs b/buffa/src/types.rs index 05670912..01fc2043 100644 --- a/buffa/src/types.rs +++ b/buffa/src/types.rs @@ -638,12 +638,18 @@ put_field_fn!( /// length coming from the [`SizeCache`](crate::SizeCache)) and for packed /// repeated fields (the payload loop follows). /// -/// The arguments are `(field_number, len)` — both `u32`, so transposing them -/// compiles but emits a structurally-valid-but-wrong header. +/// The arguments are `(field_number, len)` — both plain integers, so +/// transposing them can compile (integer literals infer either width) but +/// emits a structurally-valid-but-wrong header. +/// +/// `len` is `u64`, like all size arithmetic feeding the encode path; +/// callers holding a `u32` (e.g. from +/// [`SizeCache::consume_next`](crate::SizeCache::consume_next)) widen with +/// `u64::from`. #[inline(always)] -pub fn put_len_delimited_header(field_number: u32, len: u32, buf: &mut impl BufMut) { +pub fn put_len_delimited_header(field_number: u32, len: u64, buf: &mut impl BufMut) { Tag::new(field_number, WireType::LengthDelimited).encode(buf); - encode_varint(len as u64, buf); + encode_varint(len, buf); } /// Write a group field's `StartGroup` tag. diff --git a/buffa/src/view.rs b/buffa/src/view.rs index 2db653ab..794f46f7 100644 --- a/buffa/src/view.rs +++ b/buffa/src/view.rs @@ -627,22 +627,79 @@ pub trait ViewEncode<'a>: MessageView<'a> { /// nested-message sizes from `cache` (populated by a prior /// [`compute_size`](Self::compute_size) call on the same cache). /// - /// Most callers should use [`encode`](Self::encode) instead. + /// Most callers should use [`encode`](Self::encode) instead. This is a + /// low-level primitive: the 2 GiB size check + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) lives in the + /// provided encode entry points, so callers driving `compute_size` / + /// `write_to` directly must validate the size themselves (via + /// [`checked_encode_size`](crate::checked_encode_size)). fn write_to(&self, cache: &mut crate::SizeCache, buf: &mut impl BufMut); /// Compute size, then write. Primary view-encode entry point. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — see + /// [`try_encode`](Self::try_encode) for the error-returning variant. + #[inline] fn encode(&self, buf: &mut impl BufMut) { + self.try_encode(buf) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Encode, returning an error instead of panicking if the encoded size + /// exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + fn try_encode(&self, buf: &mut impl BufMut) -> Result<(), crate::EncodeError> { let mut cache = crate::SizeCache::new(); - self.compute_size(&mut cache); + crate::message::checked_encode_size(self.compute_size(&mut cache))?; self.write_to(&mut cache, buf); + Ok(()) } /// Encode using a caller-supplied [`SizeCache`](crate::SizeCache), for /// reuse across many encodes in a hot loop. Clears the cache first. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — see + /// [`try_encode_with_cache`](Self::try_encode_with_cache) for the + /// error-returning variant. + #[inline] fn encode_with_cache(&self, cache: &mut crate::SizeCache, buf: &mut impl BufMut) { + self.try_encode_with_cache(cache, buf) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Encode with a caller-supplied [`SizeCache`](crate::SizeCache), + /// returning an error instead of panicking if the encoded size exceeds + /// the 2 GiB protobuf limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// Clears the cache first. + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + fn try_encode_with_cache( + &self, + cache: &mut crate::SizeCache, + buf: &mut impl BufMut, + ) -> Result<(), crate::EncodeError> { cache.clear(); - self.compute_size(cache); + crate::message::checked_encode_size(self.compute_size(cache))?; self.write_to(cache, buf); + Ok(()) } /// Compute the encoded byte size of this view. @@ -651,38 +708,162 @@ pub trait ViewEncode<'a>: MessageView<'a> { /// [`SizeCache`](crate::SizeCache). If you also intend to encode, /// prefer [`encode`](Self::encode) or [`encode_to_vec`](Self::encode_to_vec) /// — they do a single size pass and reuse the cache for the write. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — see + /// [`try_encoded_len`](Self::try_encoded_len) for the error-returning + /// variant. + #[inline] #[must_use] fn encoded_len(&self) -> u32 { - self.compute_size(&mut crate::SizeCache::new()) + self.try_encoded_len() + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Compute the encoded byte size, returning an error instead of + /// panicking if it exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + fn try_encoded_len(&self) -> Result { + crate::message::checked_encode_size(self.compute_size(&mut crate::SizeCache::new())) } /// Encode this view as a length-delimited byte sequence. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)); the check runs + /// before the length prefix is written, so nothing reaches `buf` on + /// failure. See + /// [`try_encode_length_delimited`](Self::try_encode_length_delimited) + /// for the error-returning variant. + #[inline] fn encode_length_delimited(&self, buf: &mut impl BufMut) { + self.try_encode_length_delimited(buf) + .unwrap_or_else(|_| crate::message::encode_size_overflow()) + } + + /// Encode as a length-delimited byte sequence, returning an error + /// instead of panicking if the encoded size exceeds the 2 GiB protobuf + /// limit ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// On `Err`, nothing is written to `buf`. + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + fn try_encode_length_delimited(&self, buf: &mut impl BufMut) -> Result<(), crate::EncodeError> { let mut cache = crate::SizeCache::new(); - let len = self.compute_size(&mut cache); + let len = crate::message::checked_encode_size(self.compute_size(&mut cache))?; crate::encoding::encode_varint(len as u64, buf); self.write_to(&mut cache, buf); + Ok(()) } /// Encode this view to a new `Vec`. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — see + /// [`try_encode_to_vec`](Self::try_encode_to_vec) for the + /// error-returning variant. In debug builds, also panics if a manual + /// implementation's `write_to` produces a different byte count than + /// its `compute_size` declared. + // Direct body rather than delegating to try_encode_to_vec — the + // Result> niche survives inlining and costs measurably in + // callers; see Message::encode_to_vec for the measurement. + #[inline] #[must_use] fn encode_to_vec(&self) -> alloc::vec::Vec { let mut cache = crate::SizeCache::new(); - let size = self.compute_size(&mut cache) as usize; + let size = match crate::message::checked_encode_size(self.compute_size(&mut cache)) { + Ok(size) => size as usize, + Err(_) => crate::message::encode_size_overflow(), + }; let mut buf = alloc::vec::Vec::with_capacity(size); self.write_to(&mut cache, &mut buf); + crate::message::debug_assert_two_pass(buf.len(), size); buf } + /// Encode to a new `Vec`, returning an error instead of panicking + /// if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + /// + /// # Panics + /// + /// In debug builds, panics if a manual implementation's `write_to` + /// produces a different byte count than its `compute_size` declared. + fn try_encode_to_vec(&self) -> Result, crate::EncodeError> { + let mut cache = crate::SizeCache::new(); + let size = crate::message::checked_encode_size(self.compute_size(&mut cache))? as usize; + let mut buf = alloc::vec::Vec::with_capacity(size); + self.write_to(&mut cache, &mut buf); + crate::message::debug_assert_two_pass(buf.len(), size); + Ok(buf) + } + /// Encode this view to a new [`bytes::Bytes`]. + /// + /// # Panics + /// + /// Panics if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)) — see + /// [`try_encode_to_bytes`](Self::try_encode_to_bytes) for the + /// error-returning variant. In debug builds, also panics if a manual + /// implementation's `write_to` produces a different byte count than + /// its `compute_size` declared. + // Direct body — see Message::encode_to_vec for why the fat-payload + // entry points do not delegate to their try_ twins. + #[inline] #[must_use] fn encode_to_bytes(&self) -> Bytes { let mut cache = crate::SizeCache::new(); - let size = self.compute_size(&mut cache) as usize; + let size = match crate::message::checked_encode_size(self.compute_size(&mut cache)) { + Ok(size) => size as usize, + Err(_) => crate::message::encode_size_overflow(), + }; let mut buf = bytes::BytesMut::with_capacity(size); self.write_to(&mut cache, &mut buf); + crate::message::debug_assert_two_pass(buf.len(), size); buf.freeze() } + + /// Encode to a new [`bytes::Bytes`], returning an error instead of + /// panicking if the encoded size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)). + /// + /// # Errors + /// + /// Returns [`EncodeError::MessageTooLarge`](crate::EncodeError::MessageTooLarge) + /// if the encoded size exceeds the limit. + /// + /// # Panics + /// + /// In debug builds, panics if a manual implementation's `write_to` + /// produces a different byte count than its `compute_size` declared. + fn try_encode_to_bytes(&self) -> Result { + let mut cache = crate::SizeCache::new(); + let size = crate::message::checked_encode_size(self.compute_size(&mut cache))? as usize; + let mut buf = bytes::BytesMut::with_capacity(size); + self.write_to(&mut cache, &mut buf); + crate::message::debug_assert_two_pass(buf.len(), size); + Ok(buf.freeze()) + } } /// Provides access to a lazily-initialized default view instance. @@ -2108,10 +2289,16 @@ where /// /// # Errors /// - /// Returns [`DecodeError`] if the re-encoded bytes are somehow invalid - /// (should not happen for well-formed messages). + /// Returns [`DecodeError::MessageTooLarge`] if the message's encoded + /// size exceeds the 2 GiB protobuf limit + /// ([`MAX_MESSAGE_BYTES`](crate::MAX_MESSAGE_BYTES)), or another + /// [`DecodeError`] if the re-encoded bytes are somehow invalid (should + /// not happen for well-formed messages). pub fn from_owned(msg: &V::Owned) -> Result { - let bytes = Bytes::from(msg.encode_to_vec()); + let bytes = Bytes::from( + msg.try_encode_to_vec() + .map_err(|_| DecodeError::MessageTooLarge)?, + ); Self::decode(bytes) } @@ -3659,4 +3846,199 @@ mod tests { let cloned = rep.clone(); assert_eq!(cloned.len(), 2); } + + // ── Encode-side 2 GiB guard tests ────────────────────────────────── + + /// Test double whose `compute_size` reports a caller-chosen value and + /// whose `write_to` writes nothing — exercises the over-limit paths + /// without materializing gigabytes. + #[derive(Clone, Debug, Default, PartialEq)] + struct SizedView { + reported_size: u32, + } + + impl<'a> MessageView<'a> for SizedView { + type Owned = SimpleMessage; + fn merge_view_field( + &mut self, + _tag: crate::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + _ctx: crate::DecodeContext<'_>, + ) -> Result<&'a [u8], DecodeError> { + Ok(cur) + } + fn decode_view(_buf: &'a [u8]) -> Result { + Ok(Self::default()) + } + fn to_owned_message(&self) -> Result { + Ok(SimpleMessage::default()) + } + } + + impl<'a> ViewEncode<'a> for SizedView { + fn compute_size(&self, _cache: &mut crate::SizeCache) -> u32 { + self.reported_size + } + fn write_to(&self, _cache: &mut crate::SizeCache, _buf: &mut impl bytes::BufMut) {} + } + + const OVER_LIMIT: u32 = crate::MAX_MESSAGE_BYTES + 1; + + /// View double over the shared [`SizedMsg`](crate::test_doubles::SizedMsg) + /// owned double, so `OwnedView::from_owned` can hit the over-limit + /// encode path without materializing gigabytes. + #[derive(Clone, Debug, Default, PartialEq)] + struct SizedOwnedView; + + impl<'a> MessageView<'a> for SizedOwnedView { + type Owned = crate::test_doubles::SizedMsg; + fn merge_view_field( + &mut self, + _tag: crate::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + _ctx: crate::DecodeContext<'_>, + ) -> Result<&'a [u8], DecodeError> { + Ok(cur) + } + fn decode_view(_buf: &'a [u8]) -> Result { + Ok(Self) + } + fn to_owned_message(&self) -> Result { + Ok(crate::test_doubles::SizedMsg::default()) + } + } + + #[test] + fn owned_view_from_owned_over_limit_errs_not_panics() { + // from_owned is a Result-returning API: an over-limit message must + // surface as Err(MessageTooLarge), never a panic from the interior + // encode. + let msg = crate::test_doubles::SizedMsg { + reported_size: OVER_LIMIT, + }; + let res = OwnedView::::from_owned(&msg); + assert!(matches!(res, Err(DecodeError::MessageTooLarge))); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encode_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + view.encode(&mut buf); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encoded_len_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let _ = view.encoded_len(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encode_length_delimited_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + view.encode_length_delimited(&mut buf); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encode_to_vec_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let _ = view.encode_to_vec(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encode_to_bytes_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let _ = view.encode_to_bytes(); + } + + #[test] + #[should_panic(expected = "2 GiB protobuf limit")] + fn view_encode_with_cache_over_limit_panics() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let mut cache = crate::SizeCache::new(); + let mut buf = alloc::vec::Vec::new(); + view.encode_with_cache(&mut cache, &mut buf); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic(expected = "two-pass traversal mismatch")] + fn view_encode_to_vec_ledger_catches_size_write_disagreement() { + // An under-limit reported size with a no-op write_to: the guard + // passes, then the debug ledger flags the two-pass divergence. + let view = SizedView { reported_size: 3 }; + let _ = view.encode_to_vec(); + } + + #[test] + fn view_try_encode_over_limit_errors_and_writes_nothing() { + let view = SizedView { + reported_size: OVER_LIMIT, + }; + let mut buf = alloc::vec::Vec::new(); + assert_eq!( + view.try_encode(&mut buf), + Err(crate::EncodeError::MessageTooLarge) + ); + assert!(buf.is_empty(), "no bytes may reach the buffer on Err"); + let mut cache = crate::SizeCache::new(); + assert_eq!( + view.try_encode_with_cache(&mut cache, &mut buf), + Err(crate::EncodeError::MessageTooLarge) + ); + assert!(buf.is_empty(), "no bytes may reach the buffer on Err"); + assert_eq!( + view.try_encode_length_delimited(&mut buf), + Err(crate::EncodeError::MessageTooLarge) + ); + assert!(buf.is_empty(), "not even the length prefix"); + assert_eq!( + view.try_encode_to_vec(), + Err(crate::EncodeError::MessageTooLarge) + ); + assert_eq!( + view.try_encode_to_bytes(), + Err(crate::EncodeError::MessageTooLarge) + ); + assert_eq!( + view.try_encoded_len(), + Err(crate::EncodeError::MessageTooLarge) + ); + } + + #[test] + fn view_try_encode_matches_encode_for_normal_views() { + let view = SimpleMessageView { + id: 42, + name: "hello", + }; + let mut expected = alloc::vec::Vec::new(); + view.encode(&mut expected); + let mut actual = alloc::vec::Vec::new(); + view.try_encode(&mut actual).unwrap(); + assert_eq!(actual, expected); + assert_eq!(view.try_encode_to_vec().unwrap(), expected); + assert_eq!(view.try_encode_to_bytes().unwrap(), expected); + assert_eq!(view.try_encoded_len().unwrap(), expected.len() as u32); + } } diff --git a/docs/guide.md b/docs/guide.md index e45619a3..2619176c 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -949,7 +949,20 @@ Buffa uses a two-pass model to avoid the exponential-time size computation that ### Error handling -Encoding is **infallible** — `encode()` and `write_to()` never return errors. The buffer grows as needed via `BufMut`. +Encoding has exactly one failure condition: the protobuf specification caps +any message at **2 GiB** (`buffa::MAX_MESSAGE_BYTES`), and buffa refuses to +produce over-limit output that no conforming decoder — including its own — +would read back. `encode()`, `encode_to_vec()`, `encode_to_bytes()`, +`encode_length_delimited()`, `encode_with_cache()`, and `encoded_len()` +**panic** on an over-limit message (before anything is written); each has a +`try_`-prefixed twin +(`try_encode()`, `try_encode_with_cache()`, `try_encode_to_vec()`, +`try_encode_to_bytes()`, `try_encode_length_delimited()`, +`try_encoded_len()`) that returns `Err(EncodeError::MessageTooLarge)` +instead. Writers that can grow without +bound (logs, snapshots, accumulators) should use the `try_*` variants and +split or shrink on error. There are no other encode errors — the buffer +grows as needed via `BufMut`. Decoding returns `Result`. See [`buffa::DecodeError`](https://docs.rs/buffa/latest/buffa/enum.DecodeError.html) for the full list of variants (the enum is `#[non_exhaustive]`). Common cases: @@ -1625,6 +1638,14 @@ field_opts.set_extension(&FIELD, my_rules); field_opts.clear_extension(&FIELD); ``` +Message-typed extension values are encoded to wire bytes on `set`, so +`set_extension()` panics if the value's encoded size exceeds the 2 GiB +protobuf limit; `try_set_extension()` returns +`Err(EncodeError::MessageTooLarge)` instead and leaves the extendee +unchanged. (Scalar-typed extensions cannot fail.) `Any::pack` has the same +shape: it panics on an over-limit message, and `Any::try_pack` is the +error-returning twin. + ### Extendee identity check `extension()`, `set_extension()`, and `clear_extension()` **panic** if you @@ -2029,14 +2050,18 @@ impl Message for Int64Range { // let slot = cache.reserve(); // let inner = self.m.compute_size(cache); // cache.set(slot, inner); - let mut size = 0u32; + // + // Accumulate in u64 and saturate at return — the same pattern + // generated code uses — so an over-limit message surfaces at the + // encode entry points' 2 GiB check instead of wrapping silently. + let mut size = 0u64; if self.inner.start != 0 { - size += 1 + buffa::types::int64_encoded_len(self.inner.start) as u32; + size += 1 + buffa::types::int64_encoded_len(self.inner.start) as u64; } if self.inner.end != 0 { - size += 1 + buffa::types::int64_encoded_len(self.inner.end) as u32; + size += 1 + buffa::types::int64_encoded_len(self.inner.end) as u64; } - size + buffa::saturate_size(size) } fn write_to(&self, _cache: &mut SizeCache, buf: &mut impl bytes::BufMut) { diff --git a/docs/migration-from-prost.md b/docs/migration-from-prost.md index 3d35dbb8..2e26da40 100644 --- a/docs/migration-from-prost.md +++ b/docs/migration-from-prost.md @@ -156,13 +156,14 @@ Buffa keeps the proto `SHOUTY_SNAKE_CASE` value names as the definitive Rust var // Encode to Vec — unchanged let bytes = msg.encode_to_vec(); - // Encode to buffer — now infallible (no Result) + // Encode to buffer — no Result; panics only past the 2 GiB + // protobuf limit (try_encode is the error-returning twin) -msg.encode(&mut buf)?; +msg.encode(&mut buf); - // encoded_len — now compute_size (returns u32, caches result) + // encoded_len — same name, returns u32 (try_encoded_len for no-panic) -let len = msg.encoded_len(); -+let len = msg.compute_size() as usize; ++let len = msg.encoded_len() as usize; ``` ## 6. Decoding API diff --git a/docs/migration-from-protobuf.md b/docs/migration-from-protobuf.md index 88d0c9d2..c110d16d 100644 --- a/docs/migration-from-protobuf.md +++ b/docs/migration-from-protobuf.md @@ -95,7 +95,7 @@ The macro argument is the dotted protobuf package name. For multi-package builds // Encode -let bytes = msg.write_to_bytes()?; -+let bytes = msg.encode_to_vec(); // infallible, no Result ++let bytes = msg.encode_to_vec(); // no Result; panics only past the 2 GiB limit (see try_encode_to_vec) // Merge -msg.merge_from_bytes(&more_bytes)?; @@ -122,7 +122,7 @@ The macro argument is the dotted protobuf package name. For multi-package builds // Encode -let bytes = msg.serialize()?; -+let bytes = msg.encode_to_vec(); // infallible ++let bytes = msg.encode_to_vec(); // no Result; panics only past the 2 GiB limit ``` ## 4. Optional message fields diff --git a/examples/addressbook/Cargo.lock b/examples/addressbook/Cargo.lock index e03648ff..72144d0a 100644 --- a/examples/addressbook/Cargo.lock +++ b/examples/addressbook/Cargo.lock @@ -14,34 +14,24 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -[[package]] -name = "borsh" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" -dependencies = [ - "bytes", - "cfg_aliases", -] - [[package]] name = "buffa" -version = "0.7.1" +version = "0.8.0" dependencies = [ "bytes", - "compact_str", - "ecow", + "foldhash", "hashbrown 0.15.5", "once_cell", + "rustversion", "serde", "serde_json", - "smol_str", + "smoothutf8", "thiserror", ] [[package]] name = "buffa-build" -version = "0.7.1" +version = "0.8.0" dependencies = [ "buffa", "buffa-codegen", @@ -50,7 +40,7 @@ dependencies = [ [[package]] name = "buffa-codegen" -version = "0.7.1" +version = "0.8.0" dependencies = [ "buffa", "buffa-descriptor", @@ -63,16 +53,17 @@ dependencies = [ [[package]] name = "buffa-descriptor" -version = "0.7.1" +version = "0.8.0" dependencies = [ "buffa", + "rustversion", "serde", "serde_json", ] [[package]] name = "buffa-types" -version = "0.7.1" +version = "0.8.0" dependencies = [ "buffa", "bytes", @@ -87,47 +78,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "compact_str" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "ecow" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" - [[package]] name = "equivalent" version = "1.0.2" @@ -312,12 +268,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "semver" version = "1.0.27" @@ -368,20 +318,19 @@ dependencies = [ ] [[package]] -name = "smol_str" -version = "0.3.2" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" -dependencies = [ - "borsh", - "serde", -] +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "smoothutf8" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6b4ec95892483d6d94284caccfddb9b77111a4dcac33ed25d624248def0fa5f1" +dependencies = [ + "simdutf8", +] [[package]] name = "syn" diff --git a/examples/logging/Cargo.lock b/examples/logging/Cargo.lock index 1a8ed140..93bd6944 100644 --- a/examples/logging/Cargo.lock +++ b/examples/logging/Cargo.lock @@ -2,34 +2,24 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "borsh" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" -dependencies = [ - "bytes", - "cfg_aliases", -] - [[package]] name = "buffa" -version = "0.7.1" +version = "0.8.0" dependencies = [ "bytes", - "compact_str", - "ecow", + "foldhash", "hashbrown", "once_cell", + "rustversion", "serde", "serde_json", - "smol_str", + "smoothutf8", "thiserror", ] [[package]] name = "buffa-types" -version = "0.7.1" +version = "0.8.0" dependencies = [ "buffa", "bytes", @@ -44,47 +34,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "compact_str" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "ecow" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" - [[package]] name = "example-logging" version = "0.1.0" @@ -150,12 +99,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "serde" version = "1.0.228" @@ -199,20 +142,19 @@ dependencies = [ ] [[package]] -name = "smol_str" -version = "0.3.2" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" -dependencies = [ - "borsh", - "serde", -] +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "smoothutf8" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6b4ec95892483d6d94284caccfddb9b77111a4dcac33ed25d624248def0fa5f1" +dependencies = [ + "simdutf8", +] [[package]] name = "syn" diff --git a/examples/logging/src/gen/context.v1.context.rs b/examples/logging/src/gen/context.v1.context.rs index cb3a3a8b..dcf4799f 100644 --- a/examples/logging/src/gen/context.v1.context.rs +++ b/examples/logging/src/gen/context.v1.context.rs @@ -48,12 +48,7 @@ impl RequestContext { /// Format: `type.googleapis.com/` pub const TYPE_URL: &'static str = "type.googleapis.com/buffa.examples.context.v1.RequestContext"; } -impl ::buffa::DefaultInstance for RequestContext { - fn default_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} +::buffa::impl_default_instance!(RequestContext); impl ::buffa::MessageName for RequestContext { const PACKAGE: &'static str = "buffa.examples.context.v1"; const NAME: &'static str = "RequestContext"; @@ -63,36 +58,36 @@ impl ::buffa::MessageName for RequestContext { impl ::buffa::Message for RequestContext { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if !self.request_id.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.request_id) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.request_id) as u64; } if !self.user_id.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.user_id) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.user_id) as u64; } if !self.method.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.method) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.method) as u64; } if !self.path.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.path) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.path) as u64; } - #[allow(clippy::for_kv_map)] - for (k, v) in &self.metadata { - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 + ::buffa::types::string_encoded_len(v) as u32; - size - += 1u32 + ::buffa::encoding::varint_len(entry_size as u64) as u32 - + entry_size; - } - size += self.__buffa_unknown_fields.encoded_len() as u32; size + += ::buffa::map_codec::field_len::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.metadata, 1u64); + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -102,59 +97,22 @@ impl ::buffa::Message for RequestContext { #[allow(unused_imports)] use ::buffa::Enumeration as _; if !self.request_id.is_empty() { - ::buffa::encoding::Tag::new( - 1u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.request_id, buf); + ::buffa::types::put_string_field(1u32, &self.request_id, buf); } if !self.user_id.is_empty() { - ::buffa::encoding::Tag::new( - 2u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.user_id, buf); + ::buffa::types::put_string_field(2u32, &self.user_id, buf); } if !self.method.is_empty() { - ::buffa::encoding::Tag::new( - 3u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.method, buf); + ::buffa::types::put_string_field(3u32, &self.method, buf); } if !self.path.is_empty() { - ::buffa::encoding::Tag::new( - 4u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.path, buf); - } - for (k, v) in &self.metadata { - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 + ::buffa::types::string_encoded_len(v) as u32; - ::buffa::encoding::Tag::new( - 5u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::encoding::encode_varint(entry_size as u64, buf); - ::buffa::encoding::Tag::new( - 1u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(k, buf); - ::buffa::encoding::Tag::new( - 2u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(v, buf); + ::buffa::types::put_string_field(4u32, &self.path, buf); } + ::buffa::map_codec::write_field::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.metadata, 5u32, buf); self.__buffa_unknown_fields.write_to(buf); } fn merge_field( @@ -169,111 +127,43 @@ impl ::buffa::Message for RequestContext { use ::buffa::Enumeration as _; match tag.field_number() { 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.request_id, buf)?; } 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.user_id, buf)?; } 3u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 3u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.method, buf)?; } 4u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 4u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.path, buf)?; } 5u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 5u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - let entry_len = ::buffa::encoding::decode_varint(buf)?; - let entry_len = usize::try_from(entry_len) - .map_err(|_| ::buffa::DecodeError::MessageTooLarge)?; - if buf.remaining() < entry_len { - return ::core::result::Result::Err( - ::buffa::DecodeError::UnexpectedEof, - ); - } - let entry_limit = buf.remaining() - entry_len; - let mut key = ::core::default::Default::default(); - let mut val = ::core::default::Default::default(); - while buf.remaining() > entry_limit { - let entry_tag = ::buffa::encoding::Tag::decode(buf)?; - match entry_tag.field_number() { - 1 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - key = ::buffa::types::decode_string(buf)?; - } - 2 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - val = ::buffa::types::decode_string(buf)?; - } - _ => { - ::buffa::encoding::skip_field_depth( - entry_tag, - buf, - ctx.depth(), - )?; - } - } - } - if buf.remaining() != entry_limit { - let remaining = buf.remaining(); - if remaining > entry_limit { - buf.advance(remaining - entry_limit); - } else { - return ::core::result::Result::Err( - ::buffa::DecodeError::UnexpectedEof, - ); - } - } - self.metadata.insert(key, val); + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::map_codec::merge_entry::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&mut self.metadata, buf, ctx)?; } _ => { self.__buffa_unknown_fields diff --git a/examples/logging/src/gen/log.v1.log.rs b/examples/logging/src/gen/log.v1.log.rs index 32dea651..b9d29180 100644 --- a/examples/logging/src/gen/log.v1.log.rs +++ b/examples/logging/src/gen/log.v1.log.rs @@ -92,7 +92,10 @@ pub struct LogEntry { /// When the log entry was created. /// /// Field 1: `timestamp` - pub timestamp: ::buffa::MessageField<::buffa_types::google::protobuf::Timestamp>, + pub timestamp: ::buffa::MessageField< + ::buffa_types::google::protobuf::Timestamp, + ::buffa::Inline<::buffa_types::google::protobuf::Timestamp>, + >, /// Severity level. /// /// Field 2: `severity` @@ -108,7 +111,10 @@ pub struct LogEntry { /// Request context, if this log was produced during request handling. /// /// Field 5: `context` - pub context: ::buffa::MessageField, + pub context: ::buffa::MessageField< + super::super::context::v1::RequestContext, + ::buffa::Inline, + >, /// Structured key-value fields for machine-readable data. /// /// Field 6: `fields` @@ -138,12 +144,7 @@ impl LogEntry { /// Format: `type.googleapis.com/` pub const TYPE_URL: &'static str = "type.googleapis.com/buffa.examples.log.v1.LogEntry"; } -impl ::buffa::DefaultInstance for LogEntry { - fn default_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} +::buffa::impl_default_instance!(LogEntry); impl ::buffa::MessageName for LogEntry { const PACKAGE: &'static str = "buffa.examples.log.v1"; const NAME: &'static str = "LogEntry"; @@ -153,52 +154,52 @@ impl ::buffa::MessageName for LogEntry { impl ::buffa::Message for LogEntry { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; if self.timestamp.is_set() { let __slot = __cache.reserve(); let inner_size = self.timestamp.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } { let val = self.severity.to_i32(); if val != 0 { - size += 1u32 + ::buffa::types::int32_encoded_len(val) as u32; + size += 1u64 + ::buffa::types::int32_encoded_len(val) as u64; } } if !self.message.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.message) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.message) as u64; } if !self.logger.is_empty() { - size += 1u32 + ::buffa::types::string_encoded_len(&self.logger) as u32; + size += 1u64 + ::buffa::types::string_encoded_len(&self.logger) as u64; } if self.context.is_set() { let __slot = __cache.reserve(); let inner_size = self.context.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - #[allow(clippy::for_kv_map)] - for (k, v) in &self.fields { - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 + ::buffa::types::string_encoded_len(v) as u32; - size - += 1u32 + ::buffa::encoding::varint_len(entry_size as u64) as u32 - + entry_size; - } - size += self.__buffa_unknown_fields.encoded_len() as u32; size + += ::buffa::map_codec::field_len::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.fields, 1u64); + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -208,69 +209,38 @@ impl ::buffa::Message for LogEntry { #[allow(unused_imports)] use ::buffa::Enumeration as _; if self.timestamp.is_set() { - ::buffa::encoding::Tag::new( - 1u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::encoding::encode_varint(__cache.consume_next() as u64, buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); self.timestamp.write_to(__cache, buf); } { let val = self.severity.to_i32(); if val != 0 { - ::buffa::encoding::Tag::new(2u32, ::buffa::encoding::WireType::Varint) - .encode(buf); - ::buffa::types::encode_int32(val, buf); + ::buffa::types::put_int32_field(2u32, val, buf); } } if !self.message.is_empty() { - ::buffa::encoding::Tag::new( - 3u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.message, buf); + ::buffa::types::put_string_field(3u32, &self.message, buf); } if !self.logger.is_empty() { - ::buffa::encoding::Tag::new( - 4u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(&self.logger, buf); + ::buffa::types::put_string_field(4u32, &self.logger, buf); } if self.context.is_set() { - ::buffa::encoding::Tag::new( - 5u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::encoding::encode_varint(__cache.consume_next() as u64, buf); + ::buffa::types::put_len_delimited_header( + 5u32, + u64::from(__cache.consume_next()), + buf, + ); self.context.write_to(__cache, buf); } - for (k, v) in &self.fields { - let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 - + 1u32 + ::buffa::types::string_encoded_len(v) as u32; - ::buffa::encoding::Tag::new( - 6u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::encoding::encode_varint(entry_size as u64, buf); - ::buffa::encoding::Tag::new( - 1u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(k, buf); - ::buffa::encoding::Tag::new( - 2u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::types::encode_string(v, buf); - } + ::buffa::map_codec::write_field::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.fields, 6u32, buf); self.__buffa_unknown_fields.write_to(buf); } fn merge_field( @@ -285,13 +255,10 @@ impl ::buffa::Message for LogEntry { use ::buffa::Enumeration as _; match tag.field_number() { 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::Message::merge_length_delimited( self.timestamp.get_or_insert_default(), buf, @@ -299,45 +266,33 @@ impl ::buffa::Message for LogEntry { )?; } 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::Varint, + )?; self.severity = ::buffa::EnumValue::from( ::buffa::types::decode_int32(buf)?, ); } 3u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 3u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.message, buf)?; } 4u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 4u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::types::merge_string(&mut self.logger, buf)?; } 5u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 5u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; ::buffa::Message::merge_length_delimited( self.context.get_or_insert_default(), buf, @@ -345,71 +300,15 @@ impl ::buffa::Message for LogEntry { )?; } 6u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 6u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - let entry_len = ::buffa::encoding::decode_varint(buf)?; - let entry_len = usize::try_from(entry_len) - .map_err(|_| ::buffa::DecodeError::MessageTooLarge)?; - if buf.remaining() < entry_len { - return ::core::result::Result::Err( - ::buffa::DecodeError::UnexpectedEof, - ); - } - let entry_limit = buf.remaining() - entry_len; - let mut key = ::core::default::Default::default(); - let mut val = ::core::default::Default::default(); - while buf.remaining() > entry_limit { - let entry_tag = ::buffa::encoding::Tag::decode(buf)?; - match entry_tag.field_number() { - 1 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - key = ::buffa::types::decode_string(buf)?; - } - 2 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - val = ::buffa::types::decode_string(buf)?; - } - _ => { - ::buffa::encoding::skip_field_depth( - entry_tag, - buf, - ctx.depth(), - )?; - } - } - } - if buf.remaining() != entry_limit { - let remaining = buf.remaining(); - if remaining > entry_limit { - buf.advance(remaining - entry_limit); - } else { - return ::core::result::Result::Err( - ::buffa::DecodeError::UnexpectedEof, - ); - } - } - self.fields.insert(key, val); + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::map_codec::merge_entry::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&mut self.fields, buf, ctx)?; } _ => { self.__buffa_unknown_fields @@ -457,12 +356,7 @@ impl LogBatch { /// Format: `type.googleapis.com/` pub const TYPE_URL: &'static str = "type.googleapis.com/buffa.examples.log.v1.LogBatch"; } -impl ::buffa::DefaultInstance for LogBatch { - fn default_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} +::buffa::impl_default_instance!(LogBatch); impl ::buffa::MessageName for LogBatch { const PACKAGE: &'static str = "buffa.examples.log.v1"; const NAME: &'static str = "LogBatch"; @@ -472,24 +366,26 @@ impl ::buffa::MessageName for LogBatch { impl ::buffa::Message for LogBatch { /// Returns the total encoded size in bytes. /// - /// The result is a `u32`; the protobuf specification requires all - /// messages to fit within 2 GiB (2,147,483,647 bytes), so a - /// compliant message will never overflow this type. + /// Accumulates in `u64` (which cannot overflow for in-memory + /// data) and saturates to `u32` at return, so a message whose + /// encoded size exceeds the 2 GiB protobuf limit yields a value + /// above [`::buffa::MAX_MESSAGE_BYTES`] that the encode entry + /// points reject, never a silently wrapped size. #[allow(clippy::let_and_return)] fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { #[allow(unused_imports)] use ::buffa::Enumeration as _; - let mut size = 0u32; + let mut size = 0u64; for v in &self.entries { let __slot = __cache.reserve(); let inner_size = v.compute_size(__cache); __cache.set(__slot, inner_size); size - += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 - + inner_size; + += 1u64 + ::buffa::encoding::varint_len(inner_size as u64) as u64 + + inner_size as u64; } - size += self.__buffa_unknown_fields.encoded_len() as u32; - size + size += self.__buffa_unknown_fields.encoded_len() as u64; + ::buffa::saturate_size(size) } fn write_to( &self, @@ -499,12 +395,11 @@ impl ::buffa::Message for LogBatch { #[allow(unused_imports)] use ::buffa::Enumeration as _; for v in &self.entries { - ::buffa::encoding::Tag::new( - 1u32, - ::buffa::encoding::WireType::LengthDelimited, - ) - .encode(buf); - ::buffa::encoding::encode_varint(__cache.consume_next() as u64, buf); + ::buffa::types::put_len_delimited_header( + 1u32, + u64::from(__cache.consume_next()), + buf, + ); v.write_to(__cache, buf); } self.__buffa_unknown_fields.write_to(buf); @@ -521,13 +416,10 @@ impl ::buffa::Message for LogBatch { use ::buffa::Enumeration as _; match tag.field_number() { 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; let mut elem = ::core::default::Default::default(); ::buffa::Message::merge_length_delimited(&mut elem, buf, ctx)?; self.entries.push(elem); diff --git a/examples/logging/src/gen/mod.rs b/examples/logging/src/gen/mod.rs index b64f0d81..1935aa3a 100644 --- a/examples/logging/src/gen/mod.rs +++ b/examples/logging/src/gen/mod.rs @@ -1,25 +1,95 @@ // @generated by buffa-codegen. DO NOT EDIT. -#![allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] +#![allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception +)] -#[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] +#[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception +)] pub mod buffa { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod examples { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod context { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod v1 { use super::*; include!("buffa.examples.context.v1.mod.rs"); } } - #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod log { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod v1 { use super::*; include!("buffa.examples.log.v1.mod.rs"); diff --git a/examples/logging/src/main.rs b/examples/logging/src/main.rs index 4cfceca5..326e142d 100644 --- a/examples/logging/src/main.rs +++ b/examples/logging/src/main.rs @@ -12,7 +12,6 @@ mod gen; use buffa::Message; use gen::buffa::examples::context::v1::RequestContext; use gen::buffa::examples::log::v1::{LogBatch, LogEntry, Severity}; -use std::collections::HashMap; fn main() { let args: Vec = std::env::args().collect(); @@ -78,10 +77,12 @@ fn cmd_write(file_path: &str) { user_id: "user-42".into(), method: "POST".into(), path: "/api/v1/items".into(), - metadata: HashMap::from([ + metadata: [ ("region".into(), "us-east-1".into()), ("version".into(), "1.2.3".into()), - ]), + ] + .into_iter() + .collect(), ..Default::default() }; @@ -92,10 +93,12 @@ fn cmd_write(file_path: &str) { message: "Request received".into(), logger: "http::server".into(), context: buffa::MessageField::some(ctx.clone()), - fields: HashMap::from([ + fields: [ ("content_type".into(), "application/json".into()), ("content_length".into(), "1024".into()), - ]), + ] + .into_iter() + .collect(), ..Default::default() }, LogEntry { @@ -112,10 +115,12 @@ fn cmd_write(file_path: &str) { message: "Slow query detected".into(), logger: "db::query".into(), context: buffa::MessageField::some(ctx.clone()), - fields: HashMap::from([ + fields: [ ("query_ms".into(), "1523".into()), ("table".into(), "items".into()), - ]), + ] + .into_iter() + .collect(), ..Default::default() }, LogEntry { @@ -123,7 +128,9 @@ fn cmd_write(file_path: &str) { severity: buffa::EnumValue::Known(Severity::ERROR), message: "Failed to write to cache".into(), logger: "cache::redis".into(), - fields: HashMap::from([("error".into(), "connection refused".into())]), + fields: [("error".into(), "connection refused".into())] + .into_iter() + .collect(), ..Default::default() }, ];