Skip to main content

bit_buf/
write.rs

1use crate::{BitBuf, Error, Result, StorageMut};
2
3impl<S: StorageMut> BitBuf<S> {
4  /// Write a [`u8`] in BE-bit-order at `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  pub unsafe fn write_u8_be_aligned_full_at_unchecked(
15    &mut self,
16    byte_offset: usize,
17    v: u8,
18  ) -> &mut Self {
19    let bytes = self.bytes_mut();
20
21    debug_assert!(
22      byte_offset < bytes.len(),
23      "BitBuf::write_u8_be_aligned_full_at_unchecked: index out of bounds! len is {}, offset is {}",
24      bytes.len(),
25      byte_offset,
26    );
27
28    unsafe { *bytes.get_unchecked_mut(byte_offset) = v };
29    self
30  }
31
32  /// Write a [`u8`] in BE-bit-order without performing bound checks, advancing the internal cursor
33  ///
34  /// # Safety
35  ///
36  /// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
37  /// * 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>)
38  ///
39  /// # Panics
40  ///
41  /// * Panics in debug mode if the internal cursor is not byte-aligned ([`!self.is_aligned()`][Self::is_aligned])
42  /// * 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>)
43  #[inline(always)]
44  pub unsafe fn write_u8_be_aligned_full_unchecked(&mut self, v: u8) -> &mut Self {
45    debug_assert!(
46      self.is_aligned(),
47      "BitBuf::write_u8_be_aligned_full_unchecked called at unaligned bit position: {}",
48      self.pos(),
49    );
50
51    unsafe { self.write_u8_be_aligned_full_at_unchecked(self.byte_pos(), v) };
52    self.advance_bytes(1);
53    self
54  }
55
56  /// Write a [`u8`] in BE-bit-order at `offset` without performing bound checks
57  ///
58  /// # Safety
59  ///
60  /// * This is UB if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
61  /// * This is UB if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
62  ///
63  /// # Panics
64  ///
65  /// * Panics in debug mode if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
66  /// * Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
67  #[inline(always)]
68  pub unsafe fn write_u8_be_full_at_unchecked(&mut self, offset: usize, v: u8) -> &mut Self {
69    let byte_idx = offset / 8;
70    let shift = offset % 8;
71
72    let bytes = self.bytes_mut();
73
74    debug_assert!(
75      byte_idx < bytes.len(),
76      "BitBuf::write_u8_be_full_at_unchecked: index out of bounds! len is {}, byte_idx is {}",
77      bytes.len(),
78      byte_idx,
79    );
80
81    if shift == 0 {
82      unsafe { self.write_u8_be_aligned_full_at_unchecked(byte_idx, v) };
83    } else {
84      let next_idx = byte_idx + 1;
85
86      debug_assert!(
87        next_idx < bytes.len(),
88        "BitBuf::write_u8_be_full_at_unchecked: lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
89        bytes.len(),
90        next_idx,
91      );
92
93      // first byte, higher part of value
94      unsafe {
95        // mask to keep high bits of the current byte
96        let mask = !0 << (8 - shift);
97        let b = bytes.get_unchecked(byte_idx) & mask;
98        // high bits of the value, shifted down
99        let new = v >> shift;
100        *bytes.get_unchecked_mut(byte_idx) = b | new;
101      }
102
103      // second byte, lower part of value
104      unsafe {
105        // mask to keep low bits of the current byte
106        let mask = !0 >> shift;
107        let b = bytes.get_unchecked(next_idx) & mask;
108        // low bits of the value, shifted up
109        let new = v << (8 - shift);
110        *bytes.get_unchecked_mut(next_idx) = b | new;
111      }
112    }
113
114    self
115  }
116
117  /// Write a [`u8`] in BE-bit-order without performing bound checks, advancing the internal cursor
118  ///
119  /// # Safety
120  ///
121  /// * This is UB if <code>(self.pos() / 8) >= [`self.bytes().len()`][Self::bytes]</code>
122  /// * This is UB if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
123  ///
124  /// # Panics
125  ///
126  /// * Panics in debug mode if <code>(self.pos() / 8) >= [`self.bytes().len()`][Self::bytes]</code>
127  /// * Panics in debug mode if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
128  #[inline(always)]
129  pub unsafe fn write_u8_be_full_unchecked(&mut self, v: u8) -> &mut Self {
130    unsafe { self.write_u8_be_full_at_unchecked(self.pos(), v) };
131    self.advance_bytes(1);
132    self
133  }
134
135  /// Write a BE-bit-order [`u8`] at `offset` while performing bound checks
136  ///
137  /// # Errors
138  ///
139  /// * Returns [`Error::OutOfBounds`]
140  ///   * if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
141  ///   * if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
142  #[inline(always)]
143  pub fn try_write_u8_be_full_at(&mut self, offset: usize, v: u8) -> Result<&mut Self> {
144    let byte_idx = offset / 8;
145    let shift = offset % 8;
146
147    let len = self.bytes().len();
148
149    if byte_idx >= len {
150      return Err(Error::OutOfBounds);
151    }
152
153    if shift != 0 && byte_idx + 1 >= len {
154      return Err(Error::OutOfBounds);
155    }
156
157    Ok(unsafe { self.write_u8_be_full_at_unchecked(offset, v) })
158  }
159
160  /// Write a BE-bit-order [`u8`] while performing bound checks, advancing the internal cursor
161  ///
162  /// # Errors
163  ///
164  /// * Returns [`Error::OutOfBounds`]
165  ///   * if <code>(self.pos() / 8) >= [`self.bytes().len()`][Self::bytes]</code>
166  ///   * if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
167  #[inline(always)]
168  pub fn try_write_u8_be_full(&mut self, v: u8) -> Result<&mut Self> {
169    self.try_write_u8_be_full_at(self.pos(), v)?;
170    self.advance_bytes(1);
171    Ok(self)
172  }
173
174  /// Write a BE-bit-order [`u8`] at `offset`, panicking on out of bounds
175  ///
176  /// # Panics
177  ///
178  /// * Panics if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
179  /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
180  #[inline(always)]
181  pub fn write_u8_be_full_at(&mut self, offset: usize, v: u8) -> &mut Self {
182    self
183      .try_write_u8_be_full_at(offset, v)
184      .expect("BitBuf::write_u8_be_full_at out of bounds")
185  }
186
187  /// Write a BE-bit-order [`u8`], panicking on out of bounds, advancing the internal cursor
188  ///
189  /// # Panics
190  ///
191  /// * Panics if <code>(self.pos() / 8) >= [`self.bytes().len()`][Self::bytes]</code>
192  /// * Panics if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
193  #[inline(always)]
194  pub fn write_u8_be_full(&mut self, v: u8) -> &mut Self {
195    self
196      .try_write_u8_be_full(v)
197      .expect("BitBuf::write_u8_be_full out of bounds")
198  }
199
200  /// Write a [`u16`] in [BE-bit, BE-byte] order at `byte_offset` without performing bound checks
201  ///
202  /// # Safety
203  ///
204  /// * This is UB if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
205  ///
206  /// # Panics
207  ///
208  /// * Panics in debug mode if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
209  #[inline(always)]
210  pub unsafe fn write_u16_be_aligned_full_at_unchecked(
211    &mut self,
212    byte_offset: usize,
213    v: u16,
214  ) -> &mut Self {
215    let bytes = self.bytes_mut();
216
217    debug_assert!(
218      byte_offset + 1 < bytes.len(),
219      "BitBuf::write_u16_be_aligned_full_at_unchecked: index out of bounds! len is {}, offset is {}",
220      bytes.len(),
221      byte_offset + 1,
222    );
223
224    let [high, low] = v.to_be_bytes();
225
226    unsafe {
227      *bytes.get_unchecked_mut(byte_offset) = high;
228      *bytes.get_unchecked_mut(byte_offset + 1) = low;
229    }
230
231    self
232  }
233
234  /// Write a [`u16`] in [BE-bit, BE-byte] order without performing bound checks, advancing the internal cursor
235  ///
236  /// # Safety
237  ///
238  /// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
239  /// * 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>)
240  ///
241  /// # Panics
242  ///
243  /// * Panics in debug mode if the internal cursor is not byte-aligned ([`!self.is_aligned()`][Self::is_aligned])
244  /// * 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>)
245  #[inline(always)]
246  pub unsafe fn write_u16_be_aligned_full_unchecked(&mut self, v: u16) -> &mut Self {
247    debug_assert!(
248      self.is_aligned(),
249      "BitBuf::write_u16_be_aligned_full_unchecked called at unaligned bit position: {}",
250      self.pos(),
251    );
252
253    unsafe { self.write_u16_be_aligned_full_at_unchecked(self.byte_pos(), v) };
254    self.advance_bytes(2);
255    self
256  }
257
258  /// Write a [`u16`] in [BE-bit, BE-byte] order at `offset` without performing bound checks
259  ///
260  /// # Safety
261  ///
262  /// * This is UB if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
263  /// * This is UB if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
264  ///
265  /// # Panics
266  ///
267  /// * Panics in debug mode if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
268  /// * Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
269  #[inline(always)]
270  pub unsafe fn write_u16_be_full_at_unchecked(&mut self, offset: usize, v: u16) -> &mut Self {
271    let first_idx = offset / 8;
272    let shift = offset % 8;
273
274    let bytes = self.bytes_mut();
275
276    debug_assert!(
277      first_idx + 1 < bytes.len(),
278      "BitBuf::write_u16_be_full_at_unchecked: index out of bounds! len is {}, byte_idx is {}",
279      bytes.len(),
280      first_idx + 1,
281    );
282
283    if shift == 0 {
284      unsafe { self.write_u16_be_aligned_full_at_unchecked(first_idx, v) };
285    } else {
286      let second_idx = first_idx + 1;
287      let third_idx = first_idx + 2;
288
289      debug_assert!(
290        third_idx < bytes.len(),
291        "BitBuf::write_u16_be_full_at_unchecked: lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
292        bytes.len(),
293        third_idx,
294      );
295
296      let [high, low] = v.to_be_bytes();
297
298      // first byte, higher part of value
299      unsafe {
300        // mask to keep high bits of the current byte
301        let mask = !0 << (8 - shift);
302        let b = bytes.get_unchecked(first_idx) & mask;
303        // high bits of the value, shifted down
304        let new = high >> shift;
305        *bytes.get_unchecked_mut(first_idx) = b | new;
306      }
307
308      // second byte, middle part of value
309      unsafe {
310        // no need to grab byte since all 8 bits will be overwritten
311        // high middle bits of the value, shifted up
312        let b_high = high << (8 - shift);
313        // low middle bits of the value, shifted down
314        let b_low = low >> shift;
315        *bytes.get_unchecked_mut(second_idx) = b_high | b_low;
316      }
317
318      // third byte, lower part of value
319      unsafe {
320        // mask to keep low bits of the current byte
321        let mask = !0 >> shift;
322        let b = bytes.get_unchecked(third_idx) & mask;
323        // low bits of the value, shifted up
324        let new = low << (8 - shift);
325        *bytes.get_unchecked_mut(third_idx) = b | new;
326      }
327    }
328
329    self
330  }
331
332  /// Write a [`u16`] in [BE-bit, BE-byte] order without performing bound checks, advancing the internal cursor
333  ///
334  /// # Safety
335  ///
336  /// * This is UB if <code>(self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
337  /// * This is UB if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
338  ///
339  /// # Panics
340  ///
341  /// * Panics in debug mode if <code>(self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
342  /// * Panics in debug mode if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
343  #[inline(always)]
344  pub unsafe fn write_u16_be_full_unchecked(&mut self, v: u16) -> &mut Self {
345    unsafe { self.write_u16_be_full_at_unchecked(self.pos(), v) };
346    self.advance_bytes(2);
347    self
348  }
349
350  /// Write a [BE-bit, BE-byte] order [`u16`] at `offset` while performing bound checks
351  ///
352  /// # Errors
353  ///
354  /// * Returns [`Error::OutOfBounds`]
355  ///   * if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
356  ///   * if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
357  #[inline(always)]
358  pub fn try_write_u16_be_full_at(&mut self, offset: usize, v: u16) -> Result<&mut Self> {
359    let first_idx = offset / 8;
360    let shift = offset % 8;
361
362    let len = self.bytes().len();
363
364    if first_idx + 1 >= len {
365      return Err(Error::OutOfBounds);
366    }
367
368    if shift != 0 && first_idx + 2 >= len {
369      return Err(Error::OutOfBounds);
370    }
371
372    Ok(unsafe { self.write_u16_be_full_at_unchecked(offset, v) })
373  }
374
375  /// Write a [BE-bit, BE-byte] order [`u16`] while performing bound checks, advancing the internal cursor
376  ///
377  /// # Errors
378  ///
379  /// * Returns [`Error::OutOfBounds`]
380  ///   * if <code>(self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
381  ///   * if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
382  #[inline(always)]
383  pub fn try_write_u16_be_full(&mut self, v: u16) -> Result<&mut Self> {
384    self.try_write_u16_be_full_at(self.pos(), v)?;
385    self.advance_bytes(2);
386    Ok(self)
387  }
388
389  /// Write a [BE-bit, BE-byte] order [`u16`] at `offset`, panicking on out of bounds
390  ///
391  /// # Panics
392  ///
393  /// * Panics if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
394  /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
395  #[inline(always)]
396  pub fn write_u16_be_full_at(&mut self, offset: usize, v: u16) -> &mut Self {
397    self
398      .try_write_u16_be_full_at(offset, v)
399      .expect("BitBuf::write_u16_be_full_at out of bounds")
400  }
401
402  /// Write a [BE-bit, BE-byte] order [`u16`], panicking on out of bounds, advancing the internal cursor
403  ///
404  /// # Panics
405  ///
406  /// * Panics if <code>(self.pos() / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
407  /// * Panics if <code>(self.pos() % 8 != 0) && (self.pos() / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
408  #[inline(always)]
409  pub fn write_u16_be_full(&mut self, v: u16) -> &mut Self {
410    self
411      .try_write_u16_be_full(v)
412      .expect("BitBuf::write_u16_be_full out of bounds")
413  }
414}