1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/*
 * SPDX-FileCopyrightText: 2023 Inria
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
 */

/*!

Traits to mark types as zero-copy or deep-copy.

*/

use crate::prelude::MaxSizeOf;
use sealed::sealed;

/// Internal trait used to select whether a type is zero-copy
/// or deep-copy.
///
/// It has only two implementations, [`Zero`] and [`Deep`].
///
/// In the first case, the type can be serialized
/// from memory and deserialized to memory as a sequence of bytes;
/// in the second case, one has to deserialize the type field
/// by field.
#[sealed]
pub trait CopySelector {
    const IS_ZERO_COPY: bool;
}
/// An implementation of a [`CopySelector`] specifying that a type is zero-copy.
pub struct Zero {}

#[sealed]
impl CopySelector for Zero {
    const IS_ZERO_COPY: bool = true;
}

/// An implementation of a [`CopySelector`] specifying that a type is deep-copy.
pub struct Deep {}

#[sealed]
impl CopySelector for Deep {
    const IS_ZERO_COPY: bool = false;
}

/**

Marker trait for data specifying whether it is zero-copy or deep-copy.

The trait comes in two flavors: `CopySelector<Type=Zero>` and
`CopySelector<Type=Deep>`. To each of these flavors corresponds two
dependent traits, [`ZeroCopy`] (which requires implementing [`MaxSizeOf`])
and [`DeepCopy`], which are automatically
implemented.
```rust
use epserde::traits::*;

struct MyType {}

impl CopyType for MyType {
    type Copy = Deep;
}
// Now MyType implements DeepCopy
```
You should not implement this trait manually, but rather use the provided [derive macro](epserde_derive::Epserde).

We use this trait to implement a different behavior for [`ZeroCopy`] and [`DeepCopy`] types,
in particular on arrays, vectors, and boxed slices,
[working around the bug that prevents the compiler from understanding that implementations
for the two flavors of `CopySelector` are mutually
exclusive](https://github.com/rust-lang/rfcs/pull/1672#issuecomment-1405377983).

For an array of elements of type `T` to be zero-copy serializable and
deserializable, `T` must implement `CopySelector<Type=Zero>`. The conditions for this marker trait are that
`T` is a [copy type](Copy), that it has a fixed memory layout,
and that it does not contain any reference (in particular, that it has `'static` lifetime).
If this happen vectors of `T` or boxed slices of `T` can be ε-copy deserialized
using a reference to a slice of `T`.

You can make zero-copy your own types, but you must ensure that they do not
contain references and that they have a fixed memory layout; for structures, this requires
`repr(C)`. ε-serde will track these conditions at compile time and check them at
runtime: in case of failure, serialization will panic.

Since we cannot use negative trait bounds, every type that is used as a parameter of
an array, vector or boxed slice must implement either `CopySelector<Type=Zero>`
or `CopySelector<Type=Deep>`. In the latter
case, slices will be deserialized element by element, and the result will be a fully
deserialized vector or boxed
slice. If you do not implement either of these traits, the type will not be serializable inside
vectors or boxed slices but error messages will be very unhelpful due to the
contrived way we have to implement mutually exclusive types.

If you use the provided derive macros all this logic will be hidden from you. You'll
just have to add `#[zero_copy]` to your structures (if you want them to be zero-copy)
and ε-serde will do the rest.

*/

pub trait CopyType: Sized {
    type Copy: CopySelector;
}

/// Marker trait for zero-copy types. You should never implement
/// this trait directly, but rather implement [`CopyType`] with `Copy=Zero`.
pub trait ZeroCopy: CopyType<Copy = Zero> + Copy + MaxSizeOf + 'static {}
impl<T: CopyType<Copy = Zero> + Copy + MaxSizeOf + 'static> ZeroCopy for T {}

/// Marker trait for deep-copy types. You should never implement
/// this trait directly, but rather implement [`CopyType`] with `Copy=Deep`.
pub trait DeepCopy: CopyType<Copy = Deep> {}
impl<T: CopyType<Copy = Deep>> DeepCopy for T {}