bits_io/buf/
byte_order.rs

1use bitvec::field::BitField;
2use funty::Integral;
3
4use crate::prelude::*;
5
6/// This trait defines operations to load and store integral values from a buffer, and enables
7/// implementing them in different ways for the different byte orders (Big Endian and Little
8/// Endian).
9pub trait ByteOrder {
10    fn load<O: BitStore, U: Integral>(src: &BitSlice<O>) -> U;
11    fn load_u16(src: &[u8]) -> u16;
12    fn load_u24(src: &[u8]) -> u24;
13    fn load_u32(src: &[u8]) -> u32;
14
15    fn store<O: BitStore, U: Integral>(dest: &mut BitSlice<O>, value: U);
16    fn store_u16(dest: &mut [u8], value: u16);
17    fn store_u24(dest: &mut [u8], value: u24);
18    fn store_u32(dest: &mut [u8], value: u32);
19}
20
21pub struct BigEndian {}
22
23pub struct LittleEndian {}
24
25pub type NetworkOrder = BigEndian;
26
27impl ByteOrder for BigEndian {
28    fn load<O: BitStore, U: Integral>(src: &BitSlice<O>) -> U {
29        src.load_be()
30    }
31
32    fn load_u16(src: &[u8]) -> u16 {
33        u16::from_be_bytes(src.try_into().unwrap())
34    }
35
36    fn load_u24(src: &[u8]) -> u24 {
37        let mut value = 0u32;
38        value |= src[0] as u32;
39        value <<= 8;
40        value |= src[1] as u32;
41        value <<= 8;
42        value |= src[2] as u32;
43
44        u24::new(value)
45    }
46
47    fn load_u32(src: &[u8]) -> u32 {
48        u32::from_be_bytes(src.try_into().unwrap())
49    }
50
51    fn store<O: BitStore, U: Integral>(dest: &mut BitSlice<O>, value: U) {
52        dest.store_be(value);
53    }
54
55    fn store_u16(dest: &mut [u8], value: u16) {
56        dest[0] = (value >> 8) as u8;
57        dest[1] = value as u8;
58    }
59
60    fn store_u24(dest: &mut [u8], value: u24) {
61        let value: u32 = value.into();
62        dest[0] = (value >> 16) as u8;
63        dest[1] = (value >> 8) as u8;
64        dest[2] = value as u8;
65    }
66
67    fn store_u32(dest: &mut [u8], value: u32) {
68        dest[0] = (value >> 24) as u8;
69        dest[1] = (value >> 16) as u8;
70        dest[2] = (value >> 8) as u8;
71        dest[3] = value as u8;
72    }
73}
74
75impl ByteOrder for LittleEndian {
76    fn load<O: BitStore, U: Integral>(src: &BitSlice<O>) -> U {
77        src.load_le()
78    }
79
80    fn load_u16(src: &[u8]) -> u16 {
81        u16::from_le_bytes(src.try_into().unwrap())
82    }
83
84    fn load_u24(src: &[u8]) -> u24 {
85        let mut value = 0u32;
86        value |= src[2] as u32;
87        value <<= 8;
88        value |= src[1] as u32;
89        value <<= 8;
90        value |= src[0] as u32;
91        u24::new(value)
92    }
93
94    fn load_u32(src: &[u8]) -> u32 {
95        u32::from_le_bytes(src.try_into().unwrap())
96    }
97
98    fn store<O: BitStore, U: Integral>(dest: &mut BitSlice<O>, value: U) {
99        dest.store_le(value)
100    }
101
102    fn store_u16(dest: &mut [u8], value: u16) {
103        dest[0] = value as u8;
104        dest[1] = (value >> 8) as u8;
105    }
106
107    fn store_u24(dest: &mut [u8], value: u24) {
108        let value: u32 = value.into();
109        dest[0] = value as u8;
110        dest[1] = (value >> 8) as u8;
111        dest[2] = (value >> 16) as u8;
112    }
113
114    fn store_u32(dest: &mut [u8], value: u32) {
115        dest[0] = value as u8;
116        dest[1] = (value >> 8) as u8;
117        dest[2] = (value >> 16) as u8;
118        dest[3] = (value >> 24) as u8;
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    // TODO: more test cases here (different data sizes)
127
128    #[test]
129    fn test_big_endian_write() {
130        let mut buf = [0u8; 2];
131        {
132            // Simulate writing 4 bits
133            let dest = &mut BitSlice::from_slice_mut(&mut buf);
134            BigEndian::store(&mut dest[..16], 0xABCDu16);
135            let value = u16::from_be_bytes(buf);
136            assert_eq!(value, 0xABCD);
137        }
138
139        // Now test that writing into a BitSlice<BitSafeU8> also works.
140        {
141            let dest = BitSlice::from_slice_mut(&mut buf);
142            let (_, dest) = unsafe { dest.split_at_unchecked_mut(0) };
143            BigEndian::store(&mut dest[..16], 0xABCDu16);
144            let value = u16::from_be_bytes(buf);
145            assert_eq!(value, 0xABCD);
146        }
147    }
148
149    #[test]
150    fn test_big_endian_read() {
151        let value = 0xABCDu16;
152        let value_bytes = value.to_be_bytes();
153        let src = BitSlice::from_slice(&value_bytes);
154        let read_value: u16 = BigEndian::load(src);
155        assert_eq!(value, read_value);
156    }
157
158    #[test]
159    fn test_little_endian_write() {
160        let mut buf = [0u8; 2];
161        {
162            // Simulate writing 4 bits
163            let dest = &mut BitSlice::from_slice_mut(&mut buf);
164            LittleEndian::store(&mut dest[..16], 0xABCDu16);
165            let value = u16::from_le_bytes(buf);
166            assert_eq!(value, 0xABCD);
167        }
168
169        // Now test that writing into a BitSlice<BitSafeU8> also works.
170        {
171            let dest = BitSlice::from_slice_mut(&mut buf);
172            let (_, dest) = unsafe { dest.split_at_unchecked_mut(0) };
173            LittleEndian::store(&mut dest[..16], 0xABCDu16);
174            let value = u16::from_le_bytes(buf);
175            assert_eq!(value, 0xABCD);
176        }
177    }
178
179    #[test]
180    fn test_little_endian_read() {
181        let value = 0xABCDu16;
182        let value_bytes = value.to_le_bytes();
183        let src = BitSlice::from_slice(&value_bytes);
184        let read_value: u16 = LittleEndian::load(src);
185        assert_eq!(value, read_value);
186    }
187}