use std::io::Read;
use super::utils::read_exact;
use crate::types::{RdbError, RdbResult};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
#[derive(Debug, Clone)]
pub enum ZiplistEntry {
String(Vec<u8>),
Number(i64),
}
pub fn read_ziplist_metadata<T: Read>(input: &mut T) -> RdbResult<(u32, u32, u16)> {
let zlbytes = input.read_u32::<LittleEndian>()?;
let zltail = input.read_u32::<LittleEndian>()?;
let zllen = input.read_u16::<LittleEndian>()?;
Ok((zlbytes, zltail, zllen))
}
pub fn read_ziplist_entry_string<R: Read>(input: &mut R) -> RdbResult<Vec<u8>> {
let entry = read_ziplist_entry(input)?;
match entry {
ZiplistEntry::String(val) => Ok(val),
ZiplistEntry::Number(val) => Ok(val.to_string().into_bytes()),
}
}
fn read_ziplist_entry<R: Read>(input: &mut R) -> RdbResult<ZiplistEntry> {
let byte = input.read_u8()?;
if byte == 254 {
let mut bytes = [0; 4];
match input.read(&mut bytes) {
Ok(4) => (),
Ok(_) => {
return Err(RdbError::MissingValue(
"4 bytes to skip after ziplist length",
))
}
Err(e) => return Err(RdbError::Io(e)),
};
}
let number_value: i64;
let flag = input.read_u8()?;
let length = match (flag & 0xC0) >> 6 {
0 => (flag & 0x3F) as u64,
1 => {
let next_byte = input.read_u8()?;
(((flag & 0x3F) as u64) << 8) | next_byte as u64
}
2 => input.read_u32::<BigEndian>()? as u64,
_ => {
match (flag & 0xF0) >> 4 {
0xC => number_value = input.read_i16::<LittleEndian>()? as i64,
0xD => number_value = input.read_i32::<LittleEndian>()? as i64,
0xE => number_value = input.read_i64::<LittleEndian>()?,
0xF => match flag & 0xF {
0 => {
let mut bytes = [0; 3];
match input.read(&mut bytes) {
Ok(3) => (),
Ok(_) => return Err(RdbError::MissingValue("24bit number")),
Err(e) => return Err(RdbError::Io(e)),
};
let number: i32 = (((bytes[2] as i32) << 24)
^ ((bytes[1] as i32) << 16)
^ ((bytes[0] as i32) << 8)
^ 48)
>> 8;
number_value = number as i64;
}
0xE => {
number_value = input.read_i8()? as i64;
}
_ => {
number_value = (flag & 0xF) as i64 - 1;
}
},
_ => {
return Err(RdbError::ParsingError {
context: "read_ziplist_entry",
message: format!("Unknown encoding value: {}", flag),
});
}
}
return Ok(ZiplistEntry::Number(number_value));
}
};
let rawval = read_exact(input, length as usize)?;
Ok(ZiplistEntry::String(rawval))
}