1use crate::error::{DecodeError, EncodeError, ValueTooWide};
2
3pub mod error;
4pub mod macros;
5
6pub trait Nestle: Sized {
7 const WIDTH: u8;
9 const TYPE_NAME: &'static str;
12
13 fn decode_from(id: i64, offset: &mut u8) -> Result<Self, DecodeError>;
15
16 fn encode_into(&self, id: &mut i64, offset: u8) -> Result<(), EncodeError>;
18
19 fn decode(id: i64) -> Result<Self, DecodeError> {
21 let mut offset = 0;
22 let res = Self::decode_from(id, &mut offset)?;
23 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 fn encode(&self) -> Result<i64, EncodeError> {
45 let mut id = 0;
46 self.encode_into(&mut id, 0)?;
47 Ok(id)
48 }
49
50 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}