use std::{cmp::Ordering, io::Write};
use crate::{
common::traits::private::Sealed,
v0::{
config::Config,
raw::error,
traits::{ReadFrom, WriteTo},
},
};
use super::error::VariableLengthEnumError;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct VariableLengthEnum {
value: VariableLengthEnumStorage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum VariableLengthEnumStorage {
U64(u64),
Overflow(Box<Vec<u8>>),
}
impl PartialOrd for VariableLengthEnumStorage {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VariableLengthEnumStorage {
fn cmp(&self, other: &Self) -> Ordering {
match self {
VariableLengthEnumStorage::U64(self_u64) => match other {
VariableLengthEnumStorage::U64(other_u64) => self_u64.cmp(other_u64), VariableLengthEnumStorage::Overflow(_) => Ordering::Less, },
VariableLengthEnumStorage::Overflow(self_overflow) => match other {
VariableLengthEnumStorage::U64(_) => Ordering::Greater, VariableLengthEnumStorage::Overflow(other_overflow) => {
let len_cmp = self_overflow.len().cmp(&other_overflow.len());
if len_cmp != Ordering::Equal {
return len_cmp;
}
self_overflow.iter().rev().cmp(other_overflow.iter().rev())
}
},
}
}
}
impl From<usize> for VariableLengthEnum {
fn from(value: usize) -> Self {
VariableLengthEnum {
value: VariableLengthEnumStorage::U64(value as u64),
}
}
}
impl Sealed for VariableLengthEnum {}
impl<R> ReadFrom<R> for VariableLengthEnum
where
R: std::io::Read + ?Sized,
{
type ReadError = VariableLengthEnumError;
fn read_from<C: ?Sized + Config>(reader: &mut R, config: &C) -> Result<Self, Self::ReadError> {
let mut byte_vec = Vec::new();
let mut accumulator: Option<u64> = Some(0);
loop {
let mut bytes: [u8; 1] = [0; 1];
reader.read_exact(&mut bytes)?;
let byte = bytes[0];
if byte == 0x80 && accumulator.is_none_or(|acc| acc == 0) {
continue;
}
accumulator = if let Some(inner) = accumulator {
if inner.leading_zeros() < 7 {
if byte_vec.is_empty() {
let inner_as_vre: VariableLengthEnum =
VariableLengthEnum::from(inner as usize);
inner_as_vre.write_to(&mut byte_vec, config)?; }
byte_vec.push(byte);
None
} else {
Some(inner << 7 | (byte & 0x7F) as u64)
}
} else {
byte_vec.push(byte);
None
};
if byte < 0x80 {
break;
}
}
if let Some(accumulator) = accumulator {
Ok(VariableLengthEnum {
value: VariableLengthEnumStorage::U64(accumulator),
})
} else {
Ok(VariableLengthEnum {
value: VariableLengthEnumStorage::Overflow(Box::new(byte_vec)),
})
}
}
}
impl TryInto<usize> for VariableLengthEnum {
type Error = error::VariableLengthEnumError;
fn try_into(self) -> Result<usize, Self::Error> {
match self.value {
VariableLengthEnumStorage::U64(u64_value) => u64_value
.try_into()
.map_err(|_| error::VariableLengthEnumError::TooBig),
VariableLengthEnumStorage::Overflow(_) => Err(error::VariableLengthEnumError::TooBig),
}
}
}
impl std::fmt::Display for VariableLengthEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.value {
VariableLengthEnumStorage::U64(u64_value) => write!(f, "{}", u64_value),
VariableLengthEnumStorage::Overflow(byte_vec) => {
write!(f, "0x")?;
let mut accumulator: u16 = 0;
let mut bit_length: u8 = 0;
for byte in byte_vec.iter() {
accumulator = accumulator << 7 | (byte & 0x7F) as u16;
bit_length += 7;
if bit_length >= 8 {
let byte = ((accumulator >> (bit_length - 8)) & 0xFF) as u8;
bit_length -= 8;
write!(f, "{:02x}", byte)?
}
}
Ok(())
}
}
}
}
impl<W> WriteTo<W> for VariableLengthEnum
where
W: Write + ?Sized,
{
type WriteError = VariableLengthEnumError;
fn write_to<C: ?Sized + Config>(
&self,
writer: &mut W,
_configuration: &C,
) -> Result<(), Self::WriteError> {
match &self.value {
VariableLengthEnumStorage::U64(u64_value) => {
let value = *u64_value;
let digits = if value == 0 { 1 } else { value.ilog2() + 1 }; let byte_count = digits.div_ceil(7);
let mut bytes = Vec::with_capacity(byte_count as usize);
for byte_index in (1..byte_count).rev() {
let byte_value = (value >> (byte_index * 7)) & 0x7F | 0x80;
bytes.push(byte_value as u8);
}
bytes.push((value & 0x7F) as u8);
writer.write_all(&bytes)?;
}
VariableLengthEnumStorage::Overflow(byte_vec) => {
writer.write_all(&byte_vec)?;
}
}
Ok(())
}
}
impl VariableLengthEnum {
pub(crate) fn min_byte_length_of_usize(value: usize) -> usize {
let digits = if value > 0 { value.ilog2() + 1 } else { 1 }; let byte_count = digits.div_ceil(7); byte_count as usize
}
pub(crate) fn min_byte_length(&self) -> usize {
match &self.value {
VariableLengthEnumStorage::U64(u64_value) => {
let value = *u64_value;
let digits = if value > 0 { value.ilog2() + 1 } else { 1 }; let byte_count = digits.div_ceil(7); byte_count as usize
}
VariableLengthEnumStorage::Overflow(byte_vec) => byte_vec.len(),
}
}
}