nestle_core/
lib.rs

1use crate::error::{DecodeError, EncodeError, ValueTooWide};
2
3pub mod error;
4pub mod macros;
5
6pub trait Nestle: Sized {
7    /// The total bit-width of this type including all fields.
8    const WIDTH: u8;
9    /// An identifier for this type. Useful for debugging decoding/encoding
10    /// errors.
11    const TYPE_NAME: &'static str;
12
13    /// Decode this type from the given `id` from `offset` bytes.
14    fn decode_from(id: i64, offset: &mut u8) -> Result<Self, DecodeError>;
15
16    /// Encode this type into the given `id` from `offset` bytes.
17    fn encode_into(&self, id: &mut i64, offset: u8) -> Result<(), EncodeError>;
18
19    /// Decode this type as the top-level type.
20    fn decode(id: i64) -> Result<Self, DecodeError> {
21        let mut offset = 0;
22        let res = Self::decode_from(id, &mut offset)?;
23        // check any trailing bits are zero
24        let mask = {
25            let (res, overflow) = 1_i64.overflowing_shl(64 - offset as u32);
26            if overflow {
27                0
28            } else {
29                res
30            }
31        };
32        let value = id & (mask - 1);
33        if value == 0 {
34            Ok(res)
35        } else {
36            Err(DecodeError::TrailingBits {
37                typ: Self::TYPE_NAME,
38                value,
39            })
40        }
41    }
42
43    /// Encode this type as the top-level type.
44    fn encode(&self) -> Result<i64, EncodeError> {
45        let mut id = 0;
46        self.encode_into(&mut id, 0)?;
47        Ok(id)
48    }
49
50    /// Any types using the macros to implement `Nestle` are guaranteed to be
51    /// able to decode from an `i64` without error.
52    fn encode_unchecked(&self) -> i64 {
53        self.encode().unwrap()
54    }
55}
56
57pub fn check_i64_width(type_name: &'static str, width: u8, offset: u8) -> Result<(), ValueTooWide> {
58    let remaining = 64 - offset;
59    if width <= remaining {
60        Ok(())
61    } else {
62        Err(ValueTooWide {
63            type_name,
64            offset,
65            width,
66            remaining,
67        })
68    }
69}
70
71macro_rules! impl_for_primitive {
72    ($typ:ty, $cst:ty, $width:literal) => {
73        impl_for_primitive_with_cast!(
74            $typ,
75            |this: $typ| { this as $cst as i64 },
76            |value: i64| { Some(value as $typ) },
77            $width
78        );
79    };
80}
81
82macro_rules! impl_for_primitive_with_cast {
83    ($typ:ty, $cst_into:expr, $cst_from:expr, $width:literal) => {
84        impl Nestle for $typ {
85            const TYPE_NAME: &'static str = stringify!($typ);
86            const WIDTH: u8 = $width;
87
88            fn decode_from(id: i64, offset: &mut u8) -> Result<Self, DecodeError> {
89                check_i64_width(Self::TYPE_NAME, Self::WIDTH, *offset)?;
90                let shift = 64 - *offset - Self::WIDTH;
91                let shifted = id >> shift;
92                let res = {
93                    let (res, overflow) = 1_i64.overflowing_shl(Self::WIDTH as u32);
94                    if overflow {
95                        0
96                    } else {
97                        res
98                    }
99                };
100                let value = $cst_from(shifted & (res - 1)).ok_or_else(|| {
101                    DecodeError::NotFound {
102                        typ: Self::TYPE_NAME,
103                        disc: shifted & (res - 1),
104                    }
105                });
106                *offset += Self::WIDTH;
107                value
108            }
109
110            fn encode_into(&self, id: &mut i64, offset: u8) -> Result<(), EncodeError> {
111                check_i64_width(Self::TYPE_NAME, Self::WIDTH, offset)?;
112                let shift = 64 - offset - Self::WIDTH;
113                *id |= $cst_into(*self) << shift;
114                Ok(())
115            }
116        }
117    };
118}
119
120impl_for_primitive!(i8, u8, 8);
121impl_for_primitive!(i16, u16, 16);
122impl_for_primitive!(i32, u32, 32);
123impl_for_primitive!(i64, u64, 64);
124impl_for_primitive!(u8, u8, 8);
125impl_for_primitive!(u16, u16, 16);
126impl_for_primitive!(u32, u32, 32);
127impl_for_primitive!(u64, u64, 64);
128impl_for_primitive_with_cast!(
129    f32,
130    |this: f32| { this.to_bits() as i64 },
131    |value: i64| { Some(f32::from_bits(value as u32)) },
132    32
133);
134impl_for_primitive_with_cast!(
135    f64,
136    |this: f64| { this.to_bits() as i64 },
137    |value: i64| { Some(f64::from_bits(value as u64)) },
138    64
139);
140impl_for_primitive_with_cast!(
141    bool,
142    |this: bool| { this as i64 },
143    |value: i64| { Some(value != 0) },
144    8
145);
146impl_for_primitive_with_cast!(
147    char,
148    |this: char| { this as i64 },
149    |value: i64| { char::from_u32(value as u32) },
150    32
151);
152
153impl Nestle for () {
154    const TYPE_NAME: &'static str = "()";
155    const WIDTH: u8 = 0;
156
157    fn decode_from(_: i64, _: &mut u8) -> Result<Self, DecodeError> {
158        Ok(())
159    }
160
161    fn encode_into(&self, _: &mut i64, _: u8) -> Result<(), EncodeError> {
162        Ok(())
163    }
164}