Skip to main content

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}