use alloc::format;
use alloc::vec::Vec;
use core::fmt;
use crate::octets::write_hex;
use crate::{Error, ErrorKind, Octets};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct Bits {
value: u64,
length: u8,
}
impl Bits {
pub const EMPTY: Self = Self {
value: 0,
length: 0,
};
pub fn new(value: u64, length: usize) -> Result<Self, Error> {
if length > 64 {
return Err(Error::new(
ErrorKind::Argument,
format!("a bit string of {length} bits is no number of 64 bits: it is a BitString"),
));
}
if length < 64 && value >> length != 0 {
return Err(Error::new(
ErrorKind::Argument,
format!("{value} does not fit in {length} bits"),
));
}
Ok(Self {
value,
length: length as u8,
})
}
pub const fn low(value: u64, length: usize) -> Self {
match length {
0 => Self::EMPTY,
1..=63 => Self {
value: value & ((1 << length) - 1),
length: length as u8,
},
_ => Self { value, length: 64 },
}
}
pub const fn value(&self) -> u64 {
self.value
}
pub const fn len(&self) -> usize {
self.length as usize
}
pub const fn is_empty(&self) -> bool {
self.length == 0
}
pub const fn get(&self, position: usize) -> Option<bool> {
if position >= self.length as usize {
return None;
}
Some(self.value >> (self.length as usize - 1 - position) & 1 == 1)
}
pub(crate) const fn leading(&self) -> [u8; 8] {
match self.length {
0 => [0; 8],
length => (self.value << (64 - length)).to_be_bytes(),
}
}
pub fn to_vec(&self) -> Vec<u8> {
self.leading()[..self.len().div_ceil(8)].to_vec()
}
pub fn from_octets(octets: &[u8], length: usize) -> Result<Self, Error> {
check_octets(octets, length)?;
if length > 64 {
return Self::new(0, length);
}
let mut leading = [0; 8];
leading[..octets.len()].copy_from_slice(octets);
let value = match length {
0 => 0,
length => u64::from_be_bytes(leading) >> (64 - length),
};
Ok(Self {
value,
length: length as u8,
})
}
}
fn check_octets(octets: &[u8], length: usize) -> Result<(), Error> {
if octets.len() != length.div_ceil(8) {
return Err(Error::new(
ErrorKind::Argument,
format!(
"a bit string of {length} bits has {} octets, not {}",
length.div_ceil(8),
octets.len()
),
));
}
let unused = octets.len() * 8 - length;
if octets
.last()
.is_some_and(|last| last.trailing_zeros() < unused as u32)
{
return Err(Error::new(
ErrorKind::Argument,
format!(
"the last {unused} bits of the octets are after the {length} bits of the bit \
string, and are not zero"
),
));
}
Ok(())
}
impl From<u8> for Bits {
fn from(value: u8) -> Self {
Self {
value: value.into(),
length: 8,
}
}
}
impl From<u16> for Bits {
fn from(value: u16) -> Self {
Self {
value: value.into(),
length: 16,
}
}
}
impl From<u32> for Bits {
fn from(value: u32) -> Self {
Self {
value: value.into(),
length: 32,
}
}
}
impl From<u64> for Bits {
fn from(value: u64) -> Self {
Self { value, length: 64 }
}
}
impl fmt::Debug for Bits {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{:#x}/{}", self.value, self.length)
}
}
#[derive(Clone)]
pub struct BitString<'a>(Held<'a>);
#[derive(Clone)]
enum Held<'a> {
Short {
octets: [u8; 8],
length: u8,
},
Long {
octets: Octets<'a>,
length: usize,
},
}
impl<'a> BitString<'a> {
pub const EMPTY: Self = Self(Held::Short {
octets: [0; 8],
length: 0,
});
pub fn new(octets: impl Into<Octets<'a>>, length: usize) -> Result<Self, Error> {
let octets = octets.into();
check_octets(&octets, length)?;
Ok(Self::of(octets, length))
}
pub fn from_octets(octets: impl Into<Octets<'a>>) -> Self {
let octets = octets.into();
let length = octets.len() * 8;
Self::of(octets, length)
}
pub(crate) fn of(octets: Octets<'a>, length: usize) -> Self {
debug_assert!(check_octets(&octets, length).is_ok());
if length > 64 {
return Self(Held::Long { octets, length });
}
let mut short = [0; 8];
short[..octets.len()].copy_from_slice(&octets);
Self(Held::Short {
octets: short,
length: length as u8,
})
}
pub fn len(&self) -> usize {
match &self.0 {
Held::Short { length, .. } => usize::from(*length),
Held::Long { length, .. } => *length,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_octets(&self) -> &[u8] {
match &self.0 {
Held::Short { octets, length } => &octets[..usize::from(*length).div_ceil(8)],
Held::Long { octets, .. } => octets,
}
}
pub fn get(&self, position: usize) -> Option<bool> {
if position >= self.len() {
return None;
}
Some(self.as_octets()[position / 8] & (0x80 >> (position % 8)) != 0)
}
pub fn to_bits(&self) -> Option<Bits> {
match &self.0 {
Held::Short { octets, length } => Some(Bits::low(
u64::from_be_bytes(*octets)
.checked_shr(64 - u32::from(*length))
.unwrap_or(0),
usize::from(*length),
)),
Held::Long { .. } => None,
}
}
#[doc(alias("to_owned", "owned", "keep"))]
pub fn into_owned(self) -> BitString<'static> {
BitString(match self.0 {
Held::Short { octets, length } => Held::Short { octets, length },
Held::Long { octets, length } => Held::Long {
octets: octets.into_owned(),
length,
},
})
}
}
impl Default for BitString<'_> {
fn default() -> Self {
Self::EMPTY
}
}
impl PartialEq for BitString<'_> {
fn eq(&self, other: &Self) -> bool {
self.len() == other.len() && self.as_octets() == other.as_octets()
}
}
impl Eq for BitString<'_> {}
impl core::hash::Hash for BitString<'_> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.len().hash(state);
self.as_octets().hash(state);
}
}
impl PartialOrd for BitString<'_> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BitString<'_> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let octets = self.as_octets().cmp(other.as_octets());
octets.then(self.len().cmp(&other.len()))
}
}
impl<'a> From<&'a [u8]> for BitString<'a> {
fn from(octets: &'a [u8]) -> Self {
Self::from_octets(octets)
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for BitString<'a> {
fn from(octets: &'a [u8; N]) -> Self {
Self::from_octets(octets)
}
}
impl From<Vec<u8>> for BitString<'_> {
fn from(octets: Vec<u8>) -> Self {
Self::from_octets(octets)
}
}
impl<const N: usize> From<[u8; N]> for BitString<'_> {
fn from(octets: [u8; N]) -> Self {
Self::from_octets(octets)
}
}
impl From<Bits> for BitString<'_> {
fn from(bits: Bits) -> Self {
Self(Held::Short {
octets: bits.leading(),
length: bits.len() as u8,
})
}
}
impl fmt::Debug for BitString<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write_hex(self.as_octets(), formatter)?;
write!(formatter, "/{}", self.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
#[test]
fn a_short_bit_string_is_a_number_with_a_length() {
let set = Bits::new(0x155, 10).unwrap();
assert_eq!((set.value(), set.len()), (0x155, 10));
assert_eq!(set.to_vec(), [0x55, 0x40]);
assert_eq!(Bits::from_octets(&[0x55, 0x40], 10).unwrap(), set);
assert_eq!(set.get(0), Some(false));
assert_eq!(set.get(1), Some(true));
assert_eq!(set.get(9), Some(true));
assert_eq!(set.get(10), None);
assert_eq!(format!("{set:?}"), "0x155/10");
assert_eq!(Bits::new(0, 0).unwrap(), Bits::EMPTY);
assert!(Bits::EMPTY.is_empty());
assert!(Bits::EMPTY.to_vec().is_empty());
assert_eq!(Bits::new(u64::MAX, 64).unwrap(), Bits::from(u64::MAX));
assert_eq!(Bits::from(u64::MAX).to_vec(), [0xff; 8]);
assert_eq!(Bits::new(1, 65).unwrap_err().kind(), ErrorKind::Argument);
assert_eq!(Bits::new(1024, 10).unwrap_err().kind(), ErrorKind::Argument);
assert_eq!(
Bits::new(1024, 10).unwrap_err().to_string(),
"1024 does not fit in 10 bits"
);
assert_eq!(Bits::low(0xffff, 10), Bits::new(0x3ff, 10).unwrap());
assert_eq!(Bits::low(0xffff, 0), Bits::EMPTY);
assert_eq!(Bits::low(u64::MAX, 64), Bits::from(u64::MAX));
assert_eq!(Bits::low(u64::MAX, 99).len(), 64);
assert_eq!(Bits::from(0x80u8), Bits::new(0x80, 8).unwrap());
assert_eq!(Bits::from(0xe000u16).to_vec(), [0xe0, 0x00]);
assert_eq!(Bits::from(7u32).len(), 32);
assert!(Bits::from_octets(&[0x55], 10).is_err());
assert!(Bits::from_octets(&[0x55, 0x41], 10).is_err());
assert!(Bits::from_octets(&[0; 9], 72).is_err());
assert_eq!(Bits::from_octets(&[], 0).unwrap(), Bits::EMPTY);
}
#[test]
fn a_long_bit_string_is_its_octets_with_a_length() {
let key = BitString::from([0x5a; 32]);
assert_eq!(key.len(), 256);
assert_eq!(key.to_bits(), None);
let bits = BitString::new(vec![0xab, 0xcd, 0xe0], 20).unwrap();
assert_eq!(bits.as_octets(), [0xab, 0xcd, 0xe0]);
assert_eq!(bits.get(0), Some(true));
assert_eq!(bits.get(1), Some(false));
assert_eq!(bits.get(19), Some(false));
assert_eq!(bits.get(20), None);
assert_eq!(format!("{bits:?}"), "abcde0/20");
assert_eq!(bits.to_bits(), Some(Bits::new(0xabcde, 20).unwrap()));
assert_eq!(BitString::from(Bits::new(0xabcde, 20).unwrap()), bits);
assert!(BitString::new(vec![0xab, 0xcd, 0xef], 20).is_err());
assert!(BitString::new(vec![0xab, 0xcd], 20).is_err());
assert!(BitString::default().is_empty());
assert_eq!(BitString::default(), BitString::EMPTY);
let short = BitString::new(vec![0xff, 0x80], 9).unwrap();
assert_eq!(short, BitString::from(Bits::new(0x1ff, 9).unwrap()));
assert_eq!(short.to_bits(), Some(Bits::new(0x1ff, 9).unwrap()));
assert_eq!(BitString::EMPTY.to_bits(), Some(Bits::EMPTY));
assert_eq!(
BitString::from([0xffu8; 8]).to_bits(),
Some(Bits::from(u64::MAX))
);
assert!(BitString::new(vec![0xa0], 4).unwrap() < BitString::new(vec![0xa0], 8).unwrap());
let received = [0xffu8, 0x80];
let borrowed = BitString::new(&received[..], 9).unwrap();
let kept: BitString<'static> = borrowed.clone().into_owned();
assert_eq!(kept, borrowed);
}
}