use {
crate::{
ReadResult, SchemaRead, SchemaReadContext, SchemaWrite, TypeMeta, WriteResult,
config::{Config, ConfigCore},
containers::decode_into_slice_t,
error::invalid_utf8_encoding,
io::{Reader, Writer},
len::SeqLen,
schema::{size_of_elem_slice, write_elem_slice_prealloc_check},
},
alloc::alloc::Layout,
bumpalo::{
Bump,
boxed::Box,
collections::{String, Vec},
},
core::{mem::MaybeUninit, slice::from_raw_parts_mut},
};
unsafe impl<'de, 'bump, C: Config, T> SchemaReadContext<'de, C, &'bump Bump> for Vec<'bump, T>
where
T: SchemaRead<'de, C>,
T::Dst: 'bump,
{
type Dst = Vec<'bump, T::Dst>;
#[inline]
fn read_with_context(
ctx: &'bump Bump,
mut reader: impl Reader<'de>,
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()> {
let len = C::LengthEncoding::read_prealloc_check::<T::Dst>(reader.by_ref())?;
let mut vec: Vec<'bump, T::Dst> = Vec::with_capacity_in(len, ctx);
let slice = unsafe { from_raw_parts_mut(vec.as_mut_ptr().cast::<MaybeUninit<_>>(), len) };
decode_into_slice_t::<T, C>(reader, slice)?;
unsafe { vec.set_len(len) };
dst.write(vec);
Ok(())
}
}
unsafe impl<'bump, C: Config, T> SchemaWrite<C> for Vec<'bump, T>
where
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = Vec<'bump, T::Src>;
#[inline]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
size_of_elem_slice::<T, C::LengthEncoding, C>(src)
}
#[inline]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
write_elem_slice_prealloc_check::<T, C::LengthEncoding, C>(writer, src)
}
}
unsafe impl<'de, 'bump, C: Config> SchemaReadContext<'de, C, &'bump Bump> for String<'bump> {
type Dst = String<'bump>;
#[inline]
fn read_with_context(
ctx: &'bump Bump,
reader: impl Reader<'de>,
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()> {
let bytes = <Vec<u8> as SchemaReadContext<C, _>>::get_with_context(ctx, reader)?;
match String::from_utf8(bytes) {
Ok(s) => {
dst.write(s);
Ok(())
}
Err(e) => Err(invalid_utf8_encoding(e.utf8_error())),
}
}
}
unsafe impl<'bump, C: Config> SchemaWrite<C> for String<'bump> {
type Src = String<'bump>;
#[inline]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
<str as SchemaWrite<C>>::size_of(src)
}
#[inline]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
C::LengthEncoding::prealloc_check::<u8>(src.len())?;
<str as SchemaWrite<C>>::write(writer, src)
}
}
unsafe impl<'de, 'bump, C: ConfigCore, T> SchemaReadContext<'de, C, &'bump Bump> for Box<'bump, T>
where
T: SchemaRead<'de, C>,
T::Dst: 'bump,
{
type Dst = Box<'bump, T::Dst>;
const TYPE_META: TypeMeta = T::TYPE_META.keep_zero_copy(false);
#[inline]
fn read_with_context(
ctx: &'bump Bump,
reader: impl Reader<'de>,
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()> {
let ptr = ctx.alloc_layout(Layout::new::<T::Dst>()).as_ptr();
T::read(reader, unsafe { &mut *ptr.cast::<MaybeUninit<T::Dst>>() })?;
let boxed = unsafe { Box::from_raw(ptr.cast::<T::Dst>()) };
dst.write(boxed);
Ok(())
}
}
unsafe impl<'bump, C: ConfigCore, T> SchemaWrite<C> for Box<'bump, T>
where
T: SchemaWrite<C>,
{
type Src = Box<'bump, T::Src>;
const TYPE_META: TypeMeta = T::TYPE_META.keep_zero_copy(false);
#[inline]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
<T as SchemaWrite<C>>::size_of(src)
}
#[inline]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
<T as SchemaWrite<C>>::write(writer, src)
}
}
#[expect(dead_code)]
fn bumpalo_vec_of_borrowed_slices_cannot_outlive_input() {}
#[expect(dead_code)]
fn bumpalo_vec_element_references_cannot_outlive_container() {}
#[expect(dead_code)]
fn bumpalo_owned_values_cannot_outlive_arena() {}
#[expect(dead_code)]
fn borrowed_values_from_bumpalo_input_cannot_outlive_input_buffer() {}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{deserialize_with_context, proptest_config::proptest_cfg, serialize},
proptest::prelude::*,
};
#[test]
fn vec_round_trip() {
proptest!(proptest_cfg(), |(val: alloc::vec::Vec<u64>)| {
let bump = Bump::new();
let val = Vec::from_iter_in(val, &bump);
let serialized = serialize(&val).unwrap();
let deserialized: Vec<u64> = deserialize_with_context(&bump, &serialized).unwrap();
prop_assert_eq!(deserialized, val);
});
}
#[test]
fn string_round_trip() {
proptest!(proptest_cfg(), |(val: alloc::string::String)| {
let bump = Bump::new();
let val = String::from_str_in(&val, &bump);
let serialized = serialize(&val).unwrap();
let deserialized: String = deserialize_with_context(&bump, &serialized).unwrap();
prop_assert_eq!(deserialized, val);
});
}
#[test]
fn box_round_trip() {
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq, proptest_derive::Arbitrary)]
#[wincode(internal)]
struct Test {
foo: u64,
bar: alloc::string::String,
baz: bool,
}
proptest!(proptest_cfg(), |(val: Test)| {
let bump = Bump::new();
let val = Box::new_in(val, &bump);
let serialized = serialize(&val).unwrap();
let deserialized: Box<Test> = deserialize_with_context(&bump, &serialized).unwrap();
prop_assert_eq!(deserialized, val);
});
}
#[test]
fn owned_string_can_outlive_input_bytes() {
let bump = Bump::new();
let deserialized: String;
{
let serialized = serialize("owned in bump").unwrap();
deserialized = deserialize_with_context(&bump, &serialized).unwrap();
}
assert_eq!(deserialized.as_str(), "owned in bump");
}
#[test]
fn copied_input_borrow_can_outlive_bumpalo_container() {
let serialized = serialize(&[b"borrowed".as_slice()][..]).unwrap();
let bytes_ref: &[u8];
{
let bump = Bump::new();
let vec: Vec<&[u8]> = deserialize_with_context(&bump, &serialized).unwrap();
bytes_ref = vec[0];
}
assert_eq!(bytes_ref, b"borrowed");
}
#[test]
fn context_derive_struct_can_outlive_input_bytes() {
#[derive(SchemaRead, SchemaWrite, Debug, PartialEq)]
#[wincode(internal, context = "&'bump Bump")]
struct Foo<'bump> {
id: u32,
#[wincode(context)]
bar: String<'bump>,
#[wincode(context)]
baz: Vec<'bump, u8>,
}
let bump = Bump::new();
let foo = Foo {
id: 42,
bar: String::from_str_in("bar", &bump),
baz: bumpalo::vec![in ≎ 1, 2, 3],
};
let deserialized: Foo = {
let serialized = serialize(&foo).unwrap();
deserialize_with_context(&bump, &serialized).unwrap()
};
assert_eq!(deserialized, foo);
}
#[test]
fn context_derive_supports_generic_fields_and_other_lifetimes() {
#[derive(SchemaRead, SchemaWrite, Debug, PartialEq)]
#[wincode(internal, context = "&'bump Bump")]
struct Foo<'bump, 'input, T> {
#[wincode(context)]
values: Vec<'bump, T>,
borrowed: &'input u8,
}
let bump = Bump::new();
let borrowed = 42;
let foo = Foo {
values: bumpalo::vec![in ≎ 1_u16, 2, 3],
borrowed: &borrowed,
};
let deserialized_values = {
let serialized = serialize(&foo).unwrap();
let deserialized: Foo<u16> = deserialize_with_context(&bump, &serialized).unwrap();
assert_eq!(deserialized.borrowed, foo.borrowed);
deserialized.values
};
assert_eq!(deserialized_values, foo.values);
}
#[test]
fn context_derive_enum_can_outlive_input_bytes() {
#[derive(SchemaRead, SchemaWrite, Debug, PartialEq)]
#[wincode(internal, context = "&'bump Bump")]
enum Foo<'bump> {
Unit,
Data {
id: u32,
#[wincode(context)]
value: String<'bump>,
},
}
let bump = Bump::new();
let foo = Foo::Data {
id: 42,
value: String::from_str_in("value", &bump),
};
let deserialized: Foo = {
let serialized = serialize(&foo).unwrap();
deserialize_with_context(&bump, &serialized).unwrap()
};
assert_eq!(deserialized, foo);
}
#[test]
fn context_derive_supports_with_adapter() {
struct StringAdapter<'bump>(core::marker::PhantomData<&'bump Bump>);
unsafe impl<'de, 'bump, C: Config> SchemaReadContext<'de, C, &'bump Bump> for StringAdapter<'bump> {
type Dst = String<'bump>;
const TYPE_META: TypeMeta =
<String<'bump> as SchemaReadContext<'de, C, &'bump Bump>>::TYPE_META;
fn read_with_context(
ctx: &'bump Bump,
reader: impl Reader<'de>,
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()> {
<String<'bump> as SchemaReadContext<'de, C, &'bump Bump>>::read_with_context(
ctx, reader, dst,
)
}
}
unsafe impl<'bump, C: Config> SchemaWrite<C> for StringAdapter<'bump> {
type Src = String<'bump>;
const TYPE_META: TypeMeta = <String<'bump> as SchemaWrite<C>>::TYPE_META;
fn size_of(src: &Self::Src) -> WriteResult<usize> {
<String<'bump> as SchemaWrite<C>>::size_of(src)
}
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
<String<'bump> as SchemaWrite<C>>::write(writer, src)
}
}
#[derive(SchemaRead, SchemaWrite, Debug, PartialEq)]
#[wincode(internal, context = "&'bump Bump")]
struct Foo<'bump> {
#[wincode(with = "StringAdapter<'bump>", context)]
value: String<'bump>,
}
let bump = Bump::new();
let foo = Foo {
value: String::from_str_in("value", &bump),
};
let deserialized: Foo = {
let serialized = serialize(&foo).unwrap();
deserialize_with_context(&bump, &serialized).unwrap()
};
assert_eq!(deserialized, foo);
}
}