1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
//! Module for [`TagTrait`].
use crate::{Tag, TagType};
use ptr_meta::Pointee;
/// A trait to abstract over all sized and unsized tags (DSTs). For sized tags,
/// this trait does not much. For DSTs, a [`TagTrait::dst_size`] implementation
/// must be provided, which returns the right size hint for the dynamically
/// sized portion of the struct.
///
/// # Trivia
/// This crate uses the [`Pointee`]-abstraction of the [`ptr_meta`] crate to
/// create fat pointers for tags that are DST.
pub trait TagTrait: Pointee {
/// The numeric ID of this tag.
const ID: TagType;
/// Returns the amount of items in the dynamically sized portion of the
/// DST. Note that this is not the amount of bytes. So if the dynamically
/// sized portion is 16 bytes in size and each element is 4 bytes big, then
/// this function must return 4.
///
/// For sized tags, this just returns `()`. For DSTs, this returns an
/// `usize`.
fn dst_size(base_tag: &Tag) -> Self::Metadata;
/// Returns the tag as the common base tag structure.
fn as_base_tag(&self) -> &Tag {
let ptr = core::ptr::addr_of!(*self);
unsafe { &*ptr.cast::<Tag>() }
}
/// Returns the total size of the tag. The depends on the `size` field of
/// the tag.
fn size(&self) -> usize {
self.as_base_tag().size as usize
}
/// Returns a slice to the underlying bytes of the tag. This includes all
/// bytes, also for tags that are DSTs. The slice length depends on the
/// `size` field of the tag.
fn as_bytes(&self) -> &[u8] {
let ptr = core::ptr::addr_of!(*self);
unsafe { core::slice::from_raw_parts(ptr.cast(), self.size()) }
}
/// Creates a reference to a (dynamically sized) tag type in a safe way.
/// DST tags need to implement a proper [`Self::dst_size`] implementation.
///
/// # Safety
/// Callers must be sure that the "size" field of the provided [`Tag`] is
/// sane and the underlying memory valid. The implementation of this trait
/// **must have** a correct [`Self::dst_size`] implementation.
unsafe fn from_base_tag(tag: &Tag) -> &Self {
let ptr = core::ptr::addr_of!(*tag);
let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag));
&*ptr
}
}