use bytes::Bytes;
use rkyv::api::high::{HighDeserializer, HighSerializer, HighValidator};
use rkyv::rancor::Error as RancorError;
use rkyv::ser::allocator::ArenaHandle;
use rkyv::util::AlignedVec;
use rkyv::{Archive, Deserialize};
use std::fmt;
pub use rkyv;
pub use rkyv::rancor::Error as RkyvRancorError;
pub use rkyv::Archived;
pub use rkyv::Deserialize as RkyvDeserialize;
pub use rkyv::Serialize as RkyvSerialize;
#[derive(Debug)]
pub enum RkyvError {
Serialization(String),
Deserialization(String),
Validation(String),
InvalidAlignment { required: usize, actual: usize },
}
impl fmt::Display for RkyvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Serialization(msg) => write!(f, "Serialization error: {msg}"),
Self::Deserialization(msg) => write!(f, "Deserialization error: {msg}"),
Self::Validation(msg) => write!(f, "Validation error: {msg}"),
Self::InvalidAlignment { required, actual } => {
write!(
f,
"Invalid alignment: required {required} bytes, got {actual} bytes"
)
}
}
}
}
impl std::error::Error for RkyvError {}
#[derive(Debug, Default)]
pub struct RkyvSerializer;
impl RkyvSerializer {
#[must_use]
pub fn new() -> Self {
Self
}
pub fn serialize<T>(&self, value: &T) -> Result<Bytes, RkyvError>
where
T: for<'a> rkyv::Serialize<HighSerializer<AlignedVec, ArenaHandle<'a>, RancorError>>,
{
let vec = rkyv::to_bytes::<RancorError>(value)
.map_err(|e| RkyvError::Serialization(e.to_string()))?;
Ok(Bytes::from(vec.into_vec()))
}
pub fn serialize_aligned<T>(&self, value: &T) -> Result<AlignedVec, RkyvError>
where
T: for<'a> rkyv::Serialize<HighSerializer<AlignedVec, ArenaHandle<'a>, RancorError>>,
{
rkyv::to_bytes::<RancorError>(value).map_err(|e| RkyvError::Serialization(e.to_string()))
}
}
pub fn zero_copy_access<T>(bytes: &[u8]) -> Result<&T, RkyvError>
where
T: rkyv::Portable + for<'a> rkyv::bytecheck::CheckBytes<HighValidator<'a, RancorError>>,
{
rkyv::access::<T, RancorError>(bytes).map_err(|e| RkyvError::Validation(e.to_string()))
}
pub fn deserialize<T>(bytes: &[u8]) -> Result<T, RkyvError>
where
T: Archive,
T::Archived: for<'a> rkyv::bytecheck::CheckBytes<HighValidator<'a, RancorError>>
+ Deserialize<T, HighDeserializer<RancorError>>,
{
rkyv::from_bytes::<T, RancorError>(bytes).map_err(|e| RkyvError::Deserialization(e.to_string()))
}
#[must_use]
pub fn is_aligned<T>(bytes: &[u8]) -> bool {
let alignment = std::mem::align_of::<T>();
(bytes.as_ptr() as usize).is_multiple_of(alignment)
}
pub fn validate_alignment<T>(bytes: &[u8]) -> Result<(), RkyvError> {
let alignment = std::mem::align_of::<T>();
let actual = bytes.as_ptr() as usize % alignment;
if actual != 0 {
return Err(RkyvError::InvalidAlignment {
required: alignment,
actual,
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use rkyv::{Archive, Deserialize, Serialize};
#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)]
#[rkyv(compare(PartialEq))]
#[rkyv(derive(Debug))]
struct TestStruct {
id: u64,
name: String,
active: bool,
}
#[test]
fn test_serialization_roundtrip() {
let original = TestStruct {
id: 42,
name: "test".to_string(),
active: true,
};
let serializer = RkyvSerializer::new();
let aligned = serializer
.serialize_aligned(&original)
.expect("serialization failed");
let deserialized: TestStruct =
deserialize(aligned.as_ref()).expect("deserialization failed");
assert_eq!(original, deserialized);
}
#[test]
fn test_zero_copy_access() {
let original = TestStruct {
id: 42,
name: "test".to_string(),
active: true,
};
let serializer = RkyvSerializer::new();
let aligned = serializer
.serialize_aligned(&original)
.expect("serialization failed");
let archived =
zero_copy_access::<ArchivedTestStruct>(aligned.as_ref()).expect("access failed");
assert_eq!(archived.id, 42);
assert_eq!(archived.name.as_str(), "test");
assert!(archived.active);
}
#[test]
fn test_default_serializer() {
let serializer = RkyvSerializer;
let original = TestStruct {
id: 1,
name: "large".to_string(),
active: false,
};
let bytes = serializer
.serialize(&original)
.expect("serialization failed");
assert!(!bytes.is_empty());
}
#[test]
fn test_alignment_validation() {
let original = TestStruct {
id: 99,
name: "align_test".to_string(),
active: true,
};
let serializer = RkyvSerializer::new();
let aligned = serializer
.serialize_aligned(&original)
.expect("serialization failed");
assert!(
is_aligned::<ArchivedTestStruct>(aligned.as_ref()),
"AlignedVec should guarantee proper alignment for ArchivedTestStruct"
);
assert!(
validate_alignment::<ArchivedTestStruct>(aligned.as_ref()).is_ok(),
"validate_alignment should succeed for properly aligned data"
);
}
#[test]
fn test_error_display() {
let err = RkyvError::Serialization("test error".to_string());
assert_eq!(err.to_string(), "Serialization error: test error");
let err = RkyvError::InvalidAlignment {
required: 8,
actual: 4,
};
assert_eq!(
err.to_string(),
"Invalid alignment: required 8 bytes, got 4 bytes"
);
}
#[test]
fn test_serialize_produces_valid_bytes() {
let original = TestStruct {
id: 77,
name: "bytes_test".to_string(),
active: true,
};
let serializer = RkyvSerializer::new();
let bytes = serializer
.serialize(&original)
.expect("serialization failed");
assert!(!bytes.is_empty(), "serialized bytes should not be empty");
assert!(
bytes.len() > 4,
"serialized bytes should contain enough data for the struct fields"
);
}
#[test]
fn test_serialize_aligned() {
let original = TestStruct {
id: 123,
name: "aligned".to_string(),
active: false,
};
let serializer = RkyvSerializer::new();
let aligned = serializer
.serialize_aligned(&original)
.expect("serialization failed");
let deserialized: TestStruct =
deserialize(aligned.as_ref()).expect("deserialization failed");
assert_eq!(original, deserialized);
}
}