oximedia_bitstream/checked.rs
1// Copyright 2017 Brian Langenberger
2// Copyright 2024-2026 COOLJAPAN OU (Team Kitasan)
3//
4// Licensed under the Apache License, Version 2.0 or the MIT license,
5// at your option. See the LICENSE-APACHE / LICENSE-MIT files for details.
6
7//! Pre-validated write helpers — `Checked`, `CheckedError`, `FixedBitCount`,
8//! `FixedSignedBitCount`, and the `Checkable` / `CheckablePrimitive` traits.
9//!
10//! Split out of `lib.rs` during the 0.1.4 refactor.
11
12use std::io;
13
14use crate::{private, BitCount, BitRead, BitWrite, SignedBitCount, SignedInteger, UnsignedInteger};
15
16/// An error when converting a value to a [`Checked`] struct
17#[derive(Copy, Clone, Debug)]
18pub enum CheckedError {
19 /// Excessive bits for type
20 ExcessiveBits,
21 /// Excessive value for bits
22 ExcessiveValue,
23}
24
25impl core::error::Error for CheckedError {}
26
27impl core::fmt::Display for CheckedError {
28 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
29 match self {
30 Self::ExcessiveBits => core::fmt::Display::fmt("excessive bits for type written", f),
31 Self::ExcessiveValue => core::fmt::Display::fmt("excessive value for bits written", f),
32 }
33 }
34}
35
36impl From<CheckedError> for io::Error {
37 #[inline]
38 fn from(error: CheckedError) -> Self {
39 match error {
40 CheckedError::ExcessiveBits => io::Error::new(
41 io::ErrorKind::InvalidInput,
42 "excessive bits for type written",
43 ),
44 CheckedError::ExcessiveValue => io::Error::new(
45 io::ErrorKind::InvalidInput,
46 "excessive value for bits written",
47 ),
48 }
49 }
50}
51
52/// A type for eliminating redundant validation when writing
53///
54/// Normally, when writing a value, not only must the number of bits
55/// must be checked against the type being written
56/// (e.g. writing 9 bits from a `u8` is always an error),
57/// but the value must also be checked against the number of bits
58/// (e.g. writing a value of 2 in 1 bit is always an error).
59///
60/// But when the value's range can be checked in advance,
61/// the write-time check can be skipped through the use
62/// of the [`BitWrite::write_checked`] method.
63#[derive(Copy, Clone, Debug)]
64pub struct Checked<C, T> {
65 pub(crate) count: C,
66 pub(crate) value: T,
67}
68
69impl<C, T> Checked<C, T> {
70 /// Returns our bit count and value
71 #[inline]
72 pub fn into_count_value(self) -> (C, T) {
73 (self.count, self.value)
74 }
75
76 /// Returns our value
77 #[inline]
78 pub fn into_value(self) -> T {
79 self.value
80 }
81}
82
83impl<C, T> AsRef<T> for Checked<C, T> {
84 fn as_ref(&self) -> &T {
85 &self.value
86 }
87}
88
89/// An unsigned type with a verified value
90pub type CheckedUnsigned<const MAX: u32, T> = Checked<BitCount<MAX>, T>;
91
92impl<const MAX: u32, U: UnsignedInteger> Checkable for CheckedUnsigned<MAX, U> {
93 #[inline]
94 fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
95 // a naive default implementation
96 writer.write_unsigned_counted(self.count, self.value)
97 }
98
99 #[inline]
100 fn written_bits(&self) -> u32 {
101 self.count.bits
102 }
103}
104
105impl<const MAX: u32, U: UnsignedInteger> CheckablePrimitive for CheckedUnsigned<MAX, U> {
106 type CountType = BitCount<MAX>;
107
108 #[inline]
109 fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self> {
110 reader
111 .read_unsigned_counted(count)
112 .map(|value| Self { count, value })
113 }
114}
115
116impl<const MAX: u32, U: UnsignedInteger> private::Checkable for CheckedUnsigned<MAX, U> {
117 fn write_endian<E, W>(
118 self,
119 writer: &mut W,
120 queue_value: &mut u8,
121 queue_bits: &mut u32,
122 ) -> io::Result<()>
123 where
124 E: private::Endianness,
125 W: io::Write,
126 {
127 E::write_bits_checked(writer, queue_value, queue_bits, self)
128 }
129}
130
131impl<const MAX: u32, U: UnsignedInteger> CheckedUnsigned<MAX, U> {
132 /// Returns our value if it fits in the given number of bits
133 ///
134 /// # Example
135 ///
136 /// ```
137 /// use oximedia_bitstream::{BitCount, CheckedUnsigned, CheckedError};
138 ///
139 /// // a value of 7 fits into a 3 bit count
140 /// assert!(CheckedUnsigned::<8, u8>::new(3, 0b111).is_ok());
141 ///
142 /// // a value of 8 does not fit into a 3 bit count
143 /// assert!(matches!(
144 /// CheckedUnsigned::<8, u8>::new(3, 0b1000),
145 /// Err(CheckedError::ExcessiveValue),
146 /// ));
147 ///
148 /// // a bit count of 9 is too large for u8
149 /// assert!(matches!(
150 /// CheckedUnsigned::<9, _>::new(9, 1u8),
151 /// Err(CheckedError::ExcessiveBits),
152 /// ));
153 /// ```
154 #[inline]
155 pub fn new(count: impl TryInto<BitCount<MAX>>, value: U) -> Result<Self, CheckedError> {
156 let count @ BitCount { bits } =
157 count.try_into().map_err(|_| CheckedError::ExcessiveBits)?;
158
159 if MAX <= U::BITS_SIZE || bits <= U::BITS_SIZE {
160 if bits == 0 {
161 Ok(Self {
162 count,
163 value: U::ZERO,
164 })
165 } else if value <= U::ALL >> (U::BITS_SIZE - bits) {
166 Ok(Self { count, value })
167 } else {
168 Err(CheckedError::ExcessiveValue)
169 }
170 } else {
171 Err(CheckedError::ExcessiveBits)
172 }
173 }
174
175 /// Returns our value if it fits in the given number of const bits
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// use oximedia_bitstream::{CheckedUnsigned, CheckedError};
181 ///
182 /// // a value of 7 fits into a 3 bit count
183 /// assert!(CheckedUnsigned::<8, u8>::new_fixed::<3>(0b111).is_ok());
184 ///
185 /// // a value of 8 does not fit into a 3 bit count
186 /// assert!(matches!(
187 /// CheckedUnsigned::<8, u8>::new_fixed::<3>(0b1000),
188 /// Err(CheckedError::ExcessiveValue),
189 /// ));
190 /// ```
191 ///
192 /// ```compile_fail
193 /// use oximedia_bitstream::{BitCount, CheckedUnsigned};
194 ///
195 /// // a bit count of 9 is too large for u8
196 ///
197 /// // because this is checked at compile-time,
198 /// // it does not compile at all
199 /// let c = CheckedUnsigned::<16, u8>::new_fixed::<9>(1);
200 /// ```
201 pub fn new_fixed<const BITS: u32>(value: U) -> Result<Self, CheckedError> {
202 const {
203 assert!(BITS <= U::BITS_SIZE, "excessive bits for type written");
204 }
205
206 if BITS == 0 {
207 Ok(Self {
208 count: BitCount::new::<0>(),
209 value: U::ZERO,
210 })
211 } else if BITS == U::BITS_SIZE || value <= (U::ALL >> (U::BITS_SIZE - BITS)) {
212 Ok(Self {
213 // whether BITS is larger than MAX is checked here
214 count: BitCount::new::<BITS>(),
215 value,
216 })
217 } else {
218 Err(CheckedError::ExcessiveValue)
219 }
220 }
221}
222
223/// A fixed number of bits to be consumed or written
224///
225/// Analagous to [`BitCount`], this is a zero-sized type
226/// whose value is fixed at compile-time and cannot be changed.
227///
228/// # Example
229///
230/// ```
231/// use oximedia_bitstream::{
232/// BigEndian, BitRead, BitReader, BitWrite, BitWriter, CheckedUnsignedFixed, FixedBitCount,
233/// };
234///
235/// type FourBits = CheckedUnsignedFixed<4, u8>;
236///
237/// let input: &[u8] = &[0b0001_1111, 0b0110_1001];
238/// let mut r = BitReader::endian(input, BigEndian);
239///
240/// // read 4, 4-bit values
241/// let v1 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
242/// let v2 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
243/// let v3 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
244/// let v4 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
245///
246/// assert_eq!(v1.into_value(), 0b0001);
247/// assert_eq!(v2.into_value(), 0b1111);
248/// assert_eq!(v3.into_value(), 0b0110);
249/// assert_eq!(v4.into_value(), 0b1001);
250///
251/// // write those same values back to disk
252/// let mut w = BitWriter::endian(vec![], BigEndian);
253/// w.write_checked(v1).unwrap();
254/// w.write_checked(v2).unwrap();
255/// w.write_checked(v3).unwrap();
256/// w.write_checked(v4).unwrap();
257///
258/// // ensure they're the same
259/// assert_eq!(w.into_writer().as_slice(), input);
260/// ```
261#[derive(Copy, Clone, Debug)]
262pub struct FixedBitCount<const BITS: u32>;
263
264impl<const BITS: u32> From<FixedBitCount<BITS>> for BitCount<BITS> {
265 fn from(_count: FixedBitCount<BITS>) -> Self {
266 BitCount::new::<BITS>()
267 }
268}
269
270impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<BitCount<MAX>>
271 for FixedBitCount<BITS>
272{
273 type Error = BitCount<MAX>;
274
275 fn try_from(count: BitCount<MAX>) -> Result<Self, Self::Error> {
276 (count.bits == BITS).then_some(FixedBitCount).ok_or(count)
277 }
278}
279
280/// An unsigned type with a verified value for a fixed number of bits
281pub type CheckedUnsignedFixed<const BITS: u32, T> = Checked<FixedBitCount<BITS>, T>;
282
283impl<const BITS: u32, U: UnsignedInteger> CheckedUnsignedFixed<BITS, U> {
284 /// Returns our checked value if it fits in the given number of const bits
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// use oximedia_bitstream::{CheckedUnsignedFixed, CheckedError};
290 ///
291 /// // a value of 7 fits into a maximum of 3 bits
292 /// assert!(CheckedUnsignedFixed::<3, u8>::new_fixed(0b111).is_ok());
293 ///
294 /// // a value of 8 does not fit into a maximum of 3 bits
295 /// assert!(matches!(
296 /// CheckedUnsignedFixed::<3, u8>::new_fixed(0b1000),
297 /// Err(CheckedError::ExcessiveValue),
298 /// ));
299 /// ```
300 ///
301 /// ```compile_fail
302 /// use oximedia_bitstream::CheckedUnsignedFixed;
303 ///
304 /// // a bit count of 9 is too large for u8
305 ///
306 /// // because this is checked at compile-time,
307 /// // it does not compile at all
308 /// let c = CheckedUnsignedFixed::<9, u8>::new_fixed(1);
309 /// ```
310 pub fn new_fixed(value: U) -> Result<Self, CheckedError> {
311 const {
312 assert!(BITS <= U::BITS_SIZE, "excessive bits for type written");
313 }
314
315 if BITS == 0 {
316 Ok(Self {
317 count: FixedBitCount,
318 value: U::ZERO,
319 })
320 } else if BITS == U::BITS_SIZE || value <= (U::ALL >> (U::BITS_SIZE - BITS)) {
321 Ok(Self {
322 count: FixedBitCount,
323 value,
324 })
325 } else {
326 Err(CheckedError::ExcessiveValue)
327 }
328 }
329}
330
331impl<const BITS: u32, U: UnsignedInteger> Checkable for CheckedUnsignedFixed<BITS, U> {
332 #[inline]
333 fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
334 // a naive default implementation
335 writer.write_unsigned::<BITS, _>(self.value)
336 }
337
338 #[inline]
339 fn written_bits(&self) -> u32 {
340 BITS
341 }
342}
343
344impl<const BITS: u32, U: UnsignedInteger> private::Checkable for CheckedUnsignedFixed<BITS, U> {
345 fn write_endian<E, W>(
346 self,
347 writer: &mut W,
348 queue_value: &mut u8,
349 queue_bits: &mut u32,
350 ) -> io::Result<()>
351 where
352 E: private::Endianness,
353 W: io::Write,
354 {
355 E::write_bits_checked(
356 writer,
357 queue_value,
358 queue_bits,
359 Checked {
360 value: self.value,
361 count: self.count.into(),
362 },
363 )
364 }
365}
366
367impl<const BITS: u32, U: UnsignedInteger> CheckablePrimitive for CheckedUnsignedFixed<BITS, U> {
368 type CountType = FixedBitCount<BITS>;
369
370 fn read<R: BitRead + ?Sized>(reader: &mut R, count: FixedBitCount<BITS>) -> io::Result<Self> {
371 Ok(Self {
372 value: reader.read_unsigned::<BITS, _>()?,
373 count,
374 })
375 }
376}
377
378/// A signed type with a verified value
379pub type CheckedSigned<const MAX: u32, T> = Checked<SignedBitCount<MAX>, T>;
380
381impl<const MAX: u32, S: SignedInteger> Checkable for CheckedSigned<MAX, S> {
382 #[inline]
383 fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
384 // a naive default implementation
385 writer.write_signed_counted(self.count, self.value)
386 }
387
388 #[inline]
389 fn written_bits(&self) -> u32 {
390 self.count.bits.into()
391 }
392}
393
394impl<const MAX: u32, S: SignedInteger> CheckablePrimitive for CheckedSigned<MAX, S> {
395 type CountType = SignedBitCount<MAX>;
396
397 #[inline]
398 fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self> {
399 reader
400 .read_signed_counted(count)
401 .map(|value| Self { count, value })
402 }
403}
404
405impl<const MAX: u32, S: SignedInteger> private::Checkable for CheckedSigned<MAX, S> {
406 #[inline]
407 fn write_endian<E, W>(
408 self,
409 writer: &mut W,
410 queue_value: &mut u8,
411 queue_bits: &mut u32,
412 ) -> io::Result<()>
413 where
414 E: private::Endianness,
415 W: io::Write,
416 {
417 E::write_signed_bits_checked(writer, queue_value, queue_bits, self)
418 }
419}
420
421impl<const MAX: u32, S: SignedInteger> CheckedSigned<MAX, S> {
422 /// Returns our value if it fits in the given number of bits
423 ///
424 /// # Example
425 ///
426 /// ```
427 /// use oximedia_bitstream::{SignedBitCount, CheckedSigned, CheckedError};
428 ///
429 /// // a value of 3 fits into a 3 bit count
430 /// assert!(CheckedSigned::<8, _>::new(3, 3i8).is_ok());
431 ///
432 /// // a value of 4 does not fit into a 3 bit count
433 /// assert!(matches!(
434 /// CheckedSigned::<8, _>::new(3, 4i8),
435 /// Err(CheckedError::ExcessiveValue),
436 /// ));
437 ///
438 /// // a bit count of 9 is too large for i8
439 /// assert!(matches!(
440 /// CheckedSigned::<9, _>::new(9, 1i8),
441 /// Err(CheckedError::ExcessiveBits),
442 /// ));
443 /// ```
444 #[inline]
445 pub fn new(count: impl TryInto<SignedBitCount<MAX>>, value: S) -> Result<Self, CheckedError> {
446 let count @ SignedBitCount {
447 bits: BitCount { bits },
448 unsigned: BitCount {
449 bits: unsigned_bits,
450 },
451 } = count.try_into().map_err(|_| CheckedError::ExcessiveBits)?;
452
453 if MAX <= S::BITS_SIZE || bits <= S::BITS_SIZE {
454 if bits == S::BITS_SIZE
455 || (((S::ZERO - S::ONE) << unsigned_bits) <= value
456 && value < (S::ONE << unsigned_bits))
457 {
458 Ok(Self { count, value })
459 } else {
460 Err(CheckedError::ExcessiveValue)
461 }
462 } else {
463 Err(CheckedError::ExcessiveBits)
464 }
465 }
466
467 /// Returns our value if it fits in the given number of const bits
468 ///
469 /// # Examples
470 ///
471 /// ```
472 /// use oximedia_bitstream::{CheckedSigned, CheckedError};
473 ///
474 /// // a value of 3 fits into a 3 bit count
475 /// assert!(CheckedSigned::<8, i8>::new_fixed::<3>(3).is_ok());
476 ///
477 /// // a value of 4 does not fit into a 3 bit count
478 /// assert!(matches!(
479 /// CheckedSigned::<8, i8>::new_fixed::<3>(4),
480 /// Err(CheckedError::ExcessiveValue),
481 /// ));
482 /// ```
483 ///
484 /// ```compile_fail
485 /// use oximedia_bitstream::{BitCount, CheckedSigned};
486 ///
487 /// // a bit count of 9 is too large for i8
488 ///
489 /// // because this is checked at compile-time,
490 /// // it does not compile at all
491 /// let c = CheckedSigned::<16, i8>::new_fixed::<9>(1);
492 /// ```
493 pub fn new_fixed<const BITS: u32>(value: S) -> Result<Self, CheckedError> {
494 const {
495 assert!(BITS <= S::BITS_SIZE, "excessive bits for type written");
496 }
497
498 if BITS == S::BITS_SIZE
499 || (((S::ZERO - S::ONE) << (BITS - 1)) <= value && value < (S::ONE << (BITS - 1)))
500 {
501 Ok(Self {
502 count: SignedBitCount::new::<BITS>(),
503 value,
504 })
505 } else {
506 Err(CheckedError::ExcessiveValue)
507 }
508 }
509}
510
511/// A fixed number of bits to be consumed or written
512///
513/// Analagous to [`SignedBitCount`], this is a zero-sized type
514/// whose value is fixed at compile-time and cannot be changed.
515///
516/// # Example
517///
518/// ```
519/// use oximedia_bitstream::{
520/// BigEndian, BitRead, BitReader, BitWrite, BitWriter,
521/// CheckedSignedFixed, FixedSignedBitCount,
522/// };
523///
524/// type FourBits = CheckedSignedFixed<4, i8>;
525///
526/// let input: &[u8] = &[0b0001_1111, 0b0110_1001];
527/// let mut r = BitReader::endian(input, BigEndian);
528///
529/// // read 4, 4-bit values
530/// let v1 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
531/// let v2 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
532/// let v3 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
533/// let v4 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
534///
535/// assert_eq!(v1.into_value(), 1);
536/// assert_eq!(v2.into_value(), -1);
537/// assert_eq!(v3.into_value(), 6);
538/// assert_eq!(v4.into_value(), -7);
539///
540/// // write those same values back to disk
541/// let mut w = BitWriter::endian(vec![], BigEndian);
542/// w.write_checked(v1).unwrap();
543/// w.write_checked(v2).unwrap();
544/// w.write_checked(v3).unwrap();
545/// w.write_checked(v4).unwrap();
546///
547/// // ensure they're the same
548/// assert_eq!(w.into_writer().as_slice(), input);
549/// ```
550#[derive(Copy, Clone, Debug)]
551pub struct FixedSignedBitCount<const BITS: u32>;
552
553impl<const BITS: u32> From<FixedSignedBitCount<BITS>> for SignedBitCount<BITS> {
554 fn from(_count: FixedSignedBitCount<BITS>) -> Self {
555 SignedBitCount::new::<BITS>()
556 }
557}
558
559impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<SignedBitCount<MAX>>
560 for FixedSignedBitCount<BITS>
561{
562 type Error = SignedBitCount<MAX>;
563
564 fn try_from(count: SignedBitCount<MAX>) -> Result<Self, Self::Error> {
565 (count.bits.bits == BITS)
566 .then_some(FixedSignedBitCount)
567 .ok_or(count)
568 }
569}
570
571/// A signed type with a verified value for a fixed number of bits
572pub type CheckedSignedFixed<const BITS: u32, T> = Checked<FixedSignedBitCount<BITS>, T>;
573
574impl<const BITS: u32, S: SignedInteger> CheckedSignedFixed<BITS, S> {
575 /// Returns our checked value if it fits in the given number of const bits
576 ///
577 /// # Examples
578 ///
579 /// ```
580 /// use oximedia_bitstream::{SignedBitCount, CheckedSignedFixed, CheckedError};
581 ///
582 /// // a value of 3 fits into a 3 bit count
583 /// assert!(CheckedSignedFixed::<3, _>::new_fixed(3i8).is_ok());
584 ///
585 /// // a value of 4 does not fit into a 3 bit count
586 /// assert!(matches!(
587 /// CheckedSignedFixed::<3, _>::new_fixed(4i8),
588 /// Err(CheckedError::ExcessiveValue),
589 /// ));
590 /// ```
591 ///
592 /// ```compile_fail
593 /// use oximedia_bitstream::CheckedSignedFixed;
594 ///
595 /// // a bit count of 9 is too large for i8
596 ///
597 /// // because this is checked at compile-time,
598 /// // it does not compile at all
599 /// let c = CheckedSignedFixed::<9, _>::new_fixed(1i8);
600 /// ```
601 pub fn new_fixed(value: S) -> Result<Self, CheckedError> {
602 const {
603 assert!(BITS <= S::BITS_SIZE, "excessive bits for type written");
604 }
605
606 if BITS == S::BITS_SIZE
607 || (((S::ZERO - S::ONE) << (BITS - 1)) <= value && value < (S::ONE << (BITS - 1)))
608 {
609 Ok(Self {
610 count: FixedSignedBitCount,
611 value,
612 })
613 } else {
614 Err(CheckedError::ExcessiveValue)
615 }
616 }
617}
618impl<const BITS: u32, S: SignedInteger> Checkable for CheckedSignedFixed<BITS, S> {
619 #[inline]
620 fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
621 // a naive default implementation
622 writer.write_signed::<BITS, _>(self.value)
623 }
624
625 #[inline]
626 fn written_bits(&self) -> u32 {
627 BITS
628 }
629}
630
631impl<const BITS: u32, S: SignedInteger> private::Checkable for CheckedSignedFixed<BITS, S> {
632 #[inline]
633 fn write_endian<E, W>(
634 self,
635 writer: &mut W,
636 queue_value: &mut u8,
637 queue_bits: &mut u32,
638 ) -> io::Result<()>
639 where
640 E: private::Endianness,
641 W: io::Write,
642 {
643 E::write_signed_bits_checked(
644 writer,
645 queue_value,
646 queue_bits,
647 CheckedSigned {
648 value: self.value,
649 count: self.count.into(),
650 },
651 )
652 }
653}
654
655impl<const BITS: u32, S: SignedInteger> CheckablePrimitive for CheckedSignedFixed<BITS, S> {
656 type CountType = FixedSignedBitCount<BITS>;
657
658 fn read<R: BitRead + ?Sized>(
659 reader: &mut R,
660 count: FixedSignedBitCount<BITS>,
661 ) -> io::Result<Self> {
662 Ok(Self {
663 value: reader.read_signed::<BITS, _>()?,
664 count,
665 })
666 }
667}
668
669/// A trait for writable types whose values can be validated
670///
671/// Ordinarily, when writing a value to a stream with a given
672/// number of bits, the value must be validated to ensure
673/// it will fit within that number of bits.
674///
675/// # Example 1
676///
677/// ```
678/// use oximedia_bitstream::{BitWrite, BitWriter, BigEndian};
679///
680/// let mut w = BitWriter::endian(vec![], BigEndian);
681///
682/// // writing a value of 2 in 1 bit is always an error
683/// // which is checked here at write-time
684/// assert!(w.write::<1, u8>(2).is_err());
685/// ```
686///
687/// But if the value can be checked beforehand,
688/// it doesn't need to be checked at write-time.
689///
690/// # Example 2
691///
692/// ```
693/// use oximedia_bitstream::{BitWrite, BitWriter, BigEndian, CheckedUnsigned};
694///
695/// let mut w = BitWriter::endian(vec![], BigEndian);
696///
697/// // writing a value of 1 in 1 bit is ok
698/// // and we're checking that validity at this stage
699/// let value = CheckedUnsigned::<1, u8>::new_fixed::<1>(1).unwrap();
700///
701/// // because we've pre-validated the value beforehand,
702/// // it doesn't need to be checked again here
703/// // (though the write itself may still fail)
704/// assert!(w.write_checked(value).is_ok());
705/// ```
706///
707pub trait Checkable: private::Checkable + Sized {
708 /// Write our value to the given stream
709 fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()>;
710
711 /// The number of written bits
712 fn written_bits(&self) -> u32;
713}
714
715/// A trait for readable types whose bit counts can be saved
716///
717/// Because the intent of reading checkable values is
718/// to avoid validating their values when being written,
719/// implementing the [`Checkable`] trait is required.
720pub trait CheckablePrimitive: Checkable {
721 /// Our bit count type for reading
722 type CountType;
723
724 /// Reads our value from the given stream
725 fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self>;
726}