1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Endian {
18 Little,
20 Big,
22}
23
24impl Endian {
25 #[must_use]
27 pub fn u16(self, data: &[u8], off: usize) -> u16 {
28 match self {
29 Endian::Little => le_u16(data, off),
30 Endian::Big => be_u16(data, off),
31 }
32 }
33
34 #[must_use]
36 pub fn u32(self, data: &[u8], off: usize) -> u32 {
37 match self {
38 Endian::Little => le_u32(data, off),
39 Endian::Big => be_u32(data, off),
40 }
41 }
42
43 #[must_use]
45 pub fn i32(self, data: &[u8], off: usize) -> i32 {
46 self.u32(data, off) as i32
47 }
48
49 #[must_use]
51 pub fn u64(self, data: &[u8], off: usize) -> u64 {
52 match self {
53 Endian::Little => le_u64(data, off),
54 Endian::Big => be_u64(data, off),
55 }
56 }
57
58 #[must_use]
60 pub fn i64(self, data: &[u8], off: usize) -> i64 {
61 self.u64(data, off) as i64
62 }
63}
64
65#[must_use]
67pub fn le_u16(data: &[u8], off: usize) -> u16 {
68 let mut b = [0u8; 2];
69 if let Some(s) = data.get(off..off.saturating_add(2)) {
70 b.copy_from_slice(s);
71 }
72 u16::from_le_bytes(b)
73}
74
75#[must_use]
77pub fn be_u16(data: &[u8], off: usize) -> u16 {
78 let mut b = [0u8; 2];
79 if let Some(s) = data.get(off..off.saturating_add(2)) {
80 b.copy_from_slice(s);
81 }
82 u16::from_be_bytes(b)
83}
84
85#[must_use]
87pub fn le_u32(data: &[u8], off: usize) -> u32 {
88 let mut b = [0u8; 4];
89 if let Some(s) = data.get(off..off.saturating_add(4)) {
90 b.copy_from_slice(s);
91 }
92 u32::from_le_bytes(b)
93}
94
95#[must_use]
97pub fn be_u32(data: &[u8], off: usize) -> u32 {
98 let mut b = [0u8; 4];
99 if let Some(s) = data.get(off..off.saturating_add(4)) {
100 b.copy_from_slice(s);
101 }
102 u32::from_be_bytes(b)
103}
104
105#[must_use]
107pub fn le_u64(data: &[u8], off: usize) -> u64 {
108 let mut b = [0u8; 8];
109 if let Some(s) = data.get(off..off.saturating_add(8)) {
110 b.copy_from_slice(s);
111 }
112 u64::from_le_bytes(b)
113}
114
115#[must_use]
117pub fn be_u64(data: &[u8], off: usize) -> u64 {
118 let mut b = [0u8; 8];
119 if let Some(s) = data.get(off..off.saturating_add(8)) {
120 b.copy_from_slice(s);
121 }
122 u64::from_be_bytes(b)
123}
124
125#[must_use]
127pub fn u8_at(data: &[u8], off: usize) -> u8 {
128 data.get(off).copied().unwrap_or(0)
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn readers_decode_both_orders() {
137 let d = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
138 assert_eq!(le_u16(&d, 0), 0x2211);
139 assert_eq!(be_u16(&d, 0), 0x1122);
140 assert_eq!(le_u32(&d, 0), 0x4433_2211);
141 assert_eq!(be_u32(&d, 0), 0x1122_3344);
142 assert_eq!(le_u64(&d, 0), 0x8877_6655_4433_2211);
143 assert_eq!(be_u64(&d, 0), 0x1122_3344_5566_7788);
144 assert_eq!(u8_at(&d, 3), 0x44);
145 }
146
147 #[test]
148 fn readers_yield_zero_out_of_range() {
149 assert_eq!(le_u16(&[0x12], 0), 0);
150 assert_eq!(be_u16(&[0x12], 0), 0);
151 assert_eq!(le_u32(&[0, 0, 0], 0), 0);
152 assert_eq!(be_u32(&[0, 0, 0], 0), 0);
153 assert_eq!(le_u64(&[0; 7], 0), 0);
154 assert_eq!(be_u64(&[0; 7], 0), 0);
155 assert_eq!(u8_at(&[], 0), 0);
156 }
157
158 #[test]
159 fn endian_selector_dispatches_both_orders() {
160 let d = [0xaa, 0xbb, 0xcc, 0xdd, 0x01, 0x02, 0x03, 0x04];
161 assert_eq!(Endian::Little.u16(&d, 0), 0xbbaa);
162 assert_eq!(Endian::Big.u16(&d, 0), 0xaabb);
163 assert_eq!(Endian::Little.u32(&d, 0), 0xddcc_bbaa);
164 assert_eq!(Endian::Big.u32(&d, 0), 0xaabb_ccdd);
165 assert_eq!(Endian::Little.i32(&d, 0), 0xddcc_bbaa_u32 as i32);
166 assert_eq!(Endian::Big.i32(&d, 0), 0xaabb_ccdd_u32 as i32);
167 assert_eq!(Endian::Little.u64(&d, 0), 0x0403_0201_ddcc_bbaa);
168 assert_eq!(Endian::Big.u64(&d, 0), 0xaabb_ccdd_0102_0304);
169 assert_eq!(Endian::Little.i64(&d, 0), 0x0403_0201_ddcc_bbaa_u64 as i64);
170 assert_eq!(Endian::Big.i64(&d, 0), 0xaabb_ccdd_0102_0304_u64 as i64);
171 }
172}