Skip to main content

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}