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 BE-bit-order [`u8`] at `offset` while performing bound checks
118  ///
119  /// # Errors
120  ///
121  /// * Returns [`Error::OutOfBounds`]
122  ///   * if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
123  ///   * if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
124  #[inline(always)]
125  pub fn try_write_u8_be_full_at(&mut self, offset: usize, v: u8) -> Result<&mut Self> {
126    let byte_idx = offset / 8;
127    let shift = offset % 8;
128
129    let len = self.bytes().len();
130
131    if byte_idx >= len {
132      return Err(Error::OutOfBounds);
133    }
134
135    if shift != 0 && byte_idx + 1 >= len {
136      return Err(Error::OutOfBounds);
137    }
138
139    Ok(unsafe { self.write_u8_be_full_at_unchecked(offset, v) })
140  }
141
142  /// Write a BE-bit-order [`u8`] at `offset`, panicking on out of bounds
143  ///
144  /// # Panics
145  ///
146  /// * Panics if <code>(offset / 8) >= [`self.bytes().len()`][Self::bytes]</code>
147  /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
148  #[inline(always)]
149  pub fn write_u8_be_full_at(&mut self, offset: usize, v: u8) -> &mut Self {
150    self
151      .try_write_u8_be_full_at(offset, v)
152      .expect("BitBuf::write_u8_be_full_at out of bounds")
153  }
154
155  /// Write a [`u16`] in [BE-bit, BE-byte] order at `byte_offset` without performing bound checks
156  ///
157  /// # Safety
158  ///
159  /// * This is UB if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
160  ///
161  /// # Panics
162  ///
163  /// * Panics in debug mode if [`byte_offset + 1 >= self.bytes().len()`][Self::bytes]
164  #[inline(always)]
165  pub unsafe fn write_u16_be_aligned_full_at_unchecked(
166    &mut self,
167    byte_offset: usize,
168    v: u16,
169  ) -> &mut Self {
170    let bytes = self.bytes_mut();
171
172    debug_assert!(
173      byte_offset + 1 < bytes.len(),
174      "BitBuf::write_u16_be_aligned_full_at_unchecked: index out of bounds! len is {}, offset is {}",
175      bytes.len(),
176      byte_offset + 1,
177    );
178
179    let [high, low] = v.to_be_bytes();
180
181    unsafe {
182      *bytes.get_unchecked_mut(byte_offset) = high;
183      *bytes.get_unchecked_mut(byte_offset + 1) = low;
184    }
185
186    self
187  }
188
189  /// Write a [`u16`] in [BE-bit, BE-byte] order without performing bound checks, advancing the internal cursor
190  ///
191  /// # Safety
192  ///
193  /// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
194  /// * 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>)
195  ///
196  /// # Panics
197  ///
198  /// * Panics in debug mode if the internal cursor is not byte-aligned ([`!self.is_aligned()`][Self::is_aligned])
199  /// * 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>)
200  #[inline(always)]
201  pub unsafe fn write_u16_be_aligned_full_unchecked(&mut self, v: u16) -> &mut Self {
202    debug_assert!(
203      self.is_aligned(),
204      "BitBuf::write_u16_be_aligned_full_unchecked called at unaligned bit position: {}",
205      self.pos(),
206    );
207
208    unsafe { self.write_u16_be_aligned_full_at_unchecked(self.byte_pos(), v) };
209    self.advance_bytes(2);
210    self
211  }
212
213  /// Write a [`u16`] in [BE-bit, BE-byte] order at `offset` without performing bound checks
214  ///
215  /// # Safety
216  ///
217  /// * This is UB if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
218  /// * This is UB if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
219  ///
220  /// # Panics
221  ///
222  /// * Panics in debug mode if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
223  /// * Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
224  #[inline(always)]
225  pub unsafe fn write_u16_be_full_at_unchecked(&mut self, offset: usize, v: u16) -> &mut Self {
226    let first_idx = offset / 8;
227    let shift = offset % 8;
228
229    let bytes = self.bytes_mut();
230
231    debug_assert!(
232      first_idx + 1 < bytes.len(),
233      "BitBuf::write_u16_be_full_at_unchecked: index out of bounds! len is {}, byte_idx is {}",
234      bytes.len(),
235      first_idx + 1,
236    );
237
238    if shift == 0 {
239      unsafe { self.write_u16_be_aligned_full_at_unchecked(first_idx, v) };
240    } else {
241      let second_idx = first_idx + 1;
242      let third_idx = first_idx + 2;
243
244      debug_assert!(
245        third_idx < bytes.len(),
246        "BitBuf::write_u16_be_full_at_unchecked: lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
247        bytes.len(),
248        third_idx,
249      );
250
251      let [high, low] = v.to_be_bytes();
252
253      // first byte, higher part of value
254      unsafe {
255        // mask to keep high bits of the current byte
256        let mask = !0 << (8 - shift);
257        let b = bytes.get_unchecked(first_idx) & mask;
258        // high bits of the value, shifted down
259        let new = high >> shift;
260        *bytes.get_unchecked_mut(first_idx) = b | new;
261      }
262
263      // second byte, middle part of value
264      unsafe {
265        // no need to grab byte since all 8 bits will be overwritten
266        // high middle bits of the value, shifted up
267        let b_high = high << (8 - shift);
268        // low middle bits of the value, shifted down
269        let b_low = low >> shift;
270        *bytes.get_unchecked_mut(second_idx) = b_high | b_low;
271      }
272
273      // third byte, lower part of value
274      unsafe {
275        // mask to keep low bits of the current byte
276        let mask = !0 >> shift;
277        let b = bytes.get_unchecked(third_idx) & mask;
278        // low bits of the value, shifted up
279        let new = low << (8 - shift);
280        *bytes.get_unchecked_mut(third_idx) = b | new;
281      }
282    }
283
284    self
285  }
286
287  /// Write a [BE-bit, BE-byte] order [`u16`] at `offset` while performing bound checks
288  ///
289  /// # Errors
290  ///
291  /// * Returns [`Error::OutOfBounds`]
292  ///   * if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
293  ///   * if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
294  #[inline(always)]
295  pub fn try_write_u16_be_full_at(&mut self, offset: usize, v: u16) -> Result<&mut Self> {
296    let first_idx = offset / 8;
297    let shift = offset % 8;
298
299    let len = self.bytes().len();
300
301    if first_idx + 1 >= len {
302      return Err(Error::OutOfBounds);
303    }
304
305    if shift != 0 && first_idx + 2 >= len {
306      return Err(Error::OutOfBounds);
307    }
308
309    Ok(unsafe { self.write_u16_be_full_at_unchecked(offset, v) })
310  }
311
312  /// Write a [BE-bit, BE-byte] order [`u16`] at `offset`, panicking on out of bounds
313  ///
314  /// # Panics
315  ///
316  /// * Panics if <code>(offset / 8) + 1 >= [`self.bytes().len()`][Self::bytes]</code>
317  /// * Panics if <code>(offset % 8 != 0) && (offset / 8) + 2 >= [`self.bytes().len()`][Self::bytes]</code>
318  #[inline(always)]
319  pub fn write_u16_be_full_at(&mut self, offset: usize, v: u16) -> &mut Self {
320    self
321      .try_write_u16_be_full_at(offset, v)
322      .expect("BitBuf::write_u16_be_full_at out of bounds")
323  }
324}