// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). //! Documentation on implementing custom VarULE types. //! //! This module contains documentation for defining custom VarULE types, //! especially those using complex custom dynamically sized types. //! //! In *most cases* you should be able to create custom VarULE types using //! [`#[make_varule]`](crate::make_ule). //! //! # Example //! //! For example, if your regular stack type is: //! //! ```rust //! use zerofrom::ZeroFrom; //! use zerovec::ule::*; //! use zerovec::ZeroVec; //! //! #[derive(serde::Serialize, serde::Deserialize)] //! struct Foo<'a> { //! field1: char, //! field2: u32, //! #[serde(borrow)] //! field3: ZeroVec<'a, u32>, //! } //! ``` //! //! then the ULE type will be implemented as follows. Ideally, you should have //! `EncodeAsVarULE` and `ZeroFrom` implementations on `Foo` pertaining to `FooULE`, //! as well as a `Serialize` impl on `FooULE` and a `Deserialize` impl on `Box` //! to enable human-readable serialization and deserialization. //! //! ```rust //! use zerovec::{ZeroVec, VarZeroVec, ZeroSlice}; //! use zerovec::ule::*; //! use zerofrom::ZeroFrom; //! use core::mem; //! //! # #[derive(serde::Serialize, serde::Deserialize)] //! # struct Foo<'a> { //! # field1: char, //! # field2: u32, //! # #[serde(borrow)] //! # field3: ZeroVec<'a, u32> //! # } //! //! // Must be repr(packed) for safety of VarULE! //! // Must also only contain ULE types //! #[repr(packed)] //! struct FooULE { //! field1: ::ULE, //! field2: ::ULE, //! field3: ZeroSlice, //! } //! //! // Safety (based on the safety checklist on the VarULE trait): //! // 1. FooULE does not include any uninitialized or padding bytes. (achieved by `#[repr(packed)]` on //! // a struct with only ULE fields) //! // 2. FooULE is aligned to 1 byte. (achieved by `#[repr(packed)]` on //! // a struct with only ULE fields) //! // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. //! // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety //! // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. //! // 6. The other VarULE methods use the default impl. //! // 7. FooULE byte equality is semantic equality //! unsafe impl VarULE for FooULE { //! fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { //! // validate each field //! ::ULE::validate_byte_slice(&bytes[0..3]).map_err(|_| ZeroVecError::parse::())?; //! ::ULE::validate_byte_slice(&bytes[3..7]).map_err(|_| ZeroVecError::parse::())?; //! let _ = ZeroVec::::parse_byte_slice(&bytes[7..]).map_err(|_| ZeroVecError::parse::())?; //! Ok(()) //! } //! unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { //! let ptr = bytes.as_ptr(); //! let len = bytes.len(); //! // subtract the length of the char and u32 to get the length of the array //! let len_new = (len - 7) / 4; //! // it's hard constructing custom DSTs, we fake a pointer/length construction //! // eventually we can use the Pointer::Metadata APIs when they stabilize //! let fake_slice = core::ptr::slice_from_raw_parts(ptr as *const ::ULE, len_new); //! &*(fake_slice as *const Self) //! } //! } //! //! unsafe impl EncodeAsVarULE for Foo<'_> { //! fn encode_var_ule_as_slices(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { //! // take each field, convert to ULE byte slices, and pass them through //! cb(&[::ULE::as_byte_slice(&[self.field1.to_unaligned()]), //! ::ULE::as_byte_slice(&[self.field2.to_unaligned()]), //! // the ZeroVec is already in the correct slice format //! self.field3.as_bytes()]) //! } //! } //! //! impl<'a> ZeroFrom<'a, FooULE> for Foo<'a> { //! fn zero_from(other: &'a FooULE) -> Self { //! Self { //! field1: AsULE::from_unaligned(other.field1), //! field2: AsULE::from_unaligned(other.field2), //! field3: ZeroFrom::zero_from(&other.field3), //! } //! } //! } //! //! //! impl serde::Serialize for FooULE //! { //! fn serialize(&self, serializer: S) -> Result //! where //! S: serde::Serializer, //! { //! Foo::zero_from(self).serialize(serializer) //! } //! } //! //! impl<'de> serde::Deserialize<'de> for Box //! { //! fn deserialize(deserializer: D) -> Result //! where //! D: serde::Deserializer<'de>, //! { //! let mut foo = Foo::deserialize(deserializer)?; //! Ok(encode_varule_to_box(&foo)) //! } //! } //! //! fn main() { //! let mut foos = vec![Foo {field1: 'u', field2: 983, field3: ZeroVec::alloc_from_slice(&[1212,2309,500,7000])}, //! Foo {field1: 'l', field2: 1010, field3: ZeroVec::alloc_from_slice(&[1932, 0, 8888, 91237])}]; //! //! let vzv = VarZeroVec::<_>::from(&foos); //! //! assert_eq!(char::from_unaligned(vzv.get(0).unwrap().field1), 'u'); //! assert_eq!(u32::from_unaligned(vzv.get(0).unwrap().field2), 983); //! assert_eq!(&vzv.get(0).unwrap().field3, &[1212,2309,500,7000][..]); //! //! assert_eq!(char::from_unaligned(vzv.get(1).unwrap().field1), 'l'); //! assert_eq!(u32::from_unaligned(vzv.get(1).unwrap().field2), 1010); //! assert_eq!(&vzv.get(1).unwrap().field3, &[1932, 0, 8888, 91237][..]); //! } //! ```