#[cfg(feature = "alloc")]
use crate::len::SeqLen;
use {
crate::{
TypeMeta,
config::ConfigCore,
error::{ReadError, ReadResult, WriteResult},
io::{ReadError as IoReadError, Reader, Writer},
schema::{SchemaRead, SchemaWrite},
},
core::{marker::PhantomData, mem::MaybeUninit},
};
pub struct FromInto<Wire, Target>(PhantomData<Wire>, PhantomData<Target>);
unsafe impl<C, Wire, Target> SchemaWrite<C> for FromInto<Wire, Target>
where
C: ConfigCore,
Wire: SchemaWrite<C>,
Wire::Src: Sized + for<'a> From<&'a Target>,
{
type Src = Target;
const TYPE_META: TypeMeta = <Wire as SchemaWrite<C>>::TYPE_META.keep_zero_copy(false);
#[inline]
fn size_of(src: &Self::Src) -> WriteResult<usize> {
if let TypeMeta::Static { size, .. } = <Self as SchemaWrite<C>>::TYPE_META {
return Ok(size);
}
let wire: Wire::Src = src.into();
<Wire as SchemaWrite<C>>::size_of(&wire)
}
#[inline]
fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
let wire: Wire::Src = src.into();
<Wire as SchemaWrite<C>>::write(writer, &wire)
}
}
unsafe impl<'de, C, Wire, Target> SchemaRead<'de, C> for FromInto<Wire, Target>
where
C: ConfigCore,
Wire: SchemaRead<'de, C>,
Target: From<Wire::Dst>,
{
type Dst = Target;
const TYPE_META: TypeMeta = <Wire as SchemaRead<'de, C>>::TYPE_META.keep_zero_copy(false);
#[inline]
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let wire = <Wire as SchemaRead<'de, C>>::get(reader)?;
dst.write(Target::from(wire));
Ok(())
}
}
pub struct DefaultOnEmptyRead<T>(PhantomData<T>);
unsafe impl<'de, C, T> SchemaRead<'de, C> for DefaultOnEmptyRead<T>
where
C: ConfigCore,
T: SchemaRead<'de, C>,
T::Dst: Default,
{
type Dst = T::Dst;
#[inline]
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
match <T as SchemaRead<'de, C>>::read(reader, dst) {
Ok(()) => Ok(()),
Err(ReadError::Io(IoReadError::ReadSizeLimit(_))) => {
dst.write(Self::Dst::default());
Ok(())
}
Err(e) => Err(e),
}
}
}
unsafe impl<C, T> SchemaWrite<C> for DefaultOnEmptyRead<T>
where
C: ConfigCore,
T: SchemaWrite<C>,
{
type Src = T::Src;
const TYPE_META: TypeMeta = <T as SchemaWrite<C>>::TYPE_META;
#[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)
}
}
pub struct Discard<Inner>(PhantomData<Inner>);
unsafe impl<'de, C, Inner> SchemaRead<'de, C> for Discard<Inner>
where
C: ConfigCore,
Inner: SchemaRead<'de, C>,
Inner::Dst: Default,
{
type Dst = Inner::Dst;
const TYPE_META: TypeMeta = Inner::TYPE_META.keep_zero_copy(false);
#[inline]
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
Inner::get(reader)?;
dst.write(Default::default());
Ok(())
}
}
unsafe impl<C, Inner> SchemaWrite<C> for Discard<Inner>
where
C: ConfigCore,
Inner: SchemaWrite<C>,
Inner::Src: Default,
{
type Src = Inner::Src;
const TYPE_META: TypeMeta = Inner::TYPE_META.keep_zero_copy(false);
#[inline]
fn size_of(_src: &Self::Src) -> WriteResult<usize> {
if let TypeMeta::Static { size, .. } = Inner::TYPE_META {
return Ok(size);
}
Inner::size_of(&Default::default())
}
#[inline]
fn write(writer: impl Writer, _src: &Self::Src) -> WriteResult<()> {
Inner::write(writer, &Default::default())
}
}
#[cfg(feature = "alloc")]
pub struct DiscardSeq<T, Len>(PhantomData<T>, PhantomData<Len>);
#[cfg(feature = "alloc")]
unsafe impl<'de, C, T, Len> SchemaRead<'de, C> for DiscardSeq<T, Len>
where
C: ConfigCore,
Len: SeqLen<C>,
T: SchemaRead<'de, C>,
{
type Dst = alloc::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())?;
if let TypeMeta::Static { size, .. } = T::TYPE_META {
let mut trusted = unsafe { reader.as_trusted_for_seq(len, size)? };
for _ in 0..len {
T::get(trusted.by_ref())?;
}
} else {
for _ in 0..len {
T::get(reader.by_ref())?;
}
}
dst.write(alloc::vec::Vec::new());
Ok(())
}
}
#[cfg(feature = "alloc")]
unsafe impl<C, T, Len> SchemaWrite<C> for DiscardSeq<T, Len>
where
C: ConfigCore,
Len: SeqLen<C>,
T: SchemaWrite<C>,
T::Src: Sized,
{
type Src = alloc::vec::Vec<T::Src>;
#[inline]
fn size_of(_src: &Self::Src) -> WriteResult<usize> {
Len::write_bytes_needed(0)
}
#[inline]
fn write(writer: impl Writer, _src: &Self::Src) -> WriteResult<()> {
Len::write(writer, 0)
}
}
#[cfg(all(test, feature = "derive", feature = "alloc"))]
mod tests {
use {
crate::{
SchemaRead, SchemaWrite,
adapter::{DefaultOnEmptyRead, Discard, DiscardSeq, FromInto},
deserialize,
len::BincodeLen,
serialize,
},
alloc::{collections::BTreeMap, string::String, vec::Vec},
};
#[test]
fn scalar_wire() {
#[derive(Debug, PartialEq, Clone, Copy)]
struct Id(u32);
impl From<&Id> for u32 {
fn from(id: &Id) -> u32 {
id.0
}
}
impl From<u32> for Id {
fn from(raw: u32) -> Id {
Id(raw)
}
}
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Msg {
#[wincode(with = "FromInto<u32, _>")]
id: Id,
}
let msg = Msg {
id: Id(0xdead_beef),
};
let bytes = serialize(&msg).unwrap();
assert_eq!(bytes, serialize(&0xdead_beef_u32).unwrap());
assert_eq!(msg, deserialize(&bytes).unwrap());
}
#[test]
fn adapter_wire() {
use crate::{containers, len::UseIntLen};
#[derive(Debug, PartialEq, Clone)]
struct Name(String);
impl From<&Name> for Vec<u8> {
fn from(name: &Name) -> Vec<u8> {
name.0.as_bytes().to_vec()
}
}
impl From<Vec<u8>> for Name {
fn from(bytes: Vec<u8>) -> Name {
Name(String::from_utf8(bytes).unwrap())
}
}
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Msg {
#[wincode(with = "FromInto<containers::Vec<u8, UseIntLen<u16>>, _>")]
name: Name,
}
let msg = Msg {
name: Name("wincode".into()),
};
let bytes = serialize(&msg).unwrap();
assert_eq!(bytes.len(), 2 + "wincode".len());
assert_eq!(msg, deserialize(&bytes).unwrap());
}
#[test]
fn default_on_empty_read() {
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Record {
id: u32,
#[wincode(with = "DefaultOnEmptyRead<u64>")]
added_later: u64,
}
let record = Record {
id: 7,
added_later: 42,
};
let bytes = serialize(&record).unwrap();
assert_eq!(bytes.len(), 4 + 8);
assert_eq!(record, deserialize(&bytes).unwrap());
let legacy = serialize(&7u32).unwrap();
assert_eq!(
deserialize::<Record>(&legacy).unwrap(),
Record {
id: 7,
added_later: 0,
}
);
let truncated = [0u8; 6];
assert_eq!(
deserialize::<Record>(&truncated).unwrap(),
Record {
id: 0,
added_later: 0,
}
);
}
#[test]
fn discard_seq_nested() {
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Full {
pairs: Vec<(u32, u16)>,
singles: Vec<u32>,
nested: Vec<Vec<u8>>,
map: BTreeMap<u32, u64>,
tag: u8,
}
#[derive(SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Partial {
#[wincode(with = "DiscardSeq<(u32, u16), BincodeLen>")]
pairs: Vec<(u32, u16)>,
#[wincode(with = "DiscardSeq<Discard<u32>, BincodeLen>")]
singles: Vec<u32>,
#[wincode(with = "DiscardSeq<DiscardSeq<u8, BincodeLen>, BincodeLen>")]
nested: Vec<Vec<u8>>,
#[wincode(with = "DiscardSeq<(u32, u64), BincodeLen>")]
map: Vec<(u32, u64)>,
tag: u8,
}
let bytes = serialize(&Full {
pairs: vec![(1, 2), (3, 4)],
singles: vec![10, 20, 30],
nested: vec![vec![1, 2], vec![], vec![9]],
map: BTreeMap::from([(1, 10), (2, 20), (3, 30)]),
tag: 7,
})
.unwrap();
assert_eq!(
deserialize::<Partial>(&bytes).unwrap(),
Partial {
pairs: Vec::new(),
singles: Vec::new(),
nested: Vec::new(),
map: Vec::new(),
tag: 7,
},
);
}
#[test]
fn discard_seq_validates_non_zero_copy_elements() {
#[derive(SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Partial {
#[wincode(with = "DiscardSeq<bool, BincodeLen>")]
flags: Vec<bool>,
}
let ok = serialize(&vec![true, false]).unwrap();
assert_eq!(
deserialize::<Partial>(&ok).unwrap(),
Partial { flags: Vec::new() },
);
let mut bad = ok.clone();
*bad.last_mut().unwrap() = 2;
assert!(deserialize::<Partial>(&bad).is_err());
}
#[test]
fn discard_string_as_bytes() {
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Full {
text: String,
tag: u8,
}
#[derive(SchemaRead, Debug, PartialEq)]
#[wincode(internal)]
struct Partial {
#[wincode(with = "DiscardSeq<u8, BincodeLen>")]
text: Vec<u8>,
tag: u8,
}
let bytes = serialize(&Full {
text: "hello".into(),
tag: 7,
})
.unwrap();
assert_eq!(
deserialize::<Partial>(&bytes).unwrap(),
Partial {
text: Vec::new(),
tag: 7,
},
);
}
}