nix_compat/nar/wire/
tag.rs

1/// A type implementing Tag represents a static hash set of byte strings,
2/// with a very simple perfect hash function: every element has a unique
3/// discriminant at a common byte offset. The values of the type represent
4/// the members by this single discriminant byte; they are indices into the
5/// hash set.
6pub trait Tag: Sized {
7    /// Discriminant offset
8    const OFF: usize;
9    /// Minimum variant length
10    const MIN: usize;
11
12    /// Minimal suitably sized buffer for reading the wire representation
13    ///
14    /// HACK: This is a workaround for const generics limitations.
15    type Buf: AsMut<[u8]> + Send;
16
17    /// Make an instance of [Self::Buf]
18    fn make_buf() -> Self::Buf;
19
20    /// Convert a discriminant into the corresponding variant
21    fn from_u8(x: u8) -> Option<Self>;
22
23    /// Convert a variant back into the wire representation
24    fn as_bytes(&self) -> &'static [u8];
25}
26
27/// Generate an enum implementing [Tag], enforcing at compile time that
28/// the discriminant values are distinct.
29macro_rules! make {
30    (
31        $(
32            $(#[doc = $doc:expr])*
33            $vis:vis enum $Enum:ident[$off:expr] {
34                $(
35                    $(#[doc = $var_doc:expr])*
36                    $Var:ident = $TOK:ident,
37                )+
38            }
39        )*
40    ) => {
41        $(
42            $(#[doc = $doc])*
43            #[derive(Debug, PartialEq, Eq)]
44            #[repr(u8)]
45            $vis enum $Enum {
46                $(
47                    $(#[doc = $var_doc])*
48                    $Var = $TOK[$Enum::OFF]
49                ),+
50            }
51
52            impl Tag for $Enum {
53                /// Discriminant offset
54                const OFF: usize = $off;
55                /// Minimum variant length
56                const MIN: usize = tag::min_of(&[$($TOK.len()),+]);
57
58                /// Minimal suitably sized buffer for reading the wire representation
59                type Buf = [u8; tag::buf_of(&[$($TOK.len()),+])];
60
61                /// Make an instance of [Self::Buf]
62                #[inline(always)]
63                fn make_buf() -> Self::Buf {
64                    [0u8; tag::buf_of(&[$($TOK.len()),+])]
65                }
66
67                /// Convert a discriminant into the corresponding variant
68                #[inline(always)]
69                fn from_u8(x: u8) -> Option<Self> {
70                    #[allow(non_upper_case_globals)]
71                    mod __variant {
72                        $(
73                            pub const $Var: u8 = super::$Enum::$Var as u8;
74                        )+
75                    }
76
77                    match x {
78                        $(__variant::$Var => Some(Self::$Var),)+
79                        _ => None
80                    }
81                }
82
83                /// Convert a variant back into the wire representation
84                #[inline(always)]
85                fn as_bytes(&self) -> &'static [u8] {
86                    match self {
87                        $(Self::$Var => &$TOK,)+
88                    }
89                }
90            }
91        )*
92    };
93}
94
95// The following functions are written somewhat unusually,
96// since they're const functions that cannot use iterators.
97
98/// Maximum element of a slice
99const fn max_of(mut xs: &[usize]) -> usize {
100    let mut y = usize::MIN;
101    while let &[x, ref tail @ ..] = xs {
102        y = if x > y { x } else { y };
103        xs = tail;
104    }
105    y
106}
107
108/// Minimum element of a slice
109pub const fn min_of(mut xs: &[usize]) -> usize {
110    let mut y = usize::MAX;
111    while let &[x, ref tail @ ..] = xs {
112        y = if x < y { x } else { y };
113        xs = tail;
114    }
115    y
116}
117
118/// Minimum buffer size to contain either of `0..Tag::MIN` and `Tag::MIN..`
119/// at a particular time, for all possible tag wire representations, given
120/// the sizes of all wire representations.
121///
122/// # Example
123///
124/// ```plain
125/// OFF = 16
126/// MIN = 24
127/// MAX = 64
128///
129/// BUF = max(MIN, MAX-MIN)
130///     = max(24, 64-24)
131///     = max(24, 40)
132///     = 40
133/// ```
134pub const fn buf_of(xs: &[usize]) -> usize {
135    max_of(&[min_of(xs), max_of(xs) - min_of(xs)])
136}
137
138pub(crate) use make;
139
140#[cfg(test)]
141mod test {
142    use super::super::tag::{self, Tag};
143
144    const TOK_A: [u8; 3] = [0xed, 0xef, 0x1c];
145    const TOK_B: [u8; 3] = [0xed, 0xf0, 0x1c];
146
147    const OFFSET: usize = 1;
148
149    make! {
150        enum Token[OFFSET] {
151            A = TOK_A,
152            B = TOK_B,
153        }
154    }
155
156    #[test]
157    fn example() {
158        assert_eq!(Token::from_u8(0xed), None);
159
160        let tag = Token::from_u8(0xef).unwrap();
161        assert_eq!(tag.as_bytes(), &TOK_A[..]);
162
163        let tag = Token::from_u8(0xf0).unwrap();
164        assert_eq!(tag.as_bytes(), &TOK_B[..]);
165    }
166}