use crate::error::DbResult;
pub trait Codec<T>: Send + Sync {
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()>;
fn decode_from(&self, bytes: &[u8]) -> DbResult<T>;
fn size(&self, value: &T) -> usize {
let mut buf = Vec::new();
let _ = self.encode_to(value, &mut buf);
buf.len()
}
}
#[cfg(feature = "rapira-codec")]
#[derive(Default, Clone, Debug)]
pub struct RapiraCodec;
#[cfg(feature = "rapira-codec")]
impl<T: rapira::Rapira> Codec<T> for RapiraCodec {
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
rapira::extend_vec(value, buf);
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<T> {
unsafe {
rapira::deser_unchecked(bytes)
.map_err(|_| crate::error::DbError::CorruptedEntry { offset: 0 })
}
}
fn size(&self, value: &T) -> usize {
rapira::Rapira::size(value)
}
}
#[cfg(feature = "bitcode-codec")]
#[derive(Default, Clone, Debug)]
pub struct BitcodeCodec;
#[cfg(feature = "bitcode-codec")]
impl<T: bitcode::Encode + for<'a> bitcode::Decode<'a>> Codec<T> for BitcodeCodec {
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
buf.extend_from_slice(&bitcode::encode(value));
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<T> {
bitcode::decode(bytes).map_err(|_| crate::error::DbError::CorruptedEntry { offset: 0 })
}
}
#[cfg(feature = "postcard-codec")]
#[derive(Default, Clone, Debug)]
pub struct PostcardCodec;
#[cfg(feature = "postcard-codec")]
impl<T: serde::Serialize + for<'a> serde::Deserialize<'a>> Codec<T> for PostcardCodec {
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
let encoded = postcard::to_allocvec(value)
.map_err(|_| crate::error::DbError::Client("postcard serialization failed"))?;
buf.extend_from_slice(&encoded);
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<T> {
postcard::from_bytes(bytes).map_err(|_| crate::error::DbError::CorruptedEntry { offset: 0 })
}
}
#[derive(Default, Clone, Debug)]
pub struct ZerocopyCodec;
impl<T> Codec<T> for ZerocopyCodec
where
T: zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
{
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
buf.extend_from_slice(zerocopy::IntoBytes::as_bytes(value));
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<T> {
zerocopy::FromBytes::read_from_bytes(bytes)
.map_err(|_| crate::error::DbError::CorruptedEntry { offset: 0 })
}
fn size(&self, _value: &T) -> usize {
size_of::<T>()
}
}
#[cfg(feature = "bytemuck-codec")]
#[derive(Default, Clone, Debug)]
pub struct BytemuckCodec;
#[cfg(feature = "bytemuck-codec")]
impl<T: bytemuck::Pod> Codec<T> for BytemuckCodec {
fn encode_to(&self, value: &T, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
buf.extend_from_slice(bytemuck::bytes_of(value));
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<T> {
if bytes.len() != size_of::<T>() {
return Err(crate::error::DbError::CorruptedEntry { offset: 0 });
}
Ok(bytemuck::pod_read_unaligned(bytes))
}
fn size(&self, _value: &T) -> usize {
size_of::<T>()
}
}
#[cfg(feature = "bytemuck-codec")]
#[derive(Default, Clone, Debug)]
pub struct BytemuckSliceCodec;
#[cfg(feature = "bytemuck-codec")]
impl<W: BytemuckVec> Codec<W> for BytemuckSliceCodec {
fn encode_to(&self, value: &W, buf: &mut Vec<u8>) -> DbResult<()> {
buf.clear();
buf.extend_from_slice(bytemuck::cast_slice(value.as_slice()));
Ok(())
}
fn decode_from(&self, bytes: &[u8]) -> DbResult<W> {
let item_size = size_of::<W::Item>();
if item_size == 0 {
return Ok(W::from_vec(Vec::new()));
}
if !bytes.is_empty() && !bytes.len().is_multiple_of(item_size) {
return Err(crate::error::DbError::CorruptedEntry { offset: 0 });
}
let vec: Vec<W::Item> = bytes
.chunks_exact(item_size)
.map(bytemuck::pod_read_unaligned)
.collect();
Ok(W::from_vec(vec))
}
fn size(&self, value: &W) -> usize {
std::mem::size_of_val(value.as_slice())
}
}
#[cfg(feature = "bytemuck-codec")]
pub trait BytemuckVec: Send + Sync + Sized {
type Item: bytemuck::Pod;
fn from_vec(v: Vec<Self::Item>) -> Self;
fn as_slice(&self) -> &[Self::Item];
}
#[cfg(feature = "bytemuck-codec")]
impl<T: bytemuck::Pod + Send + Sync> BytemuckVec for Vec<T> {
type Item = T;
fn from_vec(v: Vec<T>) -> Self {
v
}
fn as_slice(&self) -> &[T] {
self
}
}
#[cfg(test)]
mod size_tests {
use super::*;
#[test]
fn zerocopy_size_is_constant() {
let c = ZerocopyCodec;
let v: u64 = 42;
assert_eq!(<ZerocopyCodec as Codec<u64>>::size(&c, &v), 8);
}
#[cfg(feature = "rapira-codec")]
#[test]
fn rapira_size_matches_encode_len() {
let c = RapiraCodec;
let v: Vec<u8> = vec![1, 2, 3, 4, 5];
let mut buf = Vec::new();
c.encode_to(&v, &mut buf).unwrap();
assert_eq!(<RapiraCodec as Codec<Vec<u8>>>::size(&c, &v), buf.len());
}
#[cfg(feature = "bytemuck-codec")]
#[test]
fn bytemuck_size_is_constant() {
let c = BytemuckCodec;
let v: [u8; 16] = [0; 16];
assert_eq!(<BytemuckCodec as Codec<[u8; 16]>>::size(&c, &v), 16);
}
#[cfg(feature = "bytemuck-codec")]
#[test]
fn bytemuck_slice_size_matches() {
let c = BytemuckSliceCodec;
let v: Vec<u32> = vec![1, 2, 3, 4];
let mut buf = Vec::new();
c.encode_to(&v, &mut buf).unwrap();
assert_eq!(
<BytemuckSliceCodec as Codec<Vec<u32>>>::size(&c, &v),
buf.len()
);
}
}