use crate::{Error, Result};
use byteorder::{ByteOrder, WriteBytesExt, LE};
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(usize)]
pub(crate) enum FramingOffsetSize {
U8 = 1,
U16 = 2,
U32 = 4,
U64 = 8,
U128 = 16,
}
impl FramingOffsetSize {
pub(crate) fn for_bare_container(container_len: usize, num_offsets: usize) -> Self {
let mut offset_size = FramingOffsetSize::U8;
loop {
if container_len + num_offsets * (offset_size as usize) <= offset_size.max() {
return offset_size;
}
offset_size = offset_size
.bump_up()
.expect("Can't handle container too large for a 128-bit pointer");
}
}
pub(crate) fn for_encoded_container(container_len: usize) -> Self {
Self::for_bare_container(container_len, 0)
}
pub(crate) fn write_offset<W>(self, writer: &mut W, offset: usize) -> Result<()>
where
W: std::io::Write,
{
match self {
FramingOffsetSize::U8 => writer.write_u8(offset as u8),
FramingOffsetSize::U16 => writer.write_u16::<LE>(offset as u16),
FramingOffsetSize::U32 => writer.write_u32::<LE>(offset as u32),
FramingOffsetSize::U64 => writer.write_u64::<LE>(offset as u64),
FramingOffsetSize::U128 => writer.write_u128::<LE>(offset as u128),
}
.map_err(Error::Io)
}
pub fn read_last_offset_from_buffer(self, buffer: &[u8]) -> usize {
if buffer.is_empty() {
return 0;
}
let end = buffer.len();
match self {
FramingOffsetSize::U8 => buffer[end - 1] as usize,
FramingOffsetSize::U16 => LE::read_u16(&buffer[end - 2..end]) as usize,
FramingOffsetSize::U32 => LE::read_u32(&buffer[end - 4..end]) as usize,
FramingOffsetSize::U64 => LE::read_u64(&buffer[end - 8..end]) as usize,
FramingOffsetSize::U128 => LE::read_u128(&buffer[end - 16..end]) as usize,
}
}
fn max(self) -> usize {
match self {
FramingOffsetSize::U8 => std::u8::MAX as usize,
FramingOffsetSize::U16 => std::u16::MAX as usize,
FramingOffsetSize::U32 => std::u32::MAX as usize,
FramingOffsetSize::U64 => std::u64::MAX as usize,
FramingOffsetSize::U128 => std::u128::MAX as usize,
}
}
fn bump_up(self) -> Option<Self> {
match self {
FramingOffsetSize::U8 => Some(FramingOffsetSize::U16),
FramingOffsetSize::U16 => Some(FramingOffsetSize::U32),
FramingOffsetSize::U32 => Some(FramingOffsetSize::U64),
FramingOffsetSize::U64 => Some(FramingOffsetSize::U128),
FramingOffsetSize::U128 => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::framing_offset_size::FramingOffsetSize;
#[test]
fn framing_offset_size_bump() {
assert_eq!(
FramingOffsetSize::for_bare_container(std::u8::MAX as usize - 3, 3),
FramingOffsetSize::U8
);
assert_eq!(
FramingOffsetSize::for_bare_container(std::u8::MAX as usize - 1, 2),
FramingOffsetSize::U16
);
assert_eq!(
FramingOffsetSize::for_bare_container(std::u16::MAX as usize - 4, 2),
FramingOffsetSize::U16
);
assert_eq!(
FramingOffsetSize::for_bare_container(std::u16::MAX as usize - 3, 2),
FramingOffsetSize::U32
);
assert_eq!(
FramingOffsetSize::for_bare_container(std::u32::MAX as usize - 12, 3),
FramingOffsetSize::U32
);
assert_eq!(
FramingOffsetSize::for_bare_container(std::u32::MAX as usize - 11, 3),
FramingOffsetSize::U64
);
}
}