bit_buf/read.rs
1use crate::{BitBuf, Error, Result, Storage};
2
3impl<S: Storage> BitBuf<S> {
4 /// Read a BE-bit-order [`u8`] from `byte_offset` without performing bound checks
5 ///
6 /// # Safety
7 ///
8 /// * This is UB if [`byte_offset >= self.bytes().len()`][Self::bytes]
9 ///
10 /// # Panics
11 ///
12 /// * Panics in debug mode if [`byte_offset >= self.bytes().len()`][Self::bytes]
13 #[inline(always)]
14 #[must_use]
15 pub unsafe fn read_u8_be_aligned_full_at_unchecked(&self, byte_offset: usize) -> u8 {
16 let bytes = self.bytes();
17
18 debug_assert!(
19 byte_offset < bytes.len(),
20 "BitBuf::read_u8_be_aligned_full_at_unchecked: index out of bounds! len is {}, offset is {}",
21 bytes.len(),
22 byte_offset,
23 );
24
25 unsafe { *bytes.get_unchecked(byte_offset) }
26 }
27
28 /// Read the next BE-bit-order [`u8`] without performing bound checks, advancing the internal cursor
29 ///
30 /// # Safety
31 ///
32 /// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
33 /// * This is UB if the internal cursor points past the end of storage (<code>[self.byte_pos()][Self::byte_pos] >= [self.bytes().len()][Self::bytes]</code>)
34 ///
35 /// # Panics
36 ///
37 /// * Panics in debug mode if the internal cursor is not byte-aligned ([`!self.is_aligned()`][Self::is_aligned])
38 /// * Panics in debug mode if the internal cursor points past the end of storage (<code>[self.byte_pos()][Self::byte_pos] >= [self.bytes().len()][Self::bytes]</code>)
39 #[inline(always)]
40 #[must_use]
41 pub unsafe fn read_u8_be_aligned_full_unchecked(&mut self) -> u8 {
42 debug_assert!(
43 self.is_aligned(),
44 "BitBuf::read_u8_be_aligned_full_unchecked called at unaligned bit position: {}",
45 self.pos(),
46 );
47
48 let b = unsafe { self.read_u8_be_aligned_full_at_unchecked(self.byte_pos()) };
49 self.advance_bytes(1);
50 b
51 }
52
53 /// Read a BE-bit-order [`u8`] from `offset` without performing bound checks
54 ///
55 /// # Safety
56 ///
57 /// * This is UB if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
58 /// * This is UB if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
59 ///
60 /// # Panics
61 ///
62 /// * Panics in debug mode if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
63 /// * Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
64 #[inline(always)]
65 #[must_use]
66 pub unsafe fn read_u8_be_full_at_unchecked(&self, offset: usize) -> u8 {
67 let byte_idx = offset / 8;
68 let shift = offset % 8;
69
70 let bytes = self.bytes();
71
72 debug_assert!(
73 byte_idx < bytes.len(),
74 "BitBuf::read_u8_be_full_at_unchecked: index out of bounds! len is {}, byte_idx is {}",
75 bytes.len(),
76 byte_idx,
77 );
78
79 if shift == 0 {
80 unsafe { self.read_u8_be_aligned_full_at_unchecked(byte_idx) }
81 } else {
82 let next_idx = byte_idx + 1;
83
84 debug_assert!(
85 next_idx < bytes.len(),
86 "BitBuf::read_u8_be_full_at_unchecked: lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
87 bytes.len(),
88 next_idx,
89 );
90
91 let (high, low) = unsafe {
92 (
93 *bytes.get_unchecked(byte_idx),
94 *bytes.get_unchecked(next_idx),
95 )
96 };
97
98 (high << shift) | (low >> (8 - shift))
99 }
100 }
101
102 /// Read a BE-bit-order [`u8`] from `offset` while performing bound checks
103 ///
104 /// # Errors
105 ///
106 /// * Returns [`Error::OutOfBounds`]
107 /// * if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
108 /// * if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
109 #[inline(always)]
110 pub fn try_read_u8_be_full_at(&self, offset: usize) -> Result<u8> {
111 let byte_idx = offset / 8;
112 let shift = offset % 8;
113
114 let len = self.bytes().len();
115
116 if byte_idx >= len {
117 return Err(Error::OutOfBounds);
118 }
119
120 if shift != 0 && byte_idx + 1 >= len {
121 return Err(Error::OutOfBounds);
122 }
123
124 Ok(unsafe { self.read_u8_be_full_at_unchecked(offset) })
125 }
126
127 /// Read a BE-bit-order [`u8`] from `offset`, panicking on out of bounds
128 ///
129 /// # Panics
130 ///
131 /// * Panics if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
132 /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
133 #[inline(always)]
134 #[must_use]
135 pub fn read_u8_be_full_at(&self, offset: usize) -> u8 {
136 self
137 .try_read_u8_be_full_at(offset)
138 .expect("BitBuf::read_u8_be_full_at out of bounds")
139 }
140
141 /// Read a [BE-bit, BE-byte] order [`u16`] from `byte_offset` without performing bound checks
142 ///
143 /// # Safety
144 ///
145 /// * This is UB if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
146 ///
147 /// # Panics
148 ///
149 /// * Panics in debug mode if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
150 #[inline(always)]
151 #[must_use]
152 pub unsafe fn read_u16_be_aligned_full_at_unchecked(&self, byte_offset: usize) -> u16 {
153 let bytes = self.bytes();
154
155 debug_assert!(
156 byte_offset + 1 < bytes.len(),
157 "BitBuf::read_u16_be_aligned_full_at_unchecked: index out of bounds! len is {}, offset is {}",
158 bytes.len(),
159 byte_offset + 1,
160 );
161
162 unsafe {
163 let high = *bytes.get_unchecked(byte_offset);
164 let low = *bytes.get_unchecked(byte_offset + 1);
165 u16::from_be_bytes([high, low])
166 }
167 }
168
169 /// Read the next [BE-bit, BE-byte] order [`u16`] without performing bound checks, advancing the internal cursor
170 ///
171 /// # Safety
172 ///
173 /// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
174 /// * This is UB if the internal cursor points past the end of storage (<code>[self.byte_pos()][Self::byte_pos] + 1 >= [self.bytes().len()][Self::bytes]</code>)
175 ///
176 /// # Panics
177 ///
178 /// * Panics in debug mode if the internal cursor is not byte-aligned ([`!self.is_aligned()`][Self::is_aligned])
179 /// * Panics in debug mode if the internal cursor points past the end of storage (<code>[self.byte_pos()][Self::byte_pos] + 1 >= [self.bytes().len()][Self::bytes]</code>)
180 #[inline(always)]
181 #[must_use]
182 pub unsafe fn read_u16_be_aligned_full_unchecked(&mut self) -> u16 {
183 debug_assert!(
184 self.is_aligned(),
185 "BitBuf::read_u16_be_aligned_full_unchecked called at unaligned bit position: {}",
186 self.pos(),
187 );
188
189 let v = unsafe { self.read_u16_be_aligned_full_at_unchecked(self.byte_pos()) };
190 self.advance_bytes(2);
191 v
192 }
193
194 /// Read a [BE-bit, BE-byte] order [`u16`] from `offset` without performing bound checks
195 ///
196 /// # Safety
197 ///
198 /// * This is UB if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
199 /// * This is UB if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
200 ///
201 /// # Panics
202 ///
203 /// * Panics in debug mode if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
204 /// * Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
205 #[inline(always)]
206 #[must_use]
207 pub unsafe fn read_u16_be_full_at_unchecked(&self, offset: usize) -> u16 {
208 let first_idx = offset / 8;
209 let shift = offset % 8;
210
211 let bytes = self.bytes();
212
213 debug_assert!(
214 first_idx + 1 < bytes.len(),
215 "BitBuf::read_u16_be_full_at_unchecked: index out of bounds! len is {}, byte_idx is {}",
216 bytes.len(),
217 first_idx + 1,
218 );
219
220 if shift == 0 {
221 unsafe { self.read_u16_be_aligned_full_at_unchecked(first_idx) }
222 } else {
223 let second_idx = first_idx + 1;
224 let third_idx = first_idx + 2;
225
226 debug_assert!(
227 third_idx < bytes.len(),
228 "BitBuf::read_u16_be_full_at_unchecked: lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
229 bytes.len(),
230 third_idx,
231 );
232
233 let (a, b, c) = unsafe {
234 (
235 *bytes.get_unchecked(first_idx),
236 *bytes.get_unchecked(second_idx),
237 *bytes.get_unchecked(third_idx),
238 )
239 };
240
241 let high = ((a as u16) << shift) | ((b as u16) >> (8 - shift));
242 let low = ((b as u16) << shift) | ((c as u16) >> (8 - shift));
243
244 (high << 8) | (low & 0xFF)
245 }
246 }
247
248 /// Read a [BE-bit, BE-byte] order [`u16`] from `offset` while performing bound checks
249 ///
250 /// # Errors
251 ///
252 /// * Returns [`Error::OutOfBounds`]
253 /// * if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
254 /// * if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
255 #[inline(always)]
256 pub fn try_read_u16_be_full_at(&self, offset: usize) -> Result<u16> {
257 let first_idx = offset / 8;
258 let shift = offset % 8;
259
260 let len = self.bytes().len();
261
262 if first_idx + 1 >= len {
263 return Err(Error::OutOfBounds);
264 }
265
266 if shift != 0 && first_idx + 2 >= len {
267 return Err(Error::OutOfBounds);
268 }
269
270 Ok(unsafe { self.read_u16_be_full_at_unchecked(offset) })
271 }
272
273 /// Read a [BE-bit, BE-byte] order [`u16`] from `offset`, panicking on out of bounds
274 ///
275 /// # Panics
276 ///
277 /// * Panics if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
278 /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
279 #[inline(always)]
280 #[must_use]
281 pub fn read_u16_be_full_at(&self, offset: usize) -> u16 {
282 self
283 .try_read_u16_be_full_at(offset)
284 .expect("BitBuf::read_u16_be_full_at out of bounds")
285 }
286}