packing/endian/
big.rs

1use crate::{
2    Bit,
3    Endian,
4};
5
6/// Defines big endian bit shifting required for packing and unpacking non aligned fields in byte slices
7///
8/// Not construcatable, used only at type level
9pub enum BigEndian {}
10impl Endian for BigEndian {
11    const IS_LITTLE: bool = false;
12    fn align_field_bits<S: Bit, E: Bit>(input_bytes: &[u8], output_bytes: &mut [u8]) {
13        // Not valid to call this with no data
14        assert!(input_bytes.len() > 0);
15        // or no 0 wide output
16        assert!(output_bytes.len() > 0);
17        // Not valid to call with 1 byte and S E bits overlapping
18        assert!(input_bytes.len() > 1 || S::USIZE >= E::USIZE);
19
20        let i_len = input_bytes.len();
21        let o_len = output_bytes.len();
22
23        // Case 1: Only a single byte, can't shrink
24        if i_len == 1 {
25            output_bytes[o_len - 1] =
26                // Mask away anything before the start bit
27                (input_bytes[0] & S::HEAD_MASK)
28                // Shift the field to the LSB
29                >> E::USIZE;
30
31        // Case 2: More than 1 byte but the LSB at the end of the input
32        // is already in position 0 so no shifting is required
33        } else if E::USIZE == 0 {
34            // Since we aren't shrinking the data down by aligning the fields, the output 
35            // buffer must be at least as long as the input
36            assert!(output_bytes.len() >= input_bytes.len());
37
38            // We need to align the ends of the arrays for big endian
39            let o_start = o_len - i_len;
40
41            // Memcopy all the data
42            output_bytes[o_start..].copy_from_slice(input_bytes);
43
44            if S::USIZE != 7 {
45                // Sort out the masked first byte
46                output_bytes[o_start] &= S::HEAD_MASK;
47            }
48
49        // Note: Case 3 and 4 could be merged with some minor tweaks around the input start 
50        //       and an extra negative offset to output index for case 4 but I've left them
51        //       split up for ease of debugging while testing edge cases for now.
52        // Case 3: More than 1 byte and LSB at the end ISN'T at position 0 and we aren't
53        // shrinking the data down by aligning the fields. We need to shift every byte
54        } else if S::USIZE >= E::USIZE {
55            // Since we aren't shrinking the data down by aligning the fields, the output 
56            // buffer must be at least as long as the input
57            assert!(output_bytes.len() >= input_bytes.len());
58
59            // We need to align the ends of the arrays for big endian
60            let o_start = o_len - i_len;
61
62            for i in 0..i_len {
63                output_bytes[o_start + i] = 
64                    match i {
65                        // No prior byte, just masked and shifted left by the number of bits
66                        // we need to fill the space in the last byte
67                        0 => (input_bytes[i] & S::HEAD_MASK) >> E::USIZE,
68                        // Prior byte is 0 so needs to be masked
69                        // Shift the prior byte right to get only the bits we need to fill the space
70                        // in the last byte. `8` because E is an inclusive bound labelled from LSB0
71                        1 => ((input_bytes[i-1] & S::HEAD_MASK) << (8-E::USIZE))
72                            | (input_bytes[i] >> E::USIZE),
73                        // Prior byte is whole so no masking required
74                        _ => (input_bytes[i-1] << (8-E::USIZE))
75                            | (input_bytes[i] >> E::USIZE),
76                    };
77            }         
78
79        // Case 4: More than 1 byte and LSB at the end ISN'T at position 0 and we ARE 
80        // shrinking the data down by 1 byte by aligning the fields. We need to shift every byte
81        } else {
82            // Since we are shrinking the data down by aligning the fields, the output 
83            // buffer can be 1 smaller than the input
84            assert!(output_bytes.len() >= input_bytes.len() - 1);
85
86            // We need to align the ends of the arrays for big endian
87            let o_start = o_len - (i_len - 1);
88
89            for i in 1..i_len {
90                output_bytes[o_start + i - 1] = 
91                    match i {
92                        // No prior byte, just masked and shifted left by the number of bits
93                        // we need to fill the space in the last byte
94                        // (unreachable in case 4) 0 => (input_bytes[i] & S::HEAD_MASK) >> E::USIZE,
95
96                        // Prior byte is 0 so needs to be masked
97                        // Shift the prior byte right to get only the bits we need to fill the space
98                        // in the last byte. `8` because E is an inclusive bound labelled from LSB0
99                        1 => ((input_bytes[i-1] & S::HEAD_MASK) << (8-E::USIZE))
100                            | (input_bytes[i] >> E::USIZE),
101                        // Prior byte is whole so no masking required
102                        _ => (input_bytes[i-1] << (8-E::USIZE))
103                            | (input_bytes[i] >> E::USIZE),
104                    };
105            }
106        }
107    }
108    fn restore_field_bits<S: Bit, E: Bit>(input_bytes: &[u8], output_bytes: &mut [u8]) {
109        // Not valid to call this with no data
110        assert!(input_bytes.len() > 0);
111        // or no 0 wide output
112        assert!(output_bytes.len() > 0);
113        // Not valid to call with 1 byte and S E bits overlapping
114        assert!(output_bytes.len() > 1 || S::USIZE >= E::USIZE);
115
116        let i_len = input_bytes.len();
117        let o_len = output_bytes.len();
118
119        // Case 1: LSB at the end of the data is already in position 0 so no shifting is required
120        if E::USIZE == 0 {
121            let n_bytes = o_len.min(i_len);
122
123            // Mask in the first byte
124            output_bytes[o_len - n_bytes] |=
125                input_bytes[i_len - n_bytes] & S::HEAD_MASK;
126
127            // Memcopy any remaining bytes
128            if n_bytes > 1 {
129                output_bytes[(o_len - n_bytes + 1)..]
130                    .copy_from_slice(&input_bytes[(i_len - n_bytes + 1)..]);
131            }
132
133        // Case 2: LSB isn't aligned with the end of the last byte so we have to shift every byte.
134        // Since in restore we have to use the length of the output as critical info (can't tell
135        // if a single byte S: 5, E: 4 is a 2 bit field or a 10 bit field without it) we just keep
136        // shifting until we run out of output bytes.
137        // TODO: Should field width in bits/bytes be a separate parameter? probably...
138        } else {
139            // i[n] contributes to o[n] and o[n-1]
140            // i[n] and o[n] might be offset from each other in either direction
141
142            let n_bytes = i_len.min(o_len);
143            // Start of input such that this + n bytes aligns with the end of the input
144            let i_start = i_len - n_bytes;
145            // Start of output such that this + n bytes aligns with the end of the output
146            let o_start = o_len - n_bytes;
147
148            for i in 0..n_bytes {
149                let i_i = i_start + i;
150                let o_i = o_start + i;
151
152                // The shifted current byte will always fit in output since n_bytes == min length
153                if o_i == 0 {
154                    // It's the first byte in the field, potentially requires masking
155                    output_bytes[o_i] |= (input_bytes[i_i] << E::USIZE) & S::HEAD_MASK;
156                } else {
157                    output_bytes[o_i] |= input_bytes[i_i] << E::USIZE;
158                }
159
160                // Overflow from current byte might not fit in previous output byte
161                if o_i == 1 {
162                    // It's the first byte in the field, potentially requires masking
163                    output_bytes[o_i-1] |= (input_bytes[i_i] >> 8 - E::USIZE) & S::HEAD_MASK;
164                } else if o_i > 1 {
165                    output_bytes[o_i-1] |= input_bytes[i_i] >> 8 - E::USIZE;
166                }
167            }
168        }
169    }
170}