From 25d0bd03752f4c5c5755d632693441633a2de2af Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Thu, 4 Jun 2026 16:53:48 -0700 Subject: [PATCH 1/3] Disable #[inline(always)] for derived structs in debug mode. --- bitcode_derive/src/decode.rs | 2 ++ bitcode_derive/src/encode.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/bitcode_derive/src/decode.rs b/bitcode_derive/src/decode.rs index c95f813..152195d 100644 --- a/bitcode_derive/src/decode.rs +++ b/bitcode_derive/src/decode.rs @@ -331,6 +331,7 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode { // Avoids bounding #impl_generics: Default. impl #decoder_impl_generics ::core::default::Default for #decoder_ty #decoder_where_clause { + #[cfg_attr(not(debug_assertions), inline(always))] fn default() -> Self { Self { #default_body @@ -339,6 +340,7 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode { } impl #decoder_impl_generics #private::View<#de> for #decoder_ty #decoder_where_clause { + #[cfg_attr(not(debug_assertions), inline(always))] fn populate(&mut self, input: &mut &#de [u8], __length: usize) -> #private::Result<()> { #populate_body Ok(()) diff --git a/bitcode_derive/src/encode.rs b/bitcode_derive/src/encode.rs index 0ddbdbc..ab643af 100644 --- a/bitcode_derive/src/encode.rs +++ b/bitcode_derive/src/encode.rs @@ -284,6 +284,7 @@ impl crate::shared::Derive<{ Item::COUNT }> for Encode { // Avoids bounding #impl_generics: Default. impl #encoder_impl_generics ::core::default::Default for #encoder_ty #encoder_where_clause { + #[cfg_attr(not(debug_assertions), inline(always))] fn default() -> Self { Self { #default_body @@ -309,10 +310,12 @@ impl crate::shared::Derive<{ Item::COUNT }> for Encode { } impl #encoder_impl_generics #private::Buffer for #encoder_ty #encoder_where_clause { + #[cfg_attr(not(debug_assertions), inline(always))] fn collect_into(&mut self, #[allow(unused)] out: &mut #private::Vec) { #collect_into_body } + #[cfg_attr(not(debug_assertions), inline(always))] fn reserve(&mut self, __additional: ::core::num::NonZeroUsize) { #reserve_body } From be347eaadc97a874462fa4b7191115565227dc62 Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Thu, 4 Jun 2026 17:06:45 -0700 Subject: [PATCH 2/3] Add indirection. --- bitcode_derive/src/decode.rs | 3 ++ bitcode_derive/src/encode.rs | 3 ++ src/derive/debug_box.rs | 77 ++++++++++++++++++++++++++++++++++++ src/derive/mod.rs | 3 ++ 4 files changed, 86 insertions(+) create mode 100644 src/derive/debug_box.rs diff --git a/bitcode_derive/src/decode.rs b/bitcode_derive/src/decode.rs index 152195d..43abe9f 100644 --- a/bitcode_derive/src/decode.rs +++ b/bitcode_derive/src/decode.rs @@ -321,6 +321,9 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode { #[allow(clippy::pedantic)] const _: () = { impl #impl_generics #private::Decode<#de> for #input_ty #where_clause { + #[cfg(debug_assertions)] + type Decoder = #private::BoxDecoder<#decoder_ty>; + #[cfg(not(debug_assertions))] type Decoder = #decoder_ty; } diff --git a/bitcode_derive/src/encode.rs b/bitcode_derive/src/encode.rs index ab643af..59aadae 100644 --- a/bitcode_derive/src/encode.rs +++ b/bitcode_derive/src/encode.rs @@ -274,6 +274,9 @@ impl crate::shared::Derive<{ Item::COUNT }> for Encode { #[allow(clippy::pedantic)] const _: () = { impl #impl_generics #private::Encode for #input_ty #where_clause { + #[cfg(debug_assertions)] + type Encoder = #private::BoxEncoder<#encoder_ty>; + #[cfg(not(debug_assertions))] type Encoder = #encoder_ty; } diff --git a/src/derive/debug_box.rs b/src/derive/debug_box.rs new file mode 100644 index 0000000..b63e876 --- /dev/null +++ b/src/derive/debug_box.rs @@ -0,0 +1,77 @@ +//! [`Box`] indirection for derived encoders/decoders, used only in debug mode +//! to avoid stack overflow. + +use crate::coder::{Buffer, Decoder, Encoder, Result, View}; +use crate::fast::{SliceImpl, Unaligned, VecImpl}; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::mem::MaybeUninit; +use core::num::NonZeroUsize; + +/// Wraps a derived [`Encoder`] in a [`Box`] and delegates to it. +pub struct BoxEncoder(Box); + +impl Default for BoxEncoder { + fn default() -> Self { + Self(Box::new(E::default())) + } +} + +impl Buffer for BoxEncoder { + fn collect_into(&mut self, out: &mut Vec) { + self.0.collect_into(out); + } + + fn reserve(&mut self, additional: NonZeroUsize) { + self.0.reserve(additional); + } +} + +impl> Encoder for BoxEncoder { + fn as_primitive(&mut self) -> Option<&mut VecImpl> + where + T: Sized, + { + self.0.as_primitive() + } + + fn encode(&mut self, t: &T) { + self.0.encode(t); + } + + fn encode_vectored<'a>(&mut self, i: impl Iterator + Clone) + where + T: 'a, + { + self.0.encode_vectored(i); + } +} + +/// Wraps a derived [`Decoder`] in a [`Box`] and delegates to it. +pub struct BoxDecoder(Box); + +impl Default for BoxDecoder { + fn default() -> Self { + Self(Box::new(D::default())) + } +} + +impl<'a, D: View<'a>> View<'a> for BoxDecoder { + fn populate(&mut self, input: &mut &'a [u8], length: usize) -> Result<()> { + self.0.populate(input, length) + } +} + +impl<'a, T, D: Decoder<'a, T>> Decoder<'a, T> for BoxDecoder { + fn as_primitive(&mut self) -> Option<&mut SliceImpl<'_, Unaligned>> { + self.0.as_primitive() + } + + fn decode(&mut self) -> T { + self.0.decode() + } + + fn decode_in_place(&mut self, out: &mut MaybeUninit) { + self.0.decode_in_place(out); + } +} diff --git a/src/derive/mod.rs b/src/derive/mod.rs index 235d711..10854ef 100644 --- a/src/derive/mod.rs +++ b/src/derive/mod.rs @@ -7,6 +7,8 @@ use core::num::NonZeroUsize; mod array; mod atomic; pub(crate) mod convert; +#[cfg(feature = "derive")] +mod debug_box; mod duration; mod empty; mod impls; @@ -27,6 +29,7 @@ pub(crate) mod vec; pub mod __private { extern crate alloc; pub use crate::coder::{uninit_field, Buffer, Decoder, Encoder, Result, View}; + pub use crate::derive::debug_box::{BoxDecoder, BoxEncoder}; pub use crate::derive::variant::{VariantDecoder, VariantEncoder}; pub use crate::derive::{Decode, Encode}; pub fn invalid_enum_variant() -> Result { From 7da87681be28f20a129e0b10bdf38660c214f312 Mon Sep 17 00:00:00 2001 From: Finn Bear Date: Thu, 4 Jun 2026 17:10:40 -0700 Subject: [PATCH 3/3] Move inline(never) to one place. --- bitcode_derive/src/decode.rs | 2 -- bitcode_derive/src/encode.rs | 3 --- src/derive/debug_box.rs | 15 +++++++++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/bitcode_derive/src/decode.rs b/bitcode_derive/src/decode.rs index 43abe9f..4e8e930 100644 --- a/bitcode_derive/src/decode.rs +++ b/bitcode_derive/src/decode.rs @@ -334,7 +334,6 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode { // Avoids bounding #impl_generics: Default. impl #decoder_impl_generics ::core::default::Default for #decoder_ty #decoder_where_clause { - #[cfg_attr(not(debug_assertions), inline(always))] fn default() -> Self { Self { #default_body @@ -343,7 +342,6 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode { } impl #decoder_impl_generics #private::View<#de> for #decoder_ty #decoder_where_clause { - #[cfg_attr(not(debug_assertions), inline(always))] fn populate(&mut self, input: &mut &#de [u8], __length: usize) -> #private::Result<()> { #populate_body Ok(()) diff --git a/bitcode_derive/src/encode.rs b/bitcode_derive/src/encode.rs index 59aadae..9f47fea 100644 --- a/bitcode_derive/src/encode.rs +++ b/bitcode_derive/src/encode.rs @@ -287,7 +287,6 @@ impl crate::shared::Derive<{ Item::COUNT }> for Encode { // Avoids bounding #impl_generics: Default. impl #encoder_impl_generics ::core::default::Default for #encoder_ty #encoder_where_clause { - #[cfg_attr(not(debug_assertions), inline(always))] fn default() -> Self { Self { #default_body @@ -313,12 +312,10 @@ impl crate::shared::Derive<{ Item::COUNT }> for Encode { } impl #encoder_impl_generics #private::Buffer for #encoder_ty #encoder_where_clause { - #[cfg_attr(not(debug_assertions), inline(always))] fn collect_into(&mut self, #[allow(unused)] out: &mut #private::Vec) { #collect_into_body } - #[cfg_attr(not(debug_assertions), inline(always))] fn reserve(&mut self, __additional: ::core::num::NonZeroUsize) { #reserve_body } diff --git a/src/derive/debug_box.rs b/src/derive/debug_box.rs index b63e876..69350da 100644 --- a/src/derive/debug_box.rs +++ b/src/derive/debug_box.rs @@ -12,22 +12,26 @@ use core::num::NonZeroUsize; pub struct BoxEncoder(Box); impl Default for BoxEncoder { + #[inline(never)] fn default() -> Self { - Self(Box::new(E::default())) + Self(Box::default()) } } impl Buffer for BoxEncoder { + #[inline(never)] fn collect_into(&mut self, out: &mut Vec) { self.0.collect_into(out); } + #[inline(never)] fn reserve(&mut self, additional: NonZeroUsize) { self.0.reserve(additional); } } impl> Encoder for BoxEncoder { + #[inline(never)] fn as_primitive(&mut self) -> Option<&mut VecImpl> where T: Sized, @@ -35,10 +39,12 @@ impl> Encoder for BoxEncoder { self.0.as_primitive() } + #[inline(never)] fn encode(&mut self, t: &T) { self.0.encode(t); } + #[inline(never)] fn encode_vectored<'a>(&mut self, i: impl Iterator + Clone) where T: 'a, @@ -51,26 +57,31 @@ impl> Encoder for BoxEncoder { pub struct BoxDecoder(Box); impl Default for BoxDecoder { + #[inline(never)] fn default() -> Self { - Self(Box::new(D::default())) + Self(Box::default()) } } impl<'a, D: View<'a>> View<'a> for BoxDecoder { + #[inline(never)] fn populate(&mut self, input: &mut &'a [u8], length: usize) -> Result<()> { self.0.populate(input, length) } } impl<'a, T, D: Decoder<'a, T>> Decoder<'a, T> for BoxDecoder { + #[inline(never)] fn as_primitive(&mut self) -> Option<&mut SliceImpl<'_, Unaligned>> { self.0.as_primitive() } + #[inline(never)] fn decode(&mut self) -> T { self.0.decode() } + #[inline(never)] fn decode_in_place(&mut self, out: &mut MaybeUninit) { self.0.decode_in_place(out); }