I have the following struct with conversion logic:
enum TagType {
DOUBLE,
BYTE,
}
impl TagType {
/// byte-size actually
fn size(&self) -> usize {
match self {
TagType::DOUBLE => 8,
TagType::BYTE => 1,
}
}
}
/// Entry with buffered data.
///
/// Should not be used for tags where the data fits in the offset field
/// byte-order of the data should be native-endian in this buffer
#[derive(Debug, PartialEq, Clone)]
pub struct BufferedEntry {
pub tag_type: TagType,
pub count: u64,
pub data: Vec<u8>,
}
impl<'a> TryFrom<&'a BufferedEntry> for &'a [f64] {
type Error = ();
fn try_from(val: &'a BufferedEntry) -> Result<Self, Self::Error> {
if val.data.len() != val.tag_type.size() * usize::try_from(val.count)? {
return Err(());
}
match val.tag_type {
TagType::DOUBLE => Ok(bytemuck::cast_slice(&val.data()[..])),
_ => Err(()),
}
}
}
Now, this will panic if the input vec is not properly aligned to an 8-byte boundary, which I cannot do. Or well, rkyv has AlignedVec which should be exactly what I need. The question, however, is: How can I test for this? e.g. how could I write a test that allocates a non-64-aligned vector?
As far as I know, you can't force the Allocator
to reduce alignment. Normal Allocator
's typically have a minimum alignment. Instead, with nightly
Rust, you can use your own dubious Allocator
:
#![feature(allocator_api)]
#![feature(ptr_metadata)]
use std::alloc::{AllocError, Allocator, Layout, System};
use std::ptr::NonNull;
fn main() {
let unaligned = Vec::<u8, _>::with_capacity_in(1, MinimizeAlignment);
// Ok.
assert!(unaligned.as_ptr().addr() % 2 != 0);
// Panic.
bytemuck::cast_slice::<u8, f64>(&unaligned);
}
/// Allocations are aligned based on `Layout` (in
/// that accessing them is perfectly sound), but
/// not over-aligned unlike typical allocators
/// which align to several words regardless of
/// `Layout`.
struct MinimizeAlignment;
unsafe impl Allocator for MinimizeAlignment {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let new =
Layout::from_size_align(layout.size() + layout.align(), layout.align() * 2).unwrap();
let slice = System.allocate(new)?;
let (ptr, meta) = slice.to_raw_parts();
let slice = unsafe { NonNull::from_raw_parts(ptr.byte_add(layout.align()), meta - layout.align()) };
Ok(slice)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
let new =
Layout::from_size_align(layout.size() + layout.align(), layout.align() * 2).unwrap();
System.deallocate(unsafe { ptr.sub(layout.align()) }, new)
}
}