#![no_std]
#![warn(missing_docs)]

//! See <code>[BitBuf]::[from()][BitBuf::from]</code>, [`Storage`]/[`StorageMut`], and <code>[BitBuf]::{read,write}_*()</code>
//!
//! | Term | Definition |
//! | - | - |
//! | UB | [Undefined Behaviour](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) |
//! | BE | [Big Endian](https://en.wikipedia.org/wiki/Endianness) |
//! | LE | [Little Endian](https://en.wikipedia.org/wiki/Endianness) |
//!
//! Any usage of `pos`/`offset` implies bits rather than bytes

#[cfg(test)]
mod tests;

mod buf;
mod error;

mod read;
mod write;

pub use buf::BitBuf;
pub use error::Error;

/// Returned from checked operations
pub type Result<T> = core::result::Result<T, Error>;

/// This trait must be implemented for types from which you wish to use `read_*` operations with
pub trait Storage {
  /// Get the bytes that represent this type
  fn as_bytes(&self) -> &[u8];
}

/// This trait must be implemented for types from which you wish to use `write_*` operations with
pub trait StorageMut: Storage {
  /// Get the bytes that represent this type
  fn as_bytes_mut(&mut self) -> &mut [u8];
}

impl<const N: usize> Storage for [u8; N] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    self
  }
}

impl<const N: usize> StorageMut for [u8; N] {
  #[inline(always)]
  fn as_bytes_mut(&mut self) -> &mut [u8] {
    self
  }
}

impl<const N: usize> Storage for &mut [u8; N] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    *self
  }
}

impl<const N: usize> StorageMut for &mut [u8; N] {
  #[inline(always)]
  fn as_bytes_mut(&mut self) -> &mut [u8] {
    *self
  }
}

impl Storage for &[u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    self
  }
}

impl Storage for &mut [u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    self
  }
}

impl StorageMut for &mut [u8] {
  #[inline(always)]
  fn as_bytes_mut(&mut self) -> &mut [u8] {
    self
  }
}
