trine-kv 0.2.0

Embedded LSM MVCC key-value database.
Documentation
use crate::{Error, error::Result};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodecId {
    None,
    FastLz4Block,
}

impl CodecId {
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::None => "none",
            Self::FastLz4Block => "fast-lz4-block",
        }
    }

    pub(crate) const fn tag(self) -> u8 {
        match self {
            Self::None => 0,
            Self::FastLz4Block => 1,
        }
    }

    pub(crate) fn from_tag(tag: u8) -> Result<Self> {
        match tag {
            0 => Ok(Self::None),
            1 => Ok(Self::FastLz4Block),
            tag => Err(Error::UnsupportedFormat {
                message: format!("unknown table codec {tag}"),
            }),
        }
    }
}

pub trait BlockCodec: Send + Sync {
    fn encode(&self, input: &[u8]) -> Result<Vec<u8>>;

    fn decode(&self, input: &[u8], uncompressed_len: usize) -> Result<Vec<u8>>;
}

#[derive(Debug, Default, Clone, Copy)]
pub struct NoneCodec;

impl BlockCodec for NoneCodec {
    fn encode(&self, input: &[u8]) -> Result<Vec<u8>> {
        Ok(input.to_vec())
    }

    fn decode(&self, input: &[u8], uncompressed_len: usize) -> Result<Vec<u8>> {
        if input.len() == uncompressed_len {
            Ok(input.to_vec())
        } else {
            Err(Error::InvalidFormat {
                message: "uncompressed block length mismatch".to_owned(),
            })
        }
    }
}

#[derive(Debug, Default, Clone, Copy)]
pub struct FastLz4BlockCodec;

impl BlockCodec for FastLz4BlockCodec {
    fn encode(&self, input: &[u8]) -> Result<Vec<u8>> {
        Ok(lz4_flex::block::compress(input))
    }

    fn decode(&self, input: &[u8], uncompressed_len: usize) -> Result<Vec<u8>> {
        let decoded = lz4_flex::block::decompress(input, uncompressed_len).map_err(|error| {
            Error::InvalidFormat {
                message: format!("invalid lz4 block: {error}"),
            }
        })?;
        if decoded.len() == uncompressed_len {
            Ok(decoded)
        } else {
            Err(Error::InvalidFormat {
                message: "lz4 block length mismatch".to_owned(),
            })
        }
    }
}

pub(crate) fn encode_block(codec: CodecId, input: &[u8]) -> Result<Vec<u8>> {
    match codec {
        CodecId::None => NoneCodec.encode(input),
        CodecId::FastLz4Block => FastLz4BlockCodec.encode(input),
    }
}

pub(crate) fn decode_block(
    codec: CodecId,
    input: &[u8],
    uncompressed_len: usize,
) -> Result<Vec<u8>> {
    match codec {
        CodecId::None => NoneCodec.decode(input, uncompressed_len),
        CodecId::FastLz4Block => FastLz4BlockCodec.decode(input, uncompressed_len),
    }
}