Trait rkyv::ArchiveUnsized[][src]

pub trait ArchiveUnsized: Pointee {
    type Archived: ArchivePointee + ?Sized;
    type MetadataResolver;
    fn resolve_metadata(
        &self,
        pos: usize,
        resolver: Self::MetadataResolver
    ) -> ArchivedMetadata<Self>; unsafe fn resolve_unsized(
        &self,
        from: usize,
        to: usize,
        resolver: Self::MetadataResolver
    ) -> RelPtr<Self::Archived> { ... } }

A counterpart of Archive that’s suitable for unsized types.

Instead of archiving its value directly, ArchiveUnsized archives a RelPtr to its archived type. As a consequence, its resolver must be usize.

ArchiveUnsized is automatically implemented for all types that implement Archive.

ArchiveUnsized is 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

use core::{
    mem,
    ops::{Deref, DerefMut},
};
use ptr_meta::Pointee;
use rkyv::{
    archived_unsized_value,
    offset_of,
    ser::{serializers::WriteSerializer, Serializer},
    AlignedVec,
    Archive,
    Archived,
    ArchivedMetadata,
    ArchivedUsize,
    ArchivePointee,
    ArchiveUnsized,
    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: ArchivedUsize,
}

// 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 {
        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.
    fn resolve_metadata(
        &self,
        _: usize,
        _: Self::MetadataResolver
    ) -> ArchivedMetadata<Self> {
        BlockSliceMetadata {
            len: self.tail.len() as ArchivedUsize,
        }
    }
}

// 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 tail_resolvers = Vec::new();
        for tail in self.tail.iter() {
            tail_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 (tail, tail_resolver) in self.tail.iter().zip(tail_resolvers.drain(..)) {
            unsafe {
                serializer.resolve_aligned(tail, tail_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 { &*mem::transmute::<(*const (), usize), *const Block<String, [i32]>>((ptr, 4)) };

let mut serializer = WriteSerializer::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[src]

The archived counterpart of this type. Unlike Archive, it may be unsized.

type MetadataResolver[src]

The resolver for the metadata of this type.

Loading content...

Required methods

fn resolve_metadata(
    &self,
    pos: usize,
    resolver: Self::MetadataResolver
) -> ArchivedMetadata<Self>
[src]

Creates the archived version of the metadata for this value at the given position.

Loading content...

Provided methods

unsafe fn resolve_unsized(
    &self,
    from: usize,
    to: usize,
    resolver: Self::MetadataResolver
) -> RelPtr<Self::Archived>
[src]

Resolves a relative pointer to this value with the given from and to.

Safety

The caller must guarantee that to is the location of an archived value and resolver is the metadata resolver for that value.

Loading content...

Implementations on Foreign Types

impl<T: Archive> ArchiveUnsized for [T][src]

type Archived = [T::Archived]

type MetadataResolver = ()

impl ArchiveUnsized for str[src]

type Archived = str

type MetadataResolver = ()

Loading content...

Implementors

impl<T: Archive> ArchiveUnsized for T[src]

type Archived = T::Archived

type MetadataResolver = ()

Loading content...