#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
use alloc::sync::Arc as AllocArc;
use {
crate::{
TypeMeta,
config::ConfigCore,
error::{ReadResult, WriteResult},
io::{Reader, Writer},
len::SeqLen,
schema::{SchemaRead, SchemaWrite, size_of_elem_iter, write_elem_iter},
},
core::{
borrow::Borrow,
marker::PhantomData,
mem::{self, MaybeUninit},
ptr,
},
};
#[cfg(feature = "alloc")]
use {
crate::{
context,
error::read_length_encoding_overflow,
schema::{
SchemaReadContext, size_of_elem_slice, write_elem_iter_prealloc_check,
write_elem_slice_prealloc_check,
},
},
alloc::{
boxed::Box as AllocBox,
collections::{self, BTreeMap as AllocBTreeMap, BTreeSet as AllocBTreeSet},
rc::Rc as AllocRc,
vec,
},
};
#[cfg(feature = "std")]
use {
core::hash::{BuildHasher, Hash},
std::collections::{
HashMap as StdHashMap, HashSet as StdHashSet, hash_map::RandomState as StdRandomState,
},
};
#[cfg(feature = "alloc")]
pub struct Vec<T, Len>(PhantomData<Len>, PhantomData<T>);
#[cfg(feature = "alloc")]
pub struct VecDeque<T, Len>(PhantomData<Len>, PhantomData<T>);
#[cfg(feature = "alloc")]
pub struct Box<T: ?Sized, Len>(PhantomData<T>, PhantomData<Len>);
#[cfg(feature = "alloc")]
pub struct Rc<T: ?Sized, Len>(PhantomData<T>, PhantomData<Len>);
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub struct Arc<T: ?Sized, Len>(PhantomData<T>, PhantomData<Len>);
#[macro_export]
macro_rules! pod_wrapper {
($(unsafe struct $name:ident($type:ty);)*) => {$(
struct $name where $type: Copy + 'static;
// SAFETY:
// - By using `pod_wrapper`, user asserts that the type is zero-copy, given the contract of
// pod_wrapper:
// - The type's in‑memory representation is exactly its serialized bytes.
// - It can be safely initialized by memcpy (no validation, no endianness/layout work).
unsafe impl<C: $crate::config::ConfigCore> $crate::config::ZeroCopy<C> for $name {}
unsafe impl<C: $crate::config::ConfigCore> $crate::SchemaWrite<C> for $name {
type Src = $type;
const TYPE_META: $crate::TypeMeta = $crate::TypeMeta::Static {
size: size_of::<$type>(),
zero_copy: true,
};
#[inline]
fn size_of(_: &$type) -> $crate::WriteResult<usize> {
Ok(size_of::<$type>())
}
#[inline]
fn write(mut writer: impl $crate::io::Writer, src: &$type) -> $crate::WriteResult<()> {
unsafe {
Ok(writer.write_t(src)?)
}
}
}
unsafe impl<'de, C: $crate::config::ConfigCore> $crate::SchemaRead<'de, C> for $name {
type Dst = $type;
const TYPE_META: $crate::TypeMeta = $crate::TypeMeta::Static {
size: size_of::<$type>(),
zero_copy: true,
};
fn read(mut reader: impl $crate::io::Reader<'de>, dst: &mut core::mem::MaybeUninit<$type>) -> $crate::ReadResult<()> {
unsafe {
Ok(reader.copy_into_t(dst)?)
}
}
}
)*}
}
pub use pod_wrapper;
#[cfg(feature = "alloc")]
unsafe impl<T, Len, C: ConfigCore> SchemaWrite<C> for Vec<T, Len>
where
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = vec::Vec<T::Src>;
#[inline(always)]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
size_of_elem_slice::<T, Len, C>(src)
}
#[inline(always)]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
write_elem_slice_prealloc_check::<T, Len, C>(writer, src)
}
}
#[cfg(feature = "alloc")]
unsafe impl<'de, T, Len, C: ConfigCore> SchemaRead<'de, C> for Vec<T, Len>
where
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
{
type Dst = vec::Vec<T::Dst>;
#[inline]
fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let len = Len::read_prealloc_check::<T::Dst>(reader.by_ref())?;
<vec::Vec<T>>::read_with_context(context::Len(len), reader, dst)?;
Ok(())
}
}
pub(crate) struct SliceDropGuard<T> {
ptr: *mut MaybeUninit<T>,
initialized_len: usize,
}
impl<T> SliceDropGuard<T> {
pub(crate) fn new(ptr: *mut MaybeUninit<T>) -> Self {
Self {
ptr,
initialized_len: 0,
}
}
#[inline(always)]
#[allow(clippy::arithmetic_side_effects)]
pub(crate) fn inc_len(&mut self) {
if mem::needs_drop::<T>() {
self.initialized_len += 1;
}
}
}
impl<T> Drop for SliceDropGuard<T> {
#[cold]
fn drop(&mut self) {
if mem::needs_drop::<T>() {
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
self.ptr.cast::<T>(),
self.initialized_len,
));
}
}
}
}
#[inline]
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
unsafe fn arc_get_mut_unchecked<T: ?Sized>(arc: &mut AllocArc<T>) -> &mut T {
unsafe { &mut *AllocArc::as_ptr(arc).cast_mut() }
}
#[inline]
#[cfg(feature = "alloc")]
unsafe fn rc_get_mut_unchecked<T: ?Sized>(rc: &mut AllocRc<T>) -> &mut T {
unsafe { &mut *AllocRc::as_ptr(rc).cast_mut() }
}
macro_rules! impl_heap_slice {
($container:ident => $target:ident, |$uninit:ident| $get_slice:expr) => {
#[cfg(feature = "alloc")]
unsafe impl<T, Len, C: ConfigCore> SchemaWrite<C> for $container<[T], Len>
where
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = $target<[T::Src]>;
#[inline(always)]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
size_of_elem_slice::<T, Len, C>(src)
}
#[inline(always)]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
write_elem_slice_prealloc_check::<T, Len, C>(writer, src)
}
}
#[cfg(feature = "alloc")]
unsafe impl<'de, T, Len, C: ConfigCore> SchemaRead<'de, C> for $container<[T], Len>
where
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
{
type Dst = $target<[T::Dst]>;
#[inline(always)]
fn read(
mut reader: impl Reader<'de>,
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()> {
let len = Len::read_prealloc_check::<T::Dst>(reader.by_ref())?;
let $uninit = decode_into_container_t::<T, C, _>(
reader,
len,
$target::<[T::Dst]>::new_uninit_slice,
|$uninit| $get_slice,
)?;
let container = unsafe { $uninit.assume_init() };
dst.write(container);
Ok(())
}
}
};
}
impl_heap_slice!(Box => AllocBox, |uninit| uninit.as_mut());
impl_heap_slice!(Rc => AllocRc, |uninit| unsafe { rc_get_mut_unchecked(uninit) });
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
impl_heap_slice!(Arc => AllocArc, |uninit| unsafe { arc_get_mut_unchecked(uninit) });
#[cfg(feature = "alloc")]
unsafe impl<T, Len, C: ConfigCore> SchemaWrite<C> for VecDeque<T, Len>
where
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = collections::VecDeque<T::Src>;
#[inline(always)]
fn size_of(value: &Self::Src) -> WriteResult<usize> {
size_of_elem_iter::<T, Len, C>(value.iter())
}
#[inline(always)]
fn write(mut writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
if let TypeMeta::Static {
size,
zero_copy: true,
} = T::TYPE_META
{
#[allow(clippy::arithmetic_side_effects)]
let needed =
Len::write_bytes_needed_prealloc_check::<T::Src>(src.len())? + src.len() * size;
let mut writer = unsafe { writer.as_trusted_for(needed) }?;
Len::write(writer.by_ref(), src.len())?;
let (front, back) = src.as_slices();
unsafe {
writer.write_slice_t(front)?;
writer.write_slice_t(back)?;
}
writer.finish()?;
return Ok(());
}
write_elem_iter_prealloc_check::<T, Len, C>(writer, src.iter())
}
}
#[cfg(feature = "alloc")]
unsafe impl<'de, T, Len, C: ConfigCore> SchemaRead<'de, C> for VecDeque<T, Len>
where
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
{
type Dst = collections::VecDeque<T::Dst>;
#[inline(always)]
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let vec = <Vec<T, Len>>::get(reader)?;
dst.write(vec.into());
Ok(())
}
}
#[cfg(feature = "alloc")]
pub struct BinaryHeap<T, Len>(PhantomData<Len>, PhantomData<T>);
#[cfg(feature = "alloc")]
unsafe impl<T, Len, C: ConfigCore> SchemaWrite<C> for BinaryHeap<T, Len>
where
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = collections::BinaryHeap<T::Src>;
#[inline(always)]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
size_of_elem_slice::<T, Len, C>(src.as_slice())
}
#[inline(always)]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
write_elem_slice_prealloc_check::<T, Len, C>(writer, src.as_slice())
}
}
#[cfg(feature = "alloc")]
unsafe impl<'de, T, Len, C: ConfigCore> SchemaRead<'de, C> for BinaryHeap<T, Len>
where
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
T::Dst: Ord,
{
type Dst = collections::BinaryHeap<T::Dst>;
#[inline(always)]
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let vec = <Vec<T, Len>>::get(reader)?;
dst.write(collections::BinaryHeap::from(vec));
Ok(())
}
}
pub trait DuplicateKeyPolicy: sealed::Sealed {
fn check(collided: bool) -> ReadResult<()>;
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::AllowDuplicateKeys {}
impl Sealed for super::CheckUniqueKeys {}
}
pub struct AllowDuplicateKeys;
impl DuplicateKeyPolicy for AllowDuplicateKeys {
#[inline(always)]
fn check(_: bool) -> ReadResult<()> {
Ok(())
}
}
pub struct CheckUniqueKeys;
impl DuplicateKeyPolicy for CheckUniqueKeys {
#[inline(always)]
fn check(collided: bool) -> ReadResult<()> {
if collided {
return Err(crate::error::duplicate_key());
}
Ok(())
}
}
#[cfg(feature = "alloc")]
macro_rules! seq_capacity {
($key: ty, $len: expr) => {
$len
};
($key: ty, $len: expr, cap_unique_keys) => {
$crate::len::unique_key_capacity::<$key>($len)
};
}
#[cfg(feature = "alloc")]
pub(crate) use seq_capacity;
#[cfg(feature = "alloc")]
#[inline]
pub(crate) fn read_kv_seq<'de, K, V, Len, C, M>(
mut reader: impl Reader<'de>,
dst: &mut MaybeUninit<M>,
make: impl FnOnce(usize) -> M,
mut insert: impl FnMut(&mut M, K::Dst, V::Dst) -> ReadResult<()>,
) -> ReadResult<()>
where
C: ConfigCore,
Len: SeqLen<C>,
K: SchemaRead<'de, C>,
V: SchemaRead<'de, C>,
{
let len = Len::read_prealloc_check::<(K::Dst, V::Dst)>(reader.by_ref())?;
macro_rules! read_entries {
($reader:expr) => {{
let mut map = make(len);
for _ in 0..len {
let k = K::get($reader.by_ref())?;
let v = V::get($reader.by_ref())?;
insert(&mut map, k, v)?;
}
map
}};
}
let map = if let (
TypeMeta::Static { size: key_size, .. },
TypeMeta::Static {
size: value_size, ..
},
) = (K::TYPE_META, V::TYPE_META)
{
let Some(el_size) = key_size.checked_add(value_size) else {
return Err(read_length_encoding_overflow("usize::MAX"));
};
let mut reader = unsafe { reader.as_trusted_for_seq(len, el_size) }?;
read_entries!(reader)
} else {
read_entries!(reader)
};
dst.write(map);
Ok(())
}
#[cfg(feature = "alloc")]
#[inline]
pub(crate) fn read_elem_seq<'de, T, Len, C, S>(
mut reader: impl Reader<'de>,
dst: &mut MaybeUninit<S>,
make: impl FnOnce(usize) -> S,
mut insert: impl FnMut(&mut S, T::Dst) -> ReadResult<()>,
) -> ReadResult<()>
where
C: ConfigCore,
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
{
let len = Len::read_prealloc_check::<T::Dst>(reader.by_ref())?;
macro_rules! read_elems {
($reader:expr) => {{
let mut set = make(len);
for _ in 0..len {
insert(&mut set, T::get($reader.by_ref())?)?;
}
set
}};
}
let set = match T::TYPE_META {
TypeMeta::Static { size, .. } => {
let mut reader = unsafe { reader.as_trusted_for_seq(len, size) }?;
read_elems!(reader)
}
TypeMeta::Dynamic => read_elems!(reader),
};
dst.write(set);
Ok(())
}
macro_rules! map_container {
(@struct $(#[cfg($cfg:meta)])? $(#[doc = $doc:expr])* $name:ident<$($generic:ident),*>) => {
$(#[doc = $doc])*
$(#[cfg($cfg)])?
pub struct $name<$($generic,)* Len, Dup = $crate::containers::AllowDuplicateKeys>(
core::marker::PhantomData<($($generic,)* Len, Dup)>,
);
};
(@struct $(#[cfg($cfg:meta)])? $(#[doc = $doc:expr])* $name:ident<$($generic:ident),*>, $state:ident = $state_default:ty) => {
$(#[doc = $doc])*
#[cfg(all($($cfg,)? feature = "std"))]
pub struct $name<
$($generic,)*
Len,
$state = $state_default,
Dup = $crate::containers::AllowDuplicateKeys,
>(core::marker::PhantomData<($($generic,)* Len, $state, Dup)>);
$(#[doc = $doc])*
#[cfg(all($($cfg,)? not(feature = "std")))]
pub struct $name<$($generic,)* Len, $state, Dup = $crate::containers::AllowDuplicateKeys>(
core::marker::PhantomData<($($generic,)* Len, $state, Dup)>,
);
};
(
$(#[cfg($cfg:meta)])?
$(#[doc = $doc:expr])*
$name:ident => $target:ident<$key:ident : $($constraint:path)|*, $value:ident
$(, $state:ident : $($state_constraint:path)|* = $state_default:ty)?>,
$with_capacity:expr
$(, $cap_unique_keys:ident)?
) => {
$crate::containers::map_container! {
@struct $(#[cfg($cfg)])? $(#[doc = $doc])* $name<$key, $value> $(, $state = $state_default)?
}
$(#[cfg($cfg)])?
unsafe impl<C: $crate::config::ConfigCore, $key, $value, Len, Dup $(, $state)?>
$crate::SchemaWrite<C> for $name<$key, $value, Len $(, $state)?, Dup>
where
Len: $crate::len::SeqLen<C>,
$key: $crate::SchemaWrite<C, Src: Sized>,
$value: $crate::SchemaWrite<C, Src: Sized>,
$($($state: $state_constraint,)*)?
{
type Src = $target<$key::Src, $value::Src $(, $state)?>;
#[inline]
#[allow(clippy::arithmetic_side_effects)]
fn size_of(src: &Self::Src) -> $crate::WriteResult<usize> {
let len = src.len();
if let ($crate::TypeMeta::Static { size: key_size, .. },
$crate::TypeMeta::Static { size: value_size, .. }) =
(<$key as $crate::SchemaWrite<C>>::TYPE_META, <$value as $crate::SchemaWrite<C>>::TYPE_META)
{
return Ok(Len::write_bytes_needed(len)? + (key_size + value_size) * len);
}
src.iter().try_fold(Len::write_bytes_needed(len)?, |acc, (k, v)| {
Ok::<_, $crate::WriteError>(acc + $key::size_of(k)? + $value::size_of(v)?)
})
}
#[inline]
fn write(mut writer: impl $crate::io::Writer, src: &Self::Src) -> $crate::WriteResult<()> {
use $crate::io::Writer as _;
let len = src.len();
Len::prealloc_check::<($key::Src, $value::Src)>(len)?;
macro_rules! write_entries {
($w:expr) => {{
Len::write($w.by_ref(), len)?;
for (k, v) in src {
$key::write($w.by_ref(), k)?;
$value::write($w.by_ref(), v)?;
}
}};
}
if let ($crate::TypeMeta::Static { size: key_size, .. },
$crate::TypeMeta::Static { size: value_size, .. }) =
(<$key as $crate::SchemaWrite<C>>::TYPE_META, <$value as $crate::SchemaWrite<C>>::TYPE_META)
{
#[allow(clippy::arithmetic_side_effects)]
let needed = Len::write_bytes_needed(len)? + (key_size + value_size) * len;
let mut writer = unsafe { writer.as_trusted_for(needed) }?;
write_entries!(writer);
writer.finish()?;
return Ok(());
}
write_entries!(writer);
Ok(())
}
}
$(#[cfg($cfg)])?
unsafe impl<'de, C: $crate::config::ConfigCore, $key, $value, Len, Dup $(, $state)?>
$crate::SchemaRead<'de, C> for $name<$key, $value, Len $(, $state)?, Dup>
where
Len: $crate::len::SeqLen<C>,
Dup: $crate::containers::DuplicateKeyPolicy,
$key: $crate::SchemaRead<'de, C>,
$value: $crate::SchemaRead<'de, C>,
$($key::Dst: $constraint,)*
$($($state: $state_constraint,)*)?
{
type Dst = $target<$key::Dst, $value::Dst $(, $state)?>;
#[inline]
fn read(
reader: impl $crate::io::Reader<'de>,
dst: &mut core::mem::MaybeUninit<Self::Dst>,
) -> $crate::ReadResult<()> {
$crate::containers::read_kv_seq::<$key, $value, Len, C, _>(
reader,
dst,
|len| $with_capacity(
$crate::containers::seq_capacity!($key::Dst, len $(, $cap_unique_keys)?)
$(, <$state as Default>::default())?
),
|map, k, v| Dup::check(map.insert(k, v).is_some()),
)
}
}
$(#[cfg($cfg)])?
unsafe impl<C: $crate::config::Config, $key, $value $(, $state)?> $crate::SchemaWrite<C>
for $target<$key, $value $(, $state)?>
where
$key: $crate::SchemaWrite<C, Src: Sized>,
$value: $crate::SchemaWrite<C, Src: Sized>,
$($($state: $state_constraint,)*)?
{
type Src = $target<$key::Src, $value::Src $(, $state)?>;
#[inline]
fn size_of(src: &Self::Src) -> $crate::WriteResult<usize> {
<$name<$key, $value, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaWrite<C>>::size_of(src)
}
#[inline]
fn write(writer: impl $crate::io::Writer, src: &Self::Src) -> $crate::WriteResult<()> {
<$name<$key, $value, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaWrite<C>>::write(writer, src)
}
}
$(#[cfg($cfg)])?
unsafe impl<'de, C: $crate::config::Config, $key, $value $(, $state)?> $crate::SchemaRead<'de, C>
for $target<$key, $value $(, $state)?>
where
$key: $crate::SchemaRead<'de, C>,
$value: $crate::SchemaRead<'de, C>,
$($key::Dst: $constraint,)*
$($($state: $state_constraint,)*)?
{
type Dst = $target<$key::Dst, $value::Dst $(, $state)?>;
#[inline]
fn read(
reader: impl $crate::io::Reader<'de>,
dst: &mut core::mem::MaybeUninit<Self::Dst>,
) -> $crate::ReadResult<()> {
<$name<$key, $value, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaRead<'de, C>>::read(reader, dst)
}
}
};
}
pub(crate) use map_container;
macro_rules! set_container {
(
$(#[cfg($cfg:meta)])?
$(#[doc = $doc:expr])*
$name:ident => $target:ident<$key:ident : $($constraint:path)|*
$(, $state:ident : $($state_constraint:path)|* = $state_default:ty)?>,
$with_capacity:expr
$(, $cap_unique_keys:ident)?
) => {
$crate::containers::map_container! {
@struct $(#[cfg($cfg)])? $(#[doc = $doc])* $name<$key> $(, $state = $state_default)?
}
$(#[cfg($cfg)])?
unsafe impl<C: $crate::config::ConfigCore, $key, Len, Dup $(, $state)?>
$crate::SchemaWrite<C> for $name<$key, Len $(, $state)?, Dup>
where
Len: $crate::len::SeqLen<C>,
$key: $crate::SchemaWrite<C, Src: Sized>,
$($($state: $state_constraint,)*)?
{
type Src = $target<$key::Src $(, $state)?>;
#[inline]
fn size_of(src: &Self::Src) -> $crate::WriteResult<usize> {
$crate::schema::size_of_elem_iter::<$key, Len, C>(src.iter())
}
#[inline]
fn write(writer: impl $crate::io::Writer, src: &Self::Src) -> $crate::WriteResult<()> {
$crate::schema::write_elem_iter_prealloc_check::<$key, Len, C>(writer, src.iter())
}
}
$(#[cfg($cfg)])?
unsafe impl<'de, C: $crate::config::ConfigCore, $key, Len, Dup $(, $state)?>
$crate::SchemaRead<'de, C> for $name<$key, Len $(, $state)?, Dup>
where
Len: $crate::len::SeqLen<C>,
Dup: $crate::containers::DuplicateKeyPolicy,
$key: $crate::SchemaRead<'de, C>,
$($key::Dst: $constraint,)*
$($($state: $state_constraint,)*)?
{
type Dst = $target<$key::Dst $(, $state)?>;
#[inline]
fn read(
reader: impl $crate::io::Reader<'de>,
dst: &mut core::mem::MaybeUninit<Self::Dst>,
) -> $crate::ReadResult<()> {
$crate::containers::read_elem_seq::<$key, Len, C, _>(
reader,
dst,
|len| $with_capacity(
$crate::containers::seq_capacity!($key::Dst, len $(, $cap_unique_keys)?)
$(, <$state as Default>::default())?
),
|set, k| Dup::check(!set.insert(k)),
)
}
}
$(#[cfg($cfg)])?
unsafe impl<C: $crate::config::Config, $key $(, $state)?> $crate::SchemaWrite<C>
for $target<$key $(, $state)?>
where
$key: $crate::SchemaWrite<C, Src: Sized>,
$($($state: $state_constraint,)*)?
{
type Src = $target<$key::Src $(, $state)?>;
#[inline]
fn size_of(src: &Self::Src) -> $crate::WriteResult<usize> {
<$name<$key, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaWrite<C>>::size_of(src)
}
#[inline]
fn write(writer: impl $crate::io::Writer, src: &Self::Src) -> $crate::WriteResult<()> {
<$name<$key, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaWrite<C>>::write(writer, src)
}
}
$(#[cfg($cfg)])?
unsafe impl<'de, C: $crate::config::Config, $key $(, $state)?> $crate::SchemaRead<'de, C>
for $target<$key $(, $state)?>
where
$key: $crate::SchemaRead<'de, C>,
$($key::Dst: $constraint,)*
$($($state: $state_constraint,)*)?
{
type Dst = $target<$key::Dst $(, $state)?>;
#[inline]
fn read(
reader: impl $crate::io::Reader<'de>,
dst: &mut core::mem::MaybeUninit<Self::Dst>,
) -> $crate::ReadResult<()> {
<$name<$key, C::LengthEncoding $(, $state)?, $crate::containers::AllowDuplicateKeys> as $crate::SchemaRead<'de, C>>::read(reader, dst)
}
}
};
}
#[cfg(feature = "indexmap")]
pub(crate) use set_container;
map_container! {
#[cfg(feature = "std")]
HashMap => StdHashMap<K: Hash | Eq, V, S: BuildHasher | Default = StdRandomState>,
StdHashMap::with_capacity_and_hasher,
cap_unique_keys
}
map_container! {
#[cfg(feature = "alloc")]
BTreeMap => AllocBTreeMap<K: Ord, V>,
|_| AllocBTreeMap::new()
}
set_container! {
#[cfg(feature = "std")]
HashSet => StdHashSet<K: Hash | Eq, S: BuildHasher | Default = StdRandomState>,
StdHashSet::with_capacity_and_hasher,
cap_unique_keys
}
set_container! {
#[cfg(feature = "alloc")]
BTreeSet => AllocBTreeSet<K: Ord>,
|_| AllocBTreeSet::new()
}
#[cfg(feature = "indexmap")]
pub use super::external::indexmap::{IndexMap, IndexSet};
struct ResultPrealloc<T, E>(Result<T, E>);
impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for ResultPrealloc<V, E> {
fn from_iter<I: IntoIterator<Item = Result<A, E>>>(iter: I) -> ResultPrealloc<V, E> {
struct Iter<I, E> {
inner: I,
error: Option<E>,
}
impl<I: Iterator<Item = Result<T, E>>, T, E> Iterator for Iter<I, E> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()?.map_err(|e| self.error = Some(e)).ok()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
let mut iter = Iter {
inner: iter.into_iter(),
error: None,
};
let result = V::from_iter(&mut iter);
ResultPrealloc(iter.error.map_or(Ok(result), Err))
}
}
trait CollectResultExt<T, E>: Iterator<Item = Result<T, E>> {
#[inline]
fn collect_result_prealloc<B: FromIterator<T>>(self) -> Result<B, E>
where
Self: Sized,
{
self.collect::<ResultPrealloc<B, E>>().0
}
}
impl<T, E, I> CollectResultExt<T, E> for I where I: Iterator<Item = Result<T, E>> {}
pub struct FromIntoIterator<Coll, Len>(PhantomData<(Coll, Len)>);
unsafe impl<Coll, Len, C: ConfigCore> SchemaWrite<C> for FromIntoIterator<Coll, Len>
where
Len: SeqLen<C>,
Coll: IntoIterator,
for<'a> &'a Coll: IntoIterator<Item: SchemaWrite<C>, IntoIter: ExactSizeIterator>,
for<'a> <&'a Coll as IntoIterator>::Item:
Borrow<<<&'a Coll as IntoIterator>::Item as SchemaWrite<C>>::Src>,
{
type Src = Coll;
#[inline]
fn size_of(src: &Coll) -> WriteResult<usize> {
size_of_elem_iter::<<&Coll as IntoIterator>::Item, Len, C>(src.into_iter())
}
#[inline]
fn write(writer: impl Writer, src: &Coll) -> WriteResult<()> {
let iter = src.into_iter();
Len::prealloc_check::<Coll::Item>(iter.len())?;
write_elem_iter::<<&Coll as IntoIterator>::Item, Len, C>(writer, iter)
}
}
unsafe impl<'de, Coll, Len, C: ConfigCore> SchemaRead<'de, C> for FromIntoIterator<Coll, Len>
where
Len: SeqLen<C>,
Coll: IntoIterator<Item: SchemaRead<'de, C>>,
Coll: FromIterator<<Coll::Item as SchemaRead<'de, C>>::Dst>,
{
type Dst = Coll;
#[inline]
fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Coll>) -> ReadResult<()> {
let len =
Len::read_prealloc_check::<<Coll::Item as SchemaRead<'de, C>>::Dst>(reader.by_ref())?;
let coll = if let TypeMeta::Static { size, .. } = Coll::Item::TYPE_META {
let mut reader = unsafe { reader.as_trusted_for_seq(len, size) }?;
(0..len)
.map(|_| Coll::Item::get(reader.by_ref()))
.collect_result_prealloc()?
} else {
(0..len)
.map(|_| Coll::Item::get(reader.by_ref()))
.collect_result_prealloc()?
};
dst.write(coll);
Ok(())
}
}
#[inline]
pub fn decode_into_slice_t<'de, T, C>(
mut reader: impl Reader<'de>,
slice: &mut [MaybeUninit<T::Dst>],
) -> ReadResult<()>
where
T: SchemaRead<'de, C>,
C: ConfigCore,
{
let base = slice.as_mut_ptr();
let len = slice.len();
let mut guard = SliceDropGuard::<T::Dst>::new(base);
match T::TYPE_META {
TypeMeta::Static {
zero_copy: true, ..
} => {
unsafe { reader.copy_into_slice_t(slice) }?
}
TypeMeta::Static {
size,
zero_copy: false,
} => {
let mut reader = unsafe { reader.as_trusted_for_seq(len, size) }?;
for i in 0..len {
let slot = unsafe { &mut *base.add(i) };
T::read(reader.by_ref(), slot)?;
guard.inc_len();
}
}
TypeMeta::Dynamic => {
for i in 0..len {
let slot = unsafe { &mut *base.add(i) };
T::read(reader.by_ref(), slot)?;
guard.inc_len();
}
}
}
mem::forget(guard);
Ok(())
}
#[inline]
pub fn decode_into_container_t<'de, T, C, A>(
mut reader: impl Reader<'de>,
len: usize,
make: impl FnOnce(usize) -> A,
get_slice: impl FnOnce(&mut A) -> &mut [MaybeUninit<T::Dst>],
) -> ReadResult<A>
where
T: SchemaRead<'de, C>,
C: ConfigCore,
{
macro_rules! decode {
($reader:expr) => {{
let mut container = make(len);
let slice = &mut get_slice(&mut container)[..len];
decode_into_slice_t::<T, C>($reader, slice)?;
container
}};
}
Ok(match T::TYPE_META {
TypeMeta::Static { size, .. } => {
let reader = unsafe { reader.as_trusted_for_seq(len, size) }?;
decode!(reader)
}
TypeMeta::Dynamic => decode!(reader),
})
}
#[cfg(feature = "alloc")]
#[inline]
pub fn encode_from_iter_prealloc_check<T, Len, C>(
writer: impl Writer,
src: impl IntoIterator<IntoIter: ExactSizeIterator, Item: Borrow<T::Src>>,
) -> WriteResult<()>
where
C: ConfigCore,
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
write_elem_iter_prealloc_check::<T, Len, C>(writer, src.into_iter())
}
#[inline]
pub fn encode_from_iter<T, Len, C>(
writer: impl Writer,
src: impl IntoIterator<IntoIter: ExactSizeIterator, Item: Borrow<T::Src>>,
) -> WriteResult<()>
where
C: ConfigCore,
Len: SeqLen<C>,
T: SchemaWrite<C>,
{
write_elem_iter::<T, Len, C>(writer, src.into_iter())
}
#[cfg(all(test, feature = "std"))]
mod tests {
use {
crate::{
Deserialize, ReadError, Serialize,
containers::{self, CheckUniqueKeys},
deserialize,
len::{BincodeLen, UseIntLen},
serialize,
},
std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, hash_map::RandomState},
};
fn dup_key_map_bytes() -> Vec<u8> {
serialize(&vec![(1u32, 10u64), (1, 20)]).unwrap()
}
fn dup_elem_set_bytes() -> Vec<u8> {
serialize(&vec![7u32, 7]).unwrap()
}
#[test]
fn allows_duplicates_by_default() {
let bytes = dup_key_map_bytes();
let plain: HashMap<u32, u64> = deserialize(&bytes).unwrap();
let loose = <containers::HashMap<u32, u64, BincodeLen>>::deserialize(&bytes).unwrap();
assert_eq!(loose, HashMap::from([(1, 20)]));
assert_eq!(loose, plain);
let bytes = dup_elem_set_bytes();
let plain: HashSet<u32> = deserialize(&bytes).unwrap();
let loose = <containers::HashSet<u32, BincodeLen>>::deserialize(&bytes).unwrap();
assert_eq!(loose, HashSet::from([7]));
assert_eq!(loose, plain);
}
#[test]
fn strict_maps_reject_duplicate_keys() {
let bytes = dup_key_map_bytes();
assert!(matches!(
<containers::HashMap<u32, u64, BincodeLen, RandomState, CheckUniqueKeys>>::deserialize(
&bytes
),
Err(ReadError::Custom(_)),
));
assert!(matches!(
<containers::BTreeMap<u32, u64, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes),
Err(ReadError::Custom(_)),
));
}
#[test]
fn strict_sets_reject_duplicate_elements() {
let bytes = dup_elem_set_bytes();
assert!(matches!(
<containers::HashSet<u32, BincodeLen, RandomState, CheckUniqueKeys>>::deserialize(
&bytes
),
Err(ReadError::Custom(_)),
));
assert!(matches!(
<containers::BTreeSet<u32, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes),
Err(ReadError::Custom(_)),
));
}
#[test]
fn strict_decoding_accepts_unique_keys() {
let map = BTreeMap::from([(1u32, 10u64), (2, 20), (3, 30)]);
let bytes = serialize(&map).unwrap();
let strict =
<containers::BTreeMap<u32, u64, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes)
.unwrap();
assert_eq!(strict, map);
let set = BTreeSet::from([1u32, 2, 3]);
let bytes = serialize(&set).unwrap();
let strict =
<containers::BTreeSet<u32, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes).unwrap();
assert_eq!(strict, set);
}
#[test]
fn strict_decoding_covers_dynamically_sized_keys() {
let bytes = serialize(&vec![("a".to_string(), 1u64), ("a".to_string(), 2)]).unwrap();
assert!(matches!(
<containers::BTreeMap<String, u64, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes),
Err(ReadError::Custom(_)),
));
let bytes = serialize(&vec!["a".to_string(), "a".to_string()]).unwrap();
assert!(matches!(
<containers::BTreeSet<String, BincodeLen, CheckUniqueKeys>>::deserialize(&bytes),
Err(ReadError::Custom(_)),
));
}
#[test]
fn keyed_containers_encode_like_the_plain_schemas() {
let map = BTreeMap::from([(1u32, 10u64), (2, 20)]);
let container = <containers::BTreeMap<u32, u64, BincodeLen>>::serialize(&map).unwrap();
assert_eq!(container, serialize(&map).unwrap());
assert_eq!(container, bincode::serialize(&map).unwrap());
let set = BTreeSet::from([1u32, 2]);
let container = <containers::BTreeSet<u32, BincodeLen>>::serialize(&set).unwrap();
assert_eq!(container, serialize(&set).unwrap());
assert_eq!(container, bincode::serialize(&set).unwrap());
}
#[test]
fn keyed_containers_customize_the_length_encoding() {
type ShortMap = containers::BTreeMap<u32, u64, UseIntLen<u16>>;
let map = BTreeMap::from([(1u32, 10u64), (2, 20)]);
let bytes = ShortMap::serialize(&map).unwrap();
assert_eq!(
bytes.len(),
size_of::<u16>() + map.len() * (size_of::<u32>() + size_of::<u64>()),
);
assert_eq!(ShortMap::deserialize(&bytes).unwrap(), map);
assert!(<containers::BTreeMap<u32, u64, BincodeLen>>::deserialize(&bytes).is_err());
}
}