1use displaydoc::Display;
3use thiserror::Error;
4
5#[derive(Debug, Error, Display)]
7pub enum CastError {
8 OutOfBounds {
10 offset: u32,
12 },
13}
14
15pub unsafe trait MinimallyAligned: Sized {}
21
22pub fn cast<T: MinimallyAligned>(buffer: &[u8], offset: u32) -> &T {
28 try_cast(buffer, offset).unwrap()
29}
30
31pub fn try_cast<T: MinimallyAligned>(buffer: &[u8], offset: u32) -> Result<&T, CastError> {
33 let base = buffer.as_ptr();
34 let len = buffer.len();
35
36 if offset as usize + std::mem::size_of::<T>() <= len {
37 unsafe {
38 let addr = base.offset(offset as isize);
39 Ok(&*(addr as *const T))
40 }
41 } else {
42 Err(CastError::OutOfBounds { offset })
43 }
44}
45
46pub fn cast_slice<T: MinimallyAligned>(buffer: &[u8], offset: u32, len: u32) -> &[T] {
52 try_cast_slice(buffer, offset, len).unwrap()
53}
54
55pub fn try_cast_slice<T: MinimallyAligned>(
57 buffer: &[u8],
58 offset: u32,
59 len: u32,
60) -> Result<&[T], CastError> {
61 let base = buffer.as_ptr();
62 let buf_len = buffer.len();
63
64 let ulen = len as usize;
65 let needed = std::mem::size_of::<T>() * ulen;
66
67 if offset as usize + needed <= buf_len {
68 unsafe {
69 let addr = base.offset(offset as isize) as *const T;
70 Ok(std::slice::from_raw_parts(addr, ulen))
71 }
72 } else {
73 Err(CastError::OutOfBounds { offset })
74 }
75}
76
77pub trait Buffer {
79 fn try_cast<T: MinimallyAligned>(&self, offset: u32) -> Result<&T, CastError>;
81
82 fn try_cast_slice<T: MinimallyAligned>(&self, offset: u32, len: u32)
84 -> Result<&[T], CastError>;
85
86 fn cast<T: MinimallyAligned>(&self, offset: u32) -> &T;
88
89 fn cast_slice<T: MinimallyAligned>(&self, offset: u32, len: u32) -> &[T];
91}
92
93impl Buffer for [u8] {
94 fn try_cast<T: MinimallyAligned>(&self, offset: u32) -> Result<&T, CastError> {
96 try_cast(self, offset)
97 }
98
99 fn try_cast_slice<T: MinimallyAligned>(
101 &self,
102 offset: u32,
103 len: u32,
104 ) -> Result<&[T], CastError> {
105 try_cast_slice(self, offset, len)
106 }
107
108 fn cast<T: MinimallyAligned>(&self, offset: u32) -> &T {
110 cast(self, offset)
111 }
112
113 fn cast_slice<T: MinimallyAligned>(&self, offset: u32, len: u32) -> &[T] {
115 cast_slice(self, offset, len)
116 }
117}
118
119pub trait Repr {
121 type Value;
123
124 fn extract(&self) -> Self::Value;
126}
127
128#[repr(C, align(1))]
130pub struct LEU16([u8; 2]);
131
132#[repr(C, align(1))]
134#[derive(Debug)]
135pub struct LEU32([u8; 4]);
136
137#[repr(C, align(1))]
139pub struct LEI64([u8; 8]);
140
141unsafe impl MinimallyAligned for LEU16 {}
142
143impl Repr for LEU16 {
144 type Value = u16;
145 fn extract(&self) -> Self::Value {
146 u16::from_le_bytes(self.0)
147 }
148}
149
150unsafe impl MinimallyAligned for LEU32 {}
151
152impl Repr for LEU32 {
153 type Value = u32;
154 fn extract(&self) -> Self::Value {
155 u32::from_le_bytes(self.0)
156 }
157}
158
159unsafe impl MinimallyAligned for LEI64 {}
160
161impl Repr for LEI64 {
162 type Value = i64;
163 fn extract(&self) -> Self::Value {
164 i64::from_le_bytes(self.0)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test() {
174 let buffer: &[u8] = &[0, 20, 0, 30, 0, 1, 1];
175 let le: &LEU16 = cast(buffer, 1);
176 assert_eq!(le.extract(), 20);
177 let le: &LEU16 = cast(buffer, 3);
178 assert_eq!(le.extract(), 30);
179 let le: &LEU16 = cast(buffer, 5);
180 assert_eq!(le.extract(), 257);
181
182 let les: &[LEU16] = cast_slice(buffer, 1, 3);
183 assert_eq!(les[0].extract(), 20);
184 assert_eq!(les[1].extract(), 30);
185 assert_eq!(les[2].extract(), 257);
186
187 assert_eq!(std::mem::align_of::<LEU16>(), 1);
188 }
189}