use std::io::{Error as IoError, ErrorKind};
use crate::prelude::*;
#[derive(Debug, Clone, Copy)]
pub enum LengthResult {
Length(u64),
Encoded(u8),
}
async fn load<R>(reader: &mut R) -> Result<LengthResult>
where
R: AsyncRead + Unpin,
{
let first_byte = reader.read_u8().await?;
load_from_first_byte(reader, first_byte).await
}
pub async fn load_plain<R>(reader: &mut R) -> Result<u64>
where
R: AsyncRead + Unpin,
{
match load(reader).await? {
LengthResult::Length(len) => Ok(len),
LengthResult::Encoded(enc) => Err(IoError::new(
ErrorKind::InvalidData,
format!("Expected plain length, got encoding type: {enc}"),
)
.into()),
}
}
pub async fn load_from_first_byte<R>(reader: &mut R, first_byte: u8) -> Result<LengthResult>
where
R: AsyncRead + Unpin,
{
let encoding_type = (first_byte & 0xC0) >> 6;
match encoding_type {
super::symbols::LEN_6BIT => Ok(LengthResult::Length(u64::from(first_byte & 0x3F))),
super::symbols::LEN_14BIT => {
let next_byte = reader.read_u8().await?;
Ok(LengthResult::Length(u64::from(
(u16::from(first_byte & 0x3F)) << 8 | u16::from(next_byte),
)))
}
super::symbols::LEN_32BIT => {
if first_byte == 0x81 {
let mut bytes = [0u8; 8];
reader.read_exact(&mut bytes).await?;
Ok(LengthResult::Length(u64::from_be_bytes(bytes)))
} else {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes).await?;
Ok(LengthResult::Length(u64::from(u32::from_be_bytes(bytes))))
}
}
super::symbols::LEN_ENCVAL => {
let enc_type = first_byte & 0x3F;
Ok(LengthResult::Encoded(enc_type))
}
_ => Err(IoError::new(
ErrorKind::InvalidData,
format!("Invalid length encoding: {encoding_type}"),
)
.into()),
}
}
pub async fn save<W>(writer: &mut W, len: u64) -> Result<()>
where
W: AsyncWrite + Unpin,
{
if len < 64 {
#[allow(clippy::cast_possible_truncation)]
writer.write_u8(len as u8).await?;
} else if len < 16384 {
#[allow(clippy::cast_possible_truncation)]
let high = ((len >> 8) as u8) | 0x40;
#[allow(clippy::cast_possible_truncation)]
let low = len as u8;
writer.write_all(&[high, low]).await?;
} else {
writer.write_u8(0x80).await?;
#[allow(clippy::cast_possible_truncation)]
writer.write_all(&(len as u32).to_be_bytes()).await?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[tokio::test]
async fn test_load_6bit() {
let data = [0x05]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Length(5)));
}
#[tokio::test]
async fn test_load_6bit_max() {
let data = [0x3F]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Length(63)));
}
#[tokio::test]
async fn test_load_14bit() {
let data = [0x41, 0x00]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Length(256)));
}
#[tokio::test]
async fn test_load_14bit_max() {
let data = [0x7F, 0xFF]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Length(16383)));
}
#[tokio::test]
async fn test_load_32bit() {
let data = [0x80, 0x00, 0x01, 0x00, 0x00]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Length(65536)));
}
#[tokio::test]
async fn test_load_encoded_int8() {
let data = [0xC0]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Encoded(0)));
}
#[tokio::test]
async fn test_load_encoded_int16() {
let data = [0xC1]; let mut reader = Cursor::new(&data);
let result = load(&mut reader).await.unwrap();
assert!(matches!(result, LengthResult::Encoded(1)));
}
#[tokio::test]
async fn test_load_plain() {
let data = [0x05];
let mut reader = Cursor::new(&data);
let len = load_plain(&mut reader).await.unwrap();
assert_eq!(len, 5);
}
#[tokio::test]
async fn test_load_plain_rejects_encoded() {
let data = [0xC0]; let mut reader = Cursor::new(&data);
let result = load_plain(&mut reader).await;
assert!(result.is_err());
}
}