Trait rkyv::ArchiveUnsized
source · [−]pub trait ArchiveUnsized: Pointee {
type Archived: ArchivePointee + ?Sized;
type MetadataResolver;
unsafe fn resolve_metadata(
&self,
pos: usize,
resolver: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>
);
unsafe fn resolve_unsized(
&self,
from: usize,
to: usize,
resolver: Self::MetadataResolver,
out: *mut RelPtr<Self::Archived>
) { ... }
}
Expand description
A counterpart of Archive
that’s suitable for unsized types.
Unlike Archive
, types that implement ArchiveUnsized
must be serialized separately from their
owning object. For example, whereas an i32
might be laid out as part of a larger struct, a
Box<i32>
would serialize the i32
somewhere in the archive and the Box
would point to it as
part of the larger struct. Because of this, the equivalent Resolver
type
for ArchiveUnsized
is always a usize
representing the position of the serialized value.
ArchiveUnsized
is automatically implemented for all types that implement Archive
. Nothing
special needs to be done to use them with types like Box
, Rc
, and Arc
. It is also already
implemented for slices and string slices, and the rkyv_dyn
crate can be used to archive trait
objects. Other unsized types must manually implement ArchiveUnsized
.
Examples
This example shows how to manually implement ArchiveUnsized
for an unsized type. Special care
must be taken to ensure that the types are laid out correctly.
use core::{mem::transmute, ops::{Deref, DerefMut}};
use ptr_meta::Pointee;
use rkyv::{
from_archived,
to_archived,
archived_unsized_value,
ser::{serializers::AlignedSerializer, Serializer},
AlignedVec,
Archive,
Archived,
ArchivedMetadata,
ArchivePointee,
ArchiveUnsized,
FixedUsize,
RelPtr,
Serialize,
SerializeUnsized,
};
// We're going to be dealing mostly with blocks that have a trailing slice
pub struct Block<H, T: ?Sized> {
head: H,
tail: T,
}
impl<H, T> Pointee for Block<H, [T]> {
type Metadata = usize;
}
// For blocks with trailing slices, we need to store the length of the slice
// in the metadata.
pub struct BlockSliceMetadata {
len: Archived<usize>,
}
// ArchivePointee is automatically derived for sized types because pointers
// to sized types don't need to store any extra information. Because we're
// making an unsized block, we need to define what metadata gets stored with
// our data pointer.
impl<H, T> ArchivePointee for Block<H, [T]> {
// This is the extra data that needs to get stored for blocks with
// trailing slices
type ArchivedMetadata = BlockSliceMetadata;
// We need to be able to turn our archived metadata into regular
// metadata for our type
fn pointer_metadata(
archived: &Self::ArchivedMetadata
) -> <Self as Pointee>::Metadata {
from_archived!(archived.len) as usize
}
}
// We're implementing ArchiveUnsized for just Block<H, [T]>. We can still
// implement Archive for blocks with sized tails and they won't conflict.
impl<H: Archive, T: Archive> ArchiveUnsized for Block<H, [T]> {
// We'll reuse our block type as our archived type.
type Archived = Block<Archived<H>, [Archived<T>]>;
// This is where we'd put any resolve data for our metadata.
// Most of the time, this can just be () because most metadata is Copy,
// but the option is there if you need it.
type MetadataResolver = ();
// Here's where we make the metadata for our pointer.
// This also gets the position and resolver for the metadata, but we
// don't need it in this case.
unsafe fn resolve_metadata(
&self,
_: usize,
_: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>,
) {
unsafe {
out.write(BlockSliceMetadata {
len: to_archived!(self.tail.len() as FixedUsize),
});
}
}
}
// The bounds we use on our serializer type indicate that we need basic
// serializer capabilities, and then whatever capabilities our head and tail
// types need to serialize themselves.
impl<
H: Serialize<S>,
T: Serialize<S>,
S: Serializer + ?Sized
> SerializeUnsized<S> for Block<H, [T]> {
// This is where we construct our unsized type in the serializer
fn serialize_unsized(
&self,
serializer: &mut S
) -> Result<usize, S::Error> {
// First, we archive the head and all the tails. This will make sure
// that when we finally build our block, we don't accidentally mess
// up the structure with serialized dependencies.
let head_resolver = self.head.serialize(serializer)?;
let mut resolvers = Vec::new();
for tail in self.tail.iter() {
resolvers.push(tail.serialize(serializer)?);
}
// Now we align our serializer for our archived type and write it.
// We can't align for unsized types so we treat the trailing slice
// like an array of 0 length for now.
serializer.align_for::<Block<Archived<H>, [Archived<T>; 0]>>()?;
let result = unsafe {
serializer.resolve_aligned(&self.head, head_resolver)?
};
serializer.align_for::<Archived<T>>()?;
for (item, resolver) in self.tail.iter().zip(resolvers.drain(..)) {
unsafe {
serializer.resolve_aligned(item, resolver)?;
}
}
Ok(result)
}
// This is where we serialize the metadata for our type. In this case,
// we do all the work in resolve and don't need to do anything here.
fn serialize_metadata(
&self,
serializer: &mut S
) -> Result<Self::MetadataResolver, S::Error> {
Ok(())
}
}
let value = Block {
head: "Numbers 1-4".to_string(),
tail: [1, 2, 3, 4],
};
// We have a Block<String, [i32; 4]> but we want to it to be a
// Block<String, [i32]>, so we need to do more pointer transmutation
let ptr = (&value as *const Block<String, [i32; 4]>).cast::<()>();
let unsized_value = unsafe {
&*transmute::<(*const (), usize), *const Block<String, [i32]>>((ptr, 4))
};
let mut serializer = AlignedSerializer::new(AlignedVec::new());
let pos = serializer.serialize_unsized_value(unsized_value)
.expect("failed to archive block");
let buf = serializer.into_inner();
let archived_ref = unsafe {
archived_unsized_value::<Block<String, [i32]>>(buf.as_slice(), pos)
};
assert_eq!(archived_ref.head, "Numbers 1-4");
assert_eq!(archived_ref.tail.len(), 4);
assert_eq!(archived_ref.tail, [1, 2, 3, 4]);
Associated Types
type Archived: ArchivePointee + ?Sized
type Archived: ArchivePointee + ?Sized
The archived counterpart of this type. Unlike Archive
, it may be unsized.
This type must implement ArchivePointee
, a trait that helps make valid pointers using
archived pointer metadata.
The resolver for the metadata of this type.
Because the pointer metadata must be archived with the relative pointer and not with the structure itself, its resolver must be passed back to the structure holding the pointer.
Required methods
unsafe fn resolve_metadata(
&self,
pos: usize,
resolver: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>
)
unsafe fn resolve_metadata(
&self,
pos: usize,
resolver: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>
)
Creates the archived version of the metadata for this value at the given position and writes it to the given output.
The output should be initialized field-by-field rather than by writing a whole struct. Performing a typed copy will mark all of the padding bytes as uninitialized, but they must remain set to the value they currently have. This prevents leaking uninitialized memory to the final archive.
Safety
pos
must be the position ofout
within the archiveresolver
must be the result of serializing this object’s metadata
Provided methods
unsafe fn resolve_unsized(
&self,
from: usize,
to: usize,
resolver: Self::MetadataResolver,
out: *mut RelPtr<Self::Archived>
)
unsafe fn resolve_unsized(
&self,
from: usize,
to: usize,
resolver: Self::MetadataResolver,
out: *mut RelPtr<Self::Archived>
)
Resolves a relative pointer to this value with the given from
and to
and writes it to
the given output.
The output should be initialized field-by-field rather than by writing a whole struct. Performing a typed copy will mark all of the padding bytes as uninitialized, but they must remain set to the value they currently have. This prevents leaking uninitialized memory to the final archive.
Safety
from
must be the position ofout
within the archiveto
must be the position of someSelf::Archived
within the archiveresolver
must be the result of serializing this object
Implementations on Foreign Types
sourceimpl<T: Archive> ArchiveUnsized for [T]
impl<T: Archive> ArchiveUnsized for [T]
type Archived = [T::Archived]
type MetadataResolver = ()
unsafe fn resolve_metadata(
&self,
_: usize,
_: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>
)
sourceimpl ArchiveUnsized for str
impl ArchiveUnsized for str
str