use crate::{MapletError, MapletResult};
pub struct VarIntEncoder;
impl VarIntEncoder {
#[must_use]
pub fn encode_u64(value: u64) -> Vec<u8> {
let mut result = Vec::new();
let mut val = value;
while val >= 128 {
result.push(u8::try_from((val & 0x7F) | 0x80).unwrap_or(0));
val >>= 7;
}
result.push(u8::try_from(val).unwrap_or(0));
result
}
pub fn decode_u64(data: &[u8]) -> MapletResult<(u64, usize)> {
if data.is_empty() {
return Err(MapletError::SerializationError("Empty data".to_string()));
}
let mut result = 0u64;
let mut shift = 0;
let mut bytes_read = 0;
for &byte in data {
bytes_read += 1;
result |= u64::from(byte & 0x7F) << shift;
if (byte & 0x80) == 0 {
return Ok((result, bytes_read));
}
shift += 7;
if shift >= 64 {
return Err(MapletError::SerializationError(
"Value too large".to_string(),
));
}
}
Err(MapletError::SerializationError(
"Incomplete encoding".to_string(),
))
}
#[must_use]
pub fn encode_u32(value: u32) -> Vec<u8> {
Self::encode_u64(u64::from(value))
}
pub fn decode_u32(data: &[u8]) -> MapletResult<(u32, usize)> {
let (value, bytes_read) = Self::decode_u64(data)?;
if value > u64::from(u32::MAX) {
return Err(MapletError::SerializationError(
"Value too large for u32".to_string(),
));
}
Ok((u32::try_from(value).unwrap_or(0), bytes_read))
}
}
pub struct ExponentialEncoder {
#[allow(dead_code)]
base: f64,
#[allow(dead_code)]
precision: u32,
}
impl ExponentialEncoder {
#[must_use]
pub const fn new(base: f64, precision: u32) -> Self {
Self { base, precision }
}
#[must_use]
pub fn encode_counter(&self, value: u64) -> Vec<u8> {
if value == 0 {
return vec![0];
}
VarIntEncoder::encode_u64(value)
}
pub fn decode_counter(&self, data: &[u8]) -> MapletResult<(u64, usize)> {
if data.is_empty() {
return Err(MapletError::SerializationError("Empty data".to_string()));
}
if data[0] == 0 {
return Ok((0, 1));
}
VarIntEncoder::decode_u64(data)
}
}
pub struct CompactEncoder;
impl CompactEncoder {
pub fn encode_inline<T: Copy + bytemuck::Pod>(value: &T) -> [u8; 8] {
let mut result = [0u8; 8];
let bytes = bytemuck::bytes_of(value);
result[..bytes.len()].copy_from_slice(bytes);
result
}
pub fn decode_inline<T: Copy + bytemuck::Pod>(data: &[u8; 8]) -> MapletResult<T> {
let size = std::mem::size_of::<T>();
if size > 8 {
return Err(MapletError::SerializationError(
"Type too large for inline encoding".to_string(),
));
}
let slice = &data[..size];
bytemuck::try_from_bytes(slice)
.copied()
.map_err(|e| MapletError::SerializationError(format!("Decode error: {e}")))
}
pub const fn can_encode_inline<T>(_value: &T) -> bool {
std::mem::size_of::<T>() <= 8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_varint_encoding() {
assert_eq!(VarIntEncoder::encode_u64(0), vec![0]);
assert_eq!(VarIntEncoder::encode_u64(127), vec![127]);
assert_eq!(VarIntEncoder::encode_u64(128), vec![0x80, 0x01]);
assert_eq!(VarIntEncoder::encode_u64(16383), vec![0xFF, 0x7F]);
for value in [0, 1, 127, 128, 16383, 16384, 1_000_000, u64::MAX] {
let encoded = VarIntEncoder::encode_u64(value);
let (decoded, bytes_read) = VarIntEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, value);
assert_eq!(bytes_read, encoded.len());
}
}
#[test]
fn test_exponential_encoding() {
let encoder = ExponentialEncoder::new(2.0, 16);
for value in [0, 1, 2, 4, 8, 16, 100, 1000, 10000] {
let encoded_data = encoder.encode_counter(value);
let (decoded, bytes_read) = encoder.decode_counter(&encoded_data).unwrap();
assert_eq!(decoded, value);
assert_eq!(bytes_read, encoded_data.len());
}
}
#[test]
fn test_compact_encoding() {
let value: u32 = 0x1234_5678;
let encoded = CompactEncoder::encode_inline(&value);
let decoded: u32 = CompactEncoder::decode_inline(&encoded).unwrap();
assert_eq!(decoded, value);
let value: u64 = 0x1234_5678_9ABC_DEF0;
let encoded = CompactEncoder::encode_inline(&value);
let decoded: u64 = CompactEncoder::decode_inline(&encoded).unwrap();
assert_eq!(decoded, value);
}
}