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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
/// A type implementing Tag represents a static hash set of byte strings,
/// with a very simple perfect hash function: every element has a unique
/// discriminant at a common byte offset. The values of the type represent
/// the members by this single discriminant byte; they are indices into the
/// hash set.
pub trait Tag: Sized {
/// Discriminant offset
const OFF: usize;
/// Minimum variant length
const MIN: usize;
/// Minimal suitably sized buffer for reading the wire representation
///
/// HACK: This is a workaround for const generics limitations.
type Buf: AsMut<[u8]> + Send;
/// Make an instance of [Self::Buf]
fn make_buf() -> Self::Buf;
/// Convert a discriminant into the corresponding variant
fn from_u8(x: u8) -> Option<Self>;
/// Convert a variant back into the wire representation
fn as_bytes(&self) -> &'static [u8];
}
/// Generate an enum implementing [Tag], enforcing at compile time that
/// the discriminant values are distinct.
macro_rules! make {
(
$(
$(#[doc = $doc:expr])*
$vis:vis enum $Enum:ident[$off:expr] {
$(
$(#[doc = $var_doc:expr])*
$Var:ident = $TOK:ident,
)+
}
)*
) => {
$(
$(#[doc = $doc])*
#[derive(Debug, PartialEq, Eq)]
#[repr(u8)]
$vis enum $Enum {
$(
$(#[doc = $var_doc])*
$Var = $TOK[$Enum::OFF]
),+
}
impl Tag for $Enum {
/// Discriminant offset
const OFF: usize = $off;
/// Minimum variant length
const MIN: usize = tag::min_of(&[$($TOK.len()),+]);
/// Minimal suitably sized buffer for reading the wire representation
type Buf = [u8; tag::buf_of(&[$($TOK.len()),+])];
/// Make an instance of [Self::Buf]
#[inline(always)]
fn make_buf() -> Self::Buf {
[0u8; tag::buf_of(&[$($TOK.len()),+])]
}
/// Convert a discriminant into the corresponding variant
#[inline(always)]
fn from_u8(x: u8) -> Option<Self> {
#[allow(non_upper_case_globals)]
mod __variant {
$(
pub const $Var: u8 = super::$Enum::$Var as u8;
)+
}
match x {
$(__variant::$Var => Some(Self::$Var),)+
_ => None
}
}
/// Convert a variant back into the wire representation
#[inline(always)]
fn as_bytes(&self) -> &'static [u8] {
match self {
$(Self::$Var => &$TOK,)+
}
}
}
)*
};
}
// The following functions are written somewhat unusually,
// since they're const functions that cannot use iterators.
/// Maximum element of a slice
const fn max_of(mut xs: &[usize]) -> usize {
let mut y = usize::MIN;
while let &[x, ref tail @ ..] = xs {
y = if x > y { x } else { y };
xs = tail;
}
y
}
/// Minimum element of a slice
pub const fn min_of(mut xs: &[usize]) -> usize {
let mut y = usize::MAX;
while let &[x, ref tail @ ..] = xs {
y = if x < y { x } else { y };
xs = tail;
}
y
}
/// Minimum buffer size to contain either of `0..Tag::MIN` and `Tag::MIN..`
/// at a particular time, for all possible tag wire representations, given
/// the sizes of all wire representations.
///
/// # Example
///
/// ```plain
/// OFF = 16
/// MIN = 24
/// MAX = 64
///
/// BUF = max(MIN, MAX-MIN)
/// = max(24, 64-24)
/// = max(24, 40)
/// = 40
/// ```
pub const fn buf_of(xs: &[usize]) -> usize {
max_of(&[min_of(xs), max_of(xs) - min_of(xs)])
}
pub(crate) use make;
#[cfg(test)]
mod test {
use super::super::tag::{self, Tag};
const TOK_A: [u8; 3] = [0xed, 0xef, 0x1c];
const TOK_B: [u8; 3] = [0xed, 0xf0, 0x1c];
const OFFSET: usize = 1;
make! {
enum Token[OFFSET] {
A = TOK_A,
B = TOK_B,
}
}
#[test]
fn example() {
assert_eq!(Token::from_u8(0xed), None);
let tag = Token::from_u8(0xef).unwrap();
assert_eq!(tag.as_bytes(), &TOK_A[..]);
let tag = Token::from_u8(0xf0).unwrap();
assert_eq!(tag.as_bytes(), &TOK_B[..]);
}
}