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(feature="std", 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}
53
54/// (Major, Minor) version of the file format, this follows semantic versioning
55pub const VERSION: (u16, u16) = (1, 1);
56
57/// Magic cookie, also used as endian ess marker.
58pub const MAGIC: u64 = u64::from_ne_bytes(*b"epserde ");
59/// What we will read if the endianness is mismatched.
60pub const MAGIC_REV: u64 = u64::from_le_bytes(MAGIC.to_be_bytes());
61
62/// Computes the padding needed for alignment, that is, the smallest
63/// number such that `((value + pad_align_to(value, align_to) & (align_to - 1) == 0`.
64pub fn pad_align_to(value: usize, align_to: usize) -> usize {
65    value.wrapping_neg() & (align_to - 1)
66}
67
68/// A type semantically equivalent to [`PhantomData`], but whose type parameter
69/// is replaced with its associated deserialization type.
70///
71/// In some case, you might find yourself with a deep-copy type that has a type
72/// parameter `T` appearing both in a field and in a [`PhantomData`]. In this
73/// case, the type will not compile, as in its associated deserialization type
74/// `T` will be replaced by `T::DeserType`, but the [`PhantomData`] field will
75/// still contain `T`. To fix this issue, you can use [`PhantomDeserData`]
76/// instead.
77///
78/// Note that `T` must be sized.
79///
80/// # Examples
81///
82/// This code will not compile:
83/// ```compile_fail
84/// use epserde::prelude::*;
85/// #[derive(Epserde, Debug, PartialEq, Eq, Clone, Default)]
86/// struct Data<T> {
87///     data: T,
88///     phantom: PhantomData<T>,
89/// }
90/// ```
91///
92/// This code, instead, will compile:
93/// ```
94/// use epserde::prelude::*;
95/// #[derive(Epserde, Debug, PartialEq, Eq, Clone, Default)]
96/// struct Data<T> {
97///     data: T,
98///     phantom: PhantomDeserData<T>,
99/// }
100/// ```
101#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
102pub struct PhantomDeserData<T: ?Sized>(pub PhantomData<T>);
103
104impl<T: DeserInner> PhantomDeserData<T> {
105    /// A custom deserialization method for [`PhantomDeserData`] that transmutes
106    /// the inner type.
107    ///
108    /// # Safety
109    ///
110    /// See [`DeserInner::_deser_eps_inner`].
111    #[inline(always)]
112    pub unsafe fn _deser_eps_inner_special<'a>(
113        _backend: &mut SliceWithPos<'a>,
114    ) -> deser::Result<PhantomDeserData<T::DeserType<'a>>> {
115        // SAFETY: types are zero-length
116        Ok(unsafe {
117            transmute::<DeserType<'a, PhantomDeserData<T>>, PhantomDeserData<T::DeserType<'a>>>(
118                PhantomDeserData(PhantomData),
119            )
120        })
121    }
122}
123
124unsafe impl<T> CopyType for PhantomDeserData<T> {
125    type Copy = Zero;
126}
127
128impl<T> AlignTo for PhantomDeserData<T> {
129    #[inline(always)]
130    fn align_to() -> usize {
131        0
132    }
133}
134
135impl<T: TypeHash> TypeHash for PhantomDeserData<T> {
136    #[inline(always)]
137    fn type_hash(hasher: &mut impl core::hash::Hasher) {
138        "PhantomDeserData".hash(hasher);
139        T::type_hash(hasher);
140    }
141}
142
143impl<T> AlignHash for PhantomDeserData<T> {
144    #[inline(always)]
145    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
146}
147
148impl<T> SerInner for PhantomDeserData<T> {
149    // This type is nominal only; nothing will be serialized
150    // or deserialized.
151    type SerType = Self;
152    const IS_ZERO_COPY: bool = true;
153
154    #[inline(always)]
155    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
156        Ok(())
157    }
158}
159
160impl<T: DeserInner> DeserInner for PhantomDeserData<T> {
161    #[inline(always)]
162    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
163        Ok(PhantomDeserData(PhantomData))
164    }
165    type DeserType<'a> = PhantomDeserData<T::DeserType<'a>>;
166    #[inline(always)]
167    unsafe fn _deser_eps_inner<'a>(
168        _backend: &mut SliceWithPos<'a>,
169    ) -> deser::Result<Self::DeserType<'a>> {
170        Ok(PhantomDeserData(PhantomData))
171    }
172}
173
174#[test]
175
176fn test_pad_align_to() {
177    assert_eq!(7 + pad_align_to(7, 8), 8);
178    assert_eq!(8 + pad_align_to(8, 8), 8);
179    assert_eq!(9 + pad_align_to(9, 8), 16);
180    assert_eq!(36 + pad_align_to(36, 16), 48);
181}