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}