Skip to main content

epserde/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Inria
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7
8#![cfg_attr(any(all(feature="std", feature="mmap"), not(doctest)), doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md")))]
9#![deny(unconditional_recursion)]
10#![cfg_attr(not(feature = "std"), no_std)]
11#[cfg(not(feature = "std"))]
12extern crate alloc;
13
14use core::{hash::Hash, marker::PhantomData, mem::transmute};
15
16#[cfg(feature = "derive")]
17pub use epserde_derive::{Epserde, TypeInfo};
18
19use crate::{
20    deser::{DeserInner, DeserType, ReadWithPos, SliceWithPos},
21    ser::{SerInner, WriteWithNames},
22    traits::{AlignHash, AlignTo, CopyType, TypeHash, Zero},
23};
24
25pub mod deser;
26pub mod impls;
27pub mod ser;
28pub mod traits;
29pub mod utils;
30
31pub mod prelude {
32    pub use crate::PhantomDeserData;
33    pub use crate::deser;
34    pub use crate::deser::DeserHelper;
35    pub use crate::deser::DeserInner;
36    pub use crate::deser::DeserType;
37    pub use crate::deser::Deserialize;
38    pub use crate::deser::Flags;
39    pub use crate::deser::MemCase;
40    pub use crate::deser::ReadWithPos;
41    pub use crate::deser::SliceWithPos;
42    pub use crate::impls::iter::SerIter;
43    pub use crate::ser;
44    pub use crate::ser::SerHelper;
45    pub use crate::ser::SerInner;
46    pub use crate::ser::Serialize;
47    pub use crate::traits::*;
48    #[allow(unused_imports)] // with some features utils is empty
49    pub use crate::utils::*;
50    #[cfg(feature = "derive")]
51    pub use epserde_derive::Epserde;
52    pub use {crate::Aligned16, crate::Aligned64};
53}
54
55/// (Major, Minor) version of the file format, this follows semantic versioning
56pub const VERSION: (u16, u16) = (1, 1);
57
58/// Magic cookie, also used as endianness marker.
59pub const MAGIC: u64 = u64::from_ne_bytes(*b"epserde ");
60/// What we will read if the endianness is mismatched.
61pub const MAGIC_REV: u64 = u64::from_le_bytes(MAGIC.to_be_bytes());
62
63/// A 128-bit aligned type.
64///
65/// This is useful for creating [`AlignedCursor`](crate::utils::AlignedCursor)
66/// and [`MemBackend::Memory`](crate::deser::MemBackend::Memory)
67/// instances with 128-bit alignment.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
70#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
71#[repr(align(16))]
72#[derive(Default)]
73pub struct Aligned16(pub [u8; 16]);
74
75/// A 64-bit aligned type.
76///
77/// This is useful for creating [`AlignedCursor`](crate::utils::AlignedCursor)
78/// and [`MemBackend::Memory`](crate::deser::MemBackend::Memory)
79/// instances with 64-bit alignment.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
82#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
83#[repr(align(64))]
84pub struct Aligned64(pub [u8; 64]);
85
86impl Default for Aligned64 {
87    fn default() -> Self {
88        Aligned64([0u8; 64])
89    }
90}
91
92/// Computes the padding needed for alignment, that is, the smallest
93/// number such that `(value + pad_align_to(value, align_to)) & (align_to - 1) == 0`.
94pub const fn pad_align_to(value: usize, align_to: usize) -> usize {
95    value.wrapping_neg() & (align_to - 1)
96}
97
98/// A type semantically equivalent to [`PhantomData`], but whose type parameter
99/// is replaced with its associated deserialization type.
100///
101/// In some case, you might find yourself with a deep-copy type that has a type
102/// parameter `T` appearing both in a field and in a [`PhantomData`]. In this
103/// case, the type will not compile, as in its associated deserialization type
104/// `T` will be replaced by `T::DeserType`, but the [`PhantomData`] field will
105/// still contain `T`. To fix this issue, you can use [`PhantomDeserData`]
106/// instead.
107///
108/// Note that `T` must be sized because of a trait bound on [`DeserInner`].
109///
110/// # Examples
111///
112/// This code will not compile:
113/// ```compile_fail
114/// use epserde::prelude::*;
115/// #[derive(Epserde, Debug, PartialEq, Eq, Clone, Default)]
116/// struct Data<T> {
117///     data: T,
118///     phantom: PhantomData<T>,
119/// }
120/// ```
121///
122/// This code, instead, will compile:
123/// ```
124/// use epserde::prelude::*;
125/// #[derive(Epserde, Debug, PartialEq, Eq, Clone, Default)]
126/// struct Data<T> {
127///     data: T,
128///     phantom: PhantomDeserData<T>,
129/// }
130/// ```
131#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
132pub struct PhantomDeserData<T>(pub PhantomData<T>);
133
134impl<T: DeserInner> PhantomDeserData<T> {
135    /// A custom deserialization method for [`PhantomDeserData`] that transmutes
136    /// the inner type.
137    ///
138    /// # Safety
139    ///
140    /// See [`DeserInner::_deser_eps_inner`].
141    #[inline(always)]
142    pub unsafe fn _deser_eps_inner_special<'a>(
143        _backend: &mut SliceWithPos<'a>,
144    ) -> deser::Result<PhantomDeserData<T::DeserType<'a>>> {
145        // SAFETY: it is a zero-sized type
146        Ok(unsafe {
147            transmute::<DeserType<'a, PhantomDeserData<T>>, PhantomDeserData<T::DeserType<'a>>>(
148                PhantomDeserData(PhantomData),
149            )
150        })
151    }
152}
153
154unsafe impl<T> CopyType for PhantomDeserData<T> {
155    type Copy = Zero;
156}
157
158impl<T> AlignTo for PhantomDeserData<T> {
159    #[inline(always)]
160    fn align_to() -> usize {
161        0
162    }
163}
164
165impl<T: TypeHash> TypeHash for PhantomDeserData<T> {
166    #[inline(always)]
167    fn type_hash(hasher: &mut impl core::hash::Hasher) {
168        "PhantomDeserData".hash(hasher);
169        T::type_hash(hasher);
170    }
171}
172
173impl<T> AlignHash for PhantomDeserData<T> {
174    #[inline(always)]
175    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
176}
177
178impl<T> SerInner for PhantomDeserData<T> {
179    // This type is nominal only; nothing will be serialized or deserialized.
180    type SerType = Self;
181    const IS_ZERO_COPY: bool = true;
182
183    #[inline(always)]
184    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
185        Ok(())
186    }
187}
188
189impl<T: DeserInner> DeserInner for PhantomDeserData<T> {
190    // SAFETY: it is a zero-sized type
191    unsafe_assume_covariance!();
192    #[inline(always)]
193    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
194        Ok(PhantomDeserData(PhantomData))
195    }
196    type DeserType<'a> = PhantomDeserData<T::DeserType<'a>>;
197    #[inline(always)]
198    unsafe fn _deser_eps_inner<'a>(
199        _backend: &mut SliceWithPos<'a>,
200    ) -> deser::Result<Self::DeserType<'a>> {
201        Ok(PhantomDeserData(PhantomData))
202    }
203}
204
205#[cfg(test)]
206#[test]
207fn test_pad_align_to() {
208    assert_eq!(7 + pad_align_to(7, 8), 8);
209    assert_eq!(8 + pad_align_to(8, 8), 8);
210    assert_eq!(9 + pad_align_to(9, 8), 16);
211    assert_eq!(36 + pad_align_to(36, 16), 48);
212}