use std::io::{Error as IoError, ErrorKind};
use super::length::LengthResult;
use crate::prelude::*;
pub async fn load<R>(reader: &mut R) -> Result<Arc<str>>
where
R: AsyncRead + Unpin,
{
let first_byte = reader.read_u8().await?;
let result = super::length::load_from_first_byte(reader, first_byte).await?;
match result {
LengthResult::Length(len) => {
#[allow(clippy::cast_possible_truncation)]
let mut buf = vec![0u8; len as usize];
reader.read_exact(&mut buf).await?;
let s = String::from_utf8_lossy(&buf).into_owned();
Ok(s.into())
}
LengthResult::Encoded(enc_type) => load_encoded(reader, enc_type).await,
}
}
async fn load_encoded<R>(reader: &mut R, enc_type: u8) -> Result<Arc<str>>
where
R: AsyncRead + Unpin,
{
match enc_type {
super::symbols::ENC_INT8 => {
let mut buf = [0u8; 1];
reader.read_exact(&mut buf).await?;
let val = i8::from_le_bytes(buf);
Ok(val.to_string().into())
}
super::symbols::ENC_INT16 => {
let mut bytes = [0u8; 2];
reader.read_exact(&mut bytes).await?;
let val = i16::from_le_bytes(bytes);
Ok(val.to_string().into())
}
super::symbols::ENC_INT32 => {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes).await?;
let val = i32::from_le_bytes(bytes);
Ok(val.to_string().into())
}
super::symbols::ENC_LZF => {
let compressed_len = super::length::load_plain(reader).await?;
let uncompressed_len = super::length::load_plain(reader).await?;
#[allow(clippy::cast_possible_truncation)]
let mut compressed = vec![0u8; compressed_len as usize];
reader.read_exact(&mut compressed).await?;
#[allow(clippy::cast_possible_truncation)]
let decompressed = super::lzf::decompress(&compressed, uncompressed_len as usize)?;
let s = String::from_utf8_lossy(&decompressed).into_owned();
Ok(s.into())
}
_ => Err(IoError::new(
ErrorKind::InvalidData,
format!("Unknown string encoding: {enc_type}"),
)
.into()),
}
}
pub async fn load_raw<R>(reader: &mut R) -> Result<Vec<u8>>
where
R: AsyncRead + Unpin,
{
let first_byte = reader.read_u8().await?;
let result = super::length::load_from_first_byte(reader, first_byte).await?;
match result {
LengthResult::Length(len) => {
#[allow(clippy::cast_possible_truncation)]
let mut buf = vec![0u8; len as usize];
reader.read_exact(&mut buf).await?;
Ok(buf)
}
LengthResult::Encoded(enc_type) => {
match enc_type {
super::symbols::ENC_LZF => {
let compressed_len = super::length::load_plain(reader).await?;
let uncompressed_len = super::length::load_plain(reader).await?;
#[allow(clippy::cast_possible_truncation)]
let mut compressed = vec![0u8; compressed_len as usize];
reader.read_exact(&mut compressed).await?;
#[allow(clippy::cast_possible_truncation)]
let decompressed =
super::lzf::decompress(&compressed, uncompressed_len as usize)?;
Ok(decompressed)
}
_ => {
Ok(vec![first_byte])
}
}
}
}
}
#[allow(clippy::cast_possible_truncation)]
pub async fn save<W>(writer: &mut W, s: &str) -> Result<()>
where
W: AsyncWrite + Unpin,
{
let bytes = s.as_bytes();
if bytes.len() >= super::lzf::MIN_COMPRESS_LEN
&& let Some(compressed) = super::lzf::compress(bytes)
{
writer.write_u8(0xC0 | super::symbols::ENC_LZF).await?;
super::length::save(writer, compressed.len() as u64).await?;
super::length::save(writer, bytes.len() as u64).await?;
writer.write_all(&compressed).await?;
return Ok(());
}
super::length::save(writer, bytes.len() as u64).await?;
writer.write_all(bytes).await?;
Ok(())
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[tokio::test]
async fn test_load_plain_string() {
let data = [0x05, b'h', b'e', b'l', b'l', b'o'];
let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "hello");
}
#[tokio::test]
async fn test_load_empty_string() {
let data = [0x00];
let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "");
}
#[tokio::test]
async fn test_load_int8_encoded() {
let data = [0xC0, 42]; let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "42");
}
#[tokio::test]
async fn test_load_int8_negative() {
let data = [0xC0, 0xFF]; let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "-1");
}
#[tokio::test]
async fn test_load_int16_encoded() {
let data = [0xC1, 0xE8, 0x03]; let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "1000");
}
#[tokio::test]
async fn test_load_int32_encoded() {
let data = [0xC2, 0xA0, 0x86, 0x01, 0x00]; let mut reader = Cursor::new(&data);
let s = load(&mut reader).await.unwrap();
assert_eq!(s.as_ref(), "100000");
}
#[tokio::test]
async fn test_load_raw_bytes() {
let data = [0x05, 0x01, 0x02, 0x03, 0x04, 0x05];
let mut reader = Cursor::new(&data);
let bytes = load_raw(&mut reader).await.unwrap();
assert_eq!(bytes, vec![0x01, 0x02, 0x03, 0x04, 0x05]);
}
}