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 MIT
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, CopyType, PadTo, TypeHash, Zero},
23};
24
25pub mod deser;
26pub mod impls;
27pub mod ser;
28pub mod traits;
29pub mod utils;
30
31/// Re-exports of the traits, types, and macros commonly needed to use ε-serde.
32///
33/// Glob-import it (`use epserde::prelude::*;`) to bring the (de)serialization
34/// traits and helpers into scope.
35pub mod prelude {
36    #[allow(deprecated)]
37    pub use crate::PhantomDeserData;
38    pub use crate::deser;
39    pub use crate::deser::DeserHelper;
40    pub use crate::deser::DeserInner;
41    pub use crate::deser::DeserType;
42    pub use crate::deser::Deserialize;
43    pub use crate::deser::Flags;
44    pub use crate::deser::MemCase;
45    pub use crate::deser::ReadWithPos;
46    pub use crate::deser::SliceWithPos;
47    pub use crate::impls::iter::SerIter;
48    pub use crate::ser;
49    pub use crate::ser::SerHelper;
50    pub use crate::ser::SerInner;
51    pub use crate::ser::Serialize;
52    pub use crate::traits::*;
53    #[allow(unused_imports)] // with some features utils is empty
54    pub use crate::utils::*;
55    #[cfg(feature = "derive")]
56    pub use epserde_derive::Epserde;
57    pub use {crate::Aligned16, crate::Aligned64};
58}
59
60/// (Major, Minor) version of the file format, this follows semantic versioning
61pub const VERSION: (u16, u16) = (2, 0);
62
63/// Magic cookie, also used as endianness marker.
64///
65/// The value is defined with a fixed (little-endian) byte order, so it is the
66/// same number on every platform. Since it is serialized in native byte order,
67/// a file written on a platform with the opposite endianness is read back as
68/// [`MAGIC_REV`]. Defining it with `from_ne_bytes` would instead make the
69/// marker ineffective: the two native conversions would cancel out, every
70/// platform would write the same bytes, and every platform would read them
71/// back as its own [`MAGIC`].
72pub const MAGIC: u64 = u64::from_le_bytes(*b"epserde ");
73/// What we will read if the endianness is mismatched.
74pub const MAGIC_REV: u64 = MAGIC.swap_bytes();
75
76/// A 16-byte (128-bit) aligned type.
77///
78/// This is useful for creating [`AlignedCursor`] and [`MemBackend::Memory`]
79/// instances with 128-bit alignment.
80///
81/// [`AlignedCursor`]: crate::utils::AlignedCursor
82/// [`MemBackend::Memory`]: crate::deser::MemBackend::Memory
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
85#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
86#[repr(align(16))]
87#[derive(Default)]
88pub struct Aligned16(pub [u8; 16]);
89
90/// A 64-byte (512-bit) aligned type.
91///
92/// This is useful for creating [`AlignedCursor`] and [`MemBackend::Memory`]
93/// instances with 512-bit alignment.
94///
95/// [`AlignedCursor`]: crate::utils::AlignedCursor
96/// [`MemBackend::Memory`]: crate::deser::MemBackend::Memory
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
99#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
100#[repr(align(64))]
101pub struct Aligned64(pub [u8; 64]);
102
103impl Default for Aligned64 {
104    fn default() -> Self {
105        Aligned64([0u8; 64])
106    }
107}
108
109/// Computes the padding needed for alignment, that is, the smallest
110/// number such that `value + pad_align_to(value, pad_to)` is a multiple
111/// of `pad_to`.
112///
113/// A `pad_to` equal to zero (the [`PadTo`] value of
114/// zero-sized types) requests no alignment and returns zero.
115///
116/// `pad_to` must be zero or a power of two, otherwise this function panics.
117pub const fn pad_align_to(value: usize, pad_to: usize) -> usize {
118    assert!(pad_to == 0 || pad_to.is_power_of_two());
119    value.wrapping_neg() & pad_to.saturating_sub(1)
120}
121
122/// **Deprecated.** Use plain [`PhantomData`] instead.
123///
124/// This type used to be a workaround for the case where a deep-copy
125/// `Epserde`-derived struct had a type parameter `T` appearing both in a field
126/// and in a [`PhantomData<T>`] field, which would otherwise fail to compile
127/// because [`PhantomData<T>`] does not substitute its parameter. The `Epserde`
128/// derive now handles [`PhantomData<T>`] natively, substituting `T` inside the
129/// derived `Self::DeserType<'_>`, so [`PhantomDeserData`] is no longer needed
130/// for new code.
131///
132/// Migrating an existing struct from [`PhantomDeserData<T>`] to
133/// [`PhantomData<T>`] changes the struct's type hash, so previously-serialized
134/// files will fail to deserialize against the new definition; re-serialize the
135/// data after migration.
136///
137/// Note that `T` must be sized because of a trait bound on [`DeserInner`].
138#[deprecated(
139    since = "0.13.0",
140    note = "use plain `PhantomData<T>` instead: the `Epserde` derive now substitutes \
141its parameter natively. Note: switching an existing struct from \
142`PhantomDeserData<T>` to `PhantomData<T>` changes the struct's type hash, so \
143previously-serialized files will fail to deserialize against the new definition."
144)]
145#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
146pub struct PhantomDeserData<T>(pub PhantomData<T>);
147
148#[allow(deprecated)]
149impl<T: DeserInner> PhantomDeserData<T> {
150    /// A custom deserialization method for [`PhantomDeserData`] that transmutes
151    /// the inner type.
152    ///
153    /// # Safety
154    ///
155    /// See [`DeserInner::_deser_eps_inner`].
156    #[inline(always)]
157    pub unsafe fn _deser_eps_inner_special<'a>(
158        _backend: &mut SliceWithPos<'a>,
159    ) -> deser::Result<PhantomDeserData<T::DeserType<'a>>> {
160        // SAFETY: it is a zero-sized type
161        Ok(unsafe {
162            transmute::<DeserType<'a, PhantomDeserData<T>>, PhantomDeserData<T::DeserType<'a>>>(
163                PhantomDeserData(PhantomData),
164            )
165        })
166    }
167}
168
169#[allow(deprecated)]
170unsafe impl<T> CopyType for PhantomDeserData<T> {
171    type Copy = Zero;
172}
173
174#[allow(deprecated)]
175impl<T> PadTo for PhantomDeserData<T> {
176    #[inline(always)]
177    fn pad_to() -> usize {
178        0
179    }
180}
181
182#[allow(deprecated)]
183impl<T: TypeHash> TypeHash for PhantomDeserData<T> {
184    #[inline(always)]
185    fn type_hash(hasher: &mut impl core::hash::Hasher) {
186        "PhantomDeserData".hash(hasher);
187        T::type_hash(hasher);
188    }
189}
190
191#[allow(deprecated)]
192impl<T> AlignHash for PhantomDeserData<T> {
193    #[inline(always)]
194    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
195}
196
197#[allow(deprecated)]
198impl<T> SerInner for PhantomDeserData<T> {
199    // This type is nominal only; nothing will be serialized or deserialized.
200    type SerType = Self;
201    const IS_ZERO_COPY: bool = true;
202
203    #[inline(always)]
204    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
205        Ok(())
206    }
207}
208
209#[allow(deprecated)]
210impl<T: DeserInner> DeserInner for PhantomDeserData<T> {
211    // SAFETY: it is a zero-sized type
212    unsafe_assume_covariance!();
213    #[inline(always)]
214    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
215        Ok(PhantomDeserData(PhantomData))
216    }
217    type DeserType<'a> = PhantomDeserData<T::DeserType<'a>>;
218    #[inline(always)]
219    unsafe fn _deser_eps_inner<'a>(
220        _backend: &mut SliceWithPos<'a>,
221    ) -> deser::Result<Self::DeserType<'a>> {
222        Ok(PhantomDeserData(PhantomData))
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::pad_align_to;
229
230    #[test]
231    fn test_pad_align_to() {
232        assert_eq!(7 + pad_align_to(7, 8), 8);
233        assert_eq!(8 + pad_align_to(8, 8), 8);
234        assert_eq!(9 + pad_align_to(9, 8), 16);
235        assert_eq!(36 + pad_align_to(36, 16), 48);
236    }
237
238    #[test]
239    #[should_panic]
240    fn test_pad_align_to_rejects_non_power_of_two() {
241        let _ = pad_align_to(2, 3);
242    }
243}