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}