ps_datachunk/typed/
mod.rs1mod implementations;
2
3use std::{marker::PhantomData, ops::Deref};
4
5use bytes::Bytes;
6use rancor::{Error, Strategy};
7use rkyv::{
8 api::high::HighValidator,
9 bytecheck::CheckBytes,
10 ser::{allocator::ArenaHandle, sharing::Share, Serializer},
11 util::AlignedVec,
12 Archive, Serialize,
13};
14
15use crate::{AlignedDataChunk, DataChunk, Hash, Result};
16
17pub struct TypedDataChunk<D: DataChunk, T: rkyv::Archive> {
18 chunk: D,
19 _p: PhantomData<T::Archived>,
20}
21
22#[must_use]
23pub fn check_byte_layout<'lt, T>(bytes: &[u8]) -> bool
24where
25 T: Archive,
26 T::Archived: for<'a> CheckBytes<HighValidator<'a, Error>>,
27{
28 rkyv::access::<T::Archived, Error>(bytes).is_ok()
29}
30
31impl<D, T> TypedDataChunk<D, T>
32where
33 D: DataChunk,
34 T: Archive,
35 T::Archived: for<'a> CheckBytes<HighValidator<'a, Error>>,
36{
37 pub fn from_data_chunk(chunk: D) -> Result<Self> {
42 rkyv::access::<T::Archived, Error>(chunk.data_ref())
43 .map_err(|_| crate::DataChunkError::InvalidArchive)?;
44
45 let chunk = Self {
46 _p: PhantomData,
47 chunk,
48 };
49
50 Ok(chunk)
51 }
52
53 pub fn typed_ref(&self) -> Result<&T::Archived> {
58 rkyv::access::<T::Archived, Error>(self.chunk.data_ref())
59 .map_err(|_| crate::DataChunkError::InvalidArchive)
60 }
61}
62
63impl<D, T> Deref for TypedDataChunk<D, T>
64where
65 D: DataChunk,
66 T: Archive,
67 for<'a> <T as Archive>::Archived: CheckBytes<HighValidator<'a, Error>>,
68{
69 type Target = T::Archived;
70
71 fn deref(&self) -> &Self::Target {
72 unsafe { rkyv::access_unchecked::<T::Archived>(self.chunk.data_ref()) }
78 }
79}
80
81impl<D, T> DataChunk for TypedDataChunk<D, T>
82where
83 D: DataChunk,
84 T: Archive,
85 for<'a> <T as Archive>::Archived: CheckBytes<HighValidator<'a, Error>>,
86{
87 fn data_ref(&self) -> &[u8] {
88 self.chunk.data_ref()
89 }
90
91 fn hash_ref(&self) -> &Hash {
92 self.chunk.hash_ref()
93 }
94
95 fn into_bytes(self) -> Bytes {
97 self.chunk.into_bytes()
98 }
99
100 fn into_owned(self) -> crate::OwnedDataChunk {
102 let Self { chunk, _p } = self;
103
104 chunk.into_owned()
105 }
106}
107
108pub trait ToDataChunk {
109 fn to_datachunk(&self) -> Result<AlignedDataChunk>;
110}
111
112impl<T: Archive + ToTypedDataChunk<T>> ToDataChunk for T {
113 fn to_datachunk(&self) -> Result<AlignedDataChunk> {
114 Ok(self.to_typed_datachunk()?.chunk)
115 }
116}
117
118pub trait ToTypedDataChunk<T: Archive> {
119 fn to_typed_datachunk(&self) -> Result<TypedDataChunk<AlignedDataChunk, T>>;
120}
121
122impl<T> ToTypedDataChunk<T> for T
123where
124 T::Archived: for<'a> CheckBytes<HighValidator<'a, Error>>,
125 T: rkyv::Archive
126 + for<'a> Serialize<Strategy<Serializer<AlignedVec, ArenaHandle<'a>, Share>, Error>>,
127{
128 fn to_typed_datachunk(&self) -> Result<TypedDataChunk<AlignedDataChunk, T>> {
129 let chunk = AlignedDataChunk::try_from::<T>(self)?;
130
131 TypedDataChunk::from_data_chunk(chunk)
132 }
133}
134
135#[allow(clippy::expect_used)]
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::{DataChunkError, OwnedDataChunk};
140
141 #[test]
142 fn typed_ref_returns_checked_ref() -> Result<()> {
143 let typed = 42_u32.to_typed_datachunk()?;
144
145 assert_eq!(*typed.typed_ref()?, 42_u32);
146 assert_eq!(*typed, *typed.typed_ref()?);
147
148 Ok(())
149 }
150
151 #[test]
152 fn from_data_chunk_rejects_invalid_archive() {
153 let chunk = OwnedDataChunk::from_data([1_u8, 2, 3]).expect("hashing failed");
154
155 let result = TypedDataChunk::<OwnedDataChunk, u32>::from_data_chunk(chunk);
156
157 assert!(matches!(result, Err(DataChunkError::InvalidArchive)));
158 }
159}