mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
Use the now-stabilized pointer_bytes_offsets
instead of what is technically UB and clean up the BufferContents
derive macro expansion (#2563)
* Clean up `BufferContents` derive expansion * Use the now-stabilized `pointer_bytes_offsets` instead of technical UB
This commit is contained in:
parent
a0b909fd8b
commit
f508cab5da
@ -12,7 +12,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.72.0"
|
rust-version = "1.75.0"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
homepage = "https://vulkano.rs"
|
homepage = "https://vulkano.rs"
|
||||||
keywords = ["vulkan", "bindings", "graphics", "gpu", "rendering"]
|
keywords = ["vulkan", "bindings", "graphics", "gpu", "rendering"]
|
||||||
|
@ -13,7 +13,7 @@ pub fn derive_buffer_contents(crate_ident: &Ident, mut ast: DeriveInput) -> Resu
|
|||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&attr| attr.path().is_ident("repr"))
|
.filter(|&attr| attr.path().is_ident("repr"))
|
||||||
.map(|attr| {
|
.all(|attr| {
|
||||||
let mut is_repr_rust = true;
|
let mut is_repr_rust = true;
|
||||||
|
|
||||||
let _ = attr.parse_nested_meta(|meta| {
|
let _ = attr.parse_nested_meta(|meta| {
|
||||||
@ -25,8 +25,7 @@ pub fn derive_buffer_contents(crate_ident: &Ident, mut ast: DeriveInput) -> Resu
|
|||||||
});
|
});
|
||||||
|
|
||||||
is_repr_rust
|
is_repr_rust
|
||||||
})
|
});
|
||||||
.all(|b| b);
|
|
||||||
|
|
||||||
if is_repr_rust {
|
if is_repr_rust {
|
||||||
bail!(
|
bail!(
|
||||||
@ -35,6 +34,18 @@ pub fn derive_buffer_contents(crate_ident: &Ident, mut ast: DeriveInput) -> Resu
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let data = match &ast.data {
|
||||||
|
Data::Struct(data) => data,
|
||||||
|
Data::Enum(_) => bail!("deriving `BufferContents` for enums is not supported"),
|
||||||
|
Data::Union(_) => bail!("deriving `BufferContents` for unions is not supported"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let fields = match &data.fields {
|
||||||
|
Fields::Named(FieldsNamed { named, .. }) => named,
|
||||||
|
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
|
||||||
|
Fields::Unit => bail!("zero-sized types are not valid buffer contents"),
|
||||||
|
};
|
||||||
|
|
||||||
let (impl_generics, type_generics, where_clause) = {
|
let (impl_generics, type_generics, where_clause) = {
|
||||||
let predicates = ast
|
let predicates = ast
|
||||||
.generics
|
.generics
|
||||||
@ -52,30 +63,85 @@ pub fn derive_buffer_contents(crate_ident: &Ident, mut ast: DeriveInput) -> Resu
|
|||||||
ast.generics.split_for_impl()
|
ast.generics.split_for_impl()
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout = write_layout(crate_ident, &ast)?;
|
let mut field_types = fields.iter().map(|field| &field.ty);
|
||||||
|
let Some(last_field_type) = field_types.next_back() else {
|
||||||
|
bail!("zero-sized types are not valid buffer contents");
|
||||||
|
};
|
||||||
|
let mut field_layouts = Vec::new();
|
||||||
|
|
||||||
Ok(quote! {
|
let mut bound_types = Vec::new();
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe impl #impl_generics ::#crate_ident::buffer::BufferContents
|
|
||||||
for #struct_ident #type_generics #where_clause
|
|
||||||
{
|
|
||||||
const LAYOUT: ::#crate_ident::buffer::BufferContentsLayout = #layout;
|
|
||||||
|
|
||||||
#[inline(always)]
|
// Accumulate the field layouts and types that have to implement `BufferContents` in order for
|
||||||
unsafe fn ptr_from_slice(slice: ::std::ptr::NonNull<[u8]>) -> *mut Self {
|
// the struct to implement the trait as well.
|
||||||
#[repr(C)]
|
for field_type in field_types {
|
||||||
union PtrRepr<T: ?Sized> {
|
bound_types.push(find_innermost_element_type(field_type));
|
||||||
components: PtrComponents,
|
field_layouts.push(quote! { ::std::alloc::Layout::new::<#field_type>() });
|
||||||
ptr: *mut T,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
let layout;
|
||||||
#[repr(C)]
|
let ptr_from_slice;
|
||||||
struct PtrComponents {
|
|
||||||
data: *mut u8,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// The last field needs special treatment.
|
||||||
|
match last_field_type {
|
||||||
|
// An array might not implement `BufferContents` depending on the element. However, we know
|
||||||
|
// the type must be sized, so we can generate the layout and function easily.
|
||||||
|
Type::Array(TypeArray { elem, .. }) => {
|
||||||
|
bound_types.push(find_innermost_element_type(elem));
|
||||||
|
|
||||||
|
layout = quote! {
|
||||||
|
::#crate_ident::buffer::BufferContentsLayout::from_sized(
|
||||||
|
::std::alloc::Layout::new::<Self>()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr_from_slice = quote! {
|
||||||
|
debug_assert_eq!(slice.len(), ::std::mem::size_of::<Self>());
|
||||||
|
|
||||||
|
<*mut [u8]>::cast::<Self>(slice.as_ptr())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// A slice might contain an array which might not implement `BufferContents`. However, we
|
||||||
|
// know the type must be unsized, so can generate the layout and function easily.
|
||||||
|
Type::Slice(TypeSlice { elem, .. }) => {
|
||||||
|
bound_types.push(find_innermost_element_type(elem));
|
||||||
|
|
||||||
|
layout = quote! {
|
||||||
|
::#crate_ident::buffer::BufferContentsLayout::from_field_layouts(
|
||||||
|
&[ #( #field_layouts ),* ],
|
||||||
|
::#crate_ident::buffer::BufferContentsLayout::from_slice(
|
||||||
|
::std::alloc::Layout::new::<#elem>(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr_from_slice = quote! {
|
||||||
|
let data = <*mut [u8]>::cast::<#elem>(slice.as_ptr());
|
||||||
|
|
||||||
|
let head_size = <Self as ::#crate_ident::buffer::BufferContents>::LAYOUT
|
||||||
|
.head_size() as usize;
|
||||||
|
let element_size = ::std::mem::size_of::<#elem>();
|
||||||
|
|
||||||
|
::std::debug_assert!(slice.len() >= head_size);
|
||||||
|
let tail_size = slice.len() - head_size;
|
||||||
|
::std::debug_assert!(tail_size % element_size == 0);
|
||||||
|
let len = tail_size / element_size;
|
||||||
|
|
||||||
|
::std::ptr::slice_from_raw_parts_mut(data, len) as *mut Self
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Any other type may be either sized or unsized and the macro has no way of knowing. But
|
||||||
|
// it surely implements `BufferContents`, so we can use the existing layout and function.
|
||||||
|
ty => {
|
||||||
|
bound_types.push(ty);
|
||||||
|
|
||||||
|
layout = quote! {
|
||||||
|
::#crate_ident::buffer::BufferContentsLayout::from_field_layouts(
|
||||||
|
&[ #( #field_layouts ),* ],
|
||||||
|
<#last_field_type as ::#crate_ident::buffer::BufferContents>::LAYOUT,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr_from_slice = quote! {
|
||||||
let data = <*mut [u8]>::cast::<u8>(slice.as_ptr());
|
let data = <*mut [u8]>::cast::<u8>(slice.as_ptr());
|
||||||
|
|
||||||
let head_size = <Self as ::#crate_ident::buffer::BufferContents>::LAYOUT
|
let head_size = <Self as ::#crate_ident::buffer::BufferContents>::LAYOUT
|
||||||
@ -87,101 +153,21 @@ pub fn derive_buffer_contents(crate_ident: &Ident, mut ast: DeriveInput) -> Resu
|
|||||||
::std::debug_assert!(slice.len() >= head_size);
|
::std::debug_assert!(slice.len() >= head_size);
|
||||||
let tail_size = slice.len() - head_size;
|
let tail_size = slice.len() - head_size;
|
||||||
::std::debug_assert!(tail_size % element_size == 0);
|
::std::debug_assert!(tail_size % element_size == 0);
|
||||||
let len = tail_size / element_size;
|
|
||||||
|
|
||||||
let components = PtrComponents { data, len };
|
<#last_field_type as ::#crate_ident::buffer::BufferContents>::ptr_from_slice(
|
||||||
|
::std::ptr::NonNull::new_unchecked(::std::ptr::slice_from_raw_parts_mut(
|
||||||
// SAFETY: All fields must implement `BufferContents`. The last field, if it is
|
data.add(head_size),
|
||||||
// unsized, must therefore be a slice or a DST derived from a slice. It cannot be
|
tail_size,
|
||||||
// any other kind of DST, unless unsafe code was used to achieve that.
|
)),
|
||||||
//
|
|
||||||
// That means we can safely rely on knowing what kind of DST the implementing type
|
|
||||||
// is, but it doesn't tell us what the correct representation for the pointer of
|
|
||||||
// this kind of DST is. For that we have to rely on what the docs tell us, namely
|
|
||||||
// that for structs where the last field is a DST, the metadata is the same as the
|
|
||||||
// last field's. We also know that the metadata of a slice is its length measured
|
|
||||||
// in the number of elements. This tells us that the components of a pointer to the
|
|
||||||
// implementing type are the address to the start of the data, and a length. It
|
|
||||||
// still does not tell us what the representation of the pointer is though.
|
|
||||||
//
|
|
||||||
// In fact, there is no way to be certain that this representation is correct.
|
|
||||||
// *Theoretically* rustc could decide tomorrow that the metadata comes first and
|
|
||||||
// the address comes last, but the chance of that ever happening is zero.
|
|
||||||
//
|
|
||||||
// But what if the implementing type is actually sized? In that case this
|
|
||||||
// conversion will simply discard the length field, and only leave the pointer.
|
|
||||||
PtrRepr { components }.ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_layout(crate_ident: &Ident, ast: &DeriveInput) -> Result<TokenStream> {
|
|
||||||
let data = match &ast.data {
|
|
||||||
Data::Struct(data) => data,
|
|
||||||
Data::Enum(_) => bail!("deriving `BufferContents` for enums is not supported"),
|
|
||||||
Data::Union(_) => bail!("deriving `BufferContents` for unions is not supported"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let fields = match &data.fields {
|
|
||||||
Fields::Named(FieldsNamed { named, .. }) => named,
|
|
||||||
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
|
|
||||||
Fields::Unit => bail!("zero-sized types are not valid buffer contents"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut field_types = fields.iter().map(|field| &field.ty);
|
|
||||||
let last_field_type = field_types.next_back().unwrap();
|
|
||||||
let mut layout = quote! { ::std::alloc::Layout::new::<()>() };
|
|
||||||
|
|
||||||
let mut bound_types = Vec::new();
|
|
||||||
|
|
||||||
// Construct the layout of the head and accumulate the types that have to implement
|
|
||||||
// `BufferContents` in order for the struct to implement the trait as well.
|
|
||||||
for field_type in field_types {
|
|
||||||
bound_types.push(find_innermost_element_type(field_type));
|
|
||||||
|
|
||||||
layout = quote! {
|
|
||||||
extend_layout(#layout, ::std::alloc::Layout::new::<#field_type>())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last field needs special treatment.
|
|
||||||
match last_field_type {
|
|
||||||
// An array might not implement `BufferContents` depending on the element, and therefore we
|
|
||||||
// can't use `BufferContents::extend_from_layout` on it.
|
|
||||||
Type::Array(TypeArray { elem, .. }) => {
|
|
||||||
bound_types.push(find_innermost_element_type(elem));
|
|
||||||
layout = quote! {
|
|
||||||
::#crate_ident::buffer::BufferContentsLayout::from_sized(
|
|
||||||
::std::alloc::Layout::new::<Self>()
|
|
||||||
)
|
)
|
||||||
|
.byte_sub(head_size) as *mut Self
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// A slice might contain an array same as above, and therefore we can't use
|
};
|
||||||
// `BufferContents::extend_from_layout` on it either.
|
|
||||||
Type::Slice(TypeSlice { elem, .. }) => {
|
|
||||||
bound_types.push(find_innermost_element_type(elem));
|
|
||||||
layout = quote! {
|
|
||||||
::#crate_ident::buffer::BufferContentsLayout::from_head_element_layout(
|
|
||||||
#layout,
|
|
||||||
::std::alloc::Layout::new::<#elem>(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ty => {
|
|
||||||
bound_types.push(ty);
|
|
||||||
layout = quote! {
|
|
||||||
<#last_field_type as ::#crate_ident::buffer::BufferContents>::LAYOUT
|
|
||||||
.extend_from_layout(&#layout)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (impl_generics, _, where_clause) = ast.generics.split_for_impl();
|
|
||||||
|
|
||||||
let bounds = bound_types.into_iter().map(|ty| {
|
let bounds = bound_types.into_iter().map(|ty| {
|
||||||
quote_spanned! { ty.span() =>
|
quote_spanned! { ty.span() =>
|
||||||
{
|
const _: () = {
|
||||||
// HACK: This works around Rust issue #48214, which makes it impossible to put
|
// HACK: This works around Rust issue #48214, which makes it impossible to put
|
||||||
// these bounds in the where clause of the trait implementation where they actually
|
// these bounds in the where clause of the trait implementation where they actually
|
||||||
// belong until that is resolved.
|
// belong until that is resolved.
|
||||||
@ -190,58 +176,25 @@ fn write_layout(crate_ident: &Ident, ast: &DeriveInput) -> Result<TokenStream> {
|
|||||||
fn assert_impl<T: ::#crate_ident::buffer::BufferContents + ?Sized>() {}
|
fn assert_impl<T: ::#crate_ident::buffer::BufferContents + ?Sized>() {}
|
||||||
assert_impl::<#ty>();
|
assert_impl::<#ty>();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let layout = quote! {
|
Ok(quote! {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl #impl_generics ::#crate_ident::buffer::BufferContents
|
||||||
|
for #struct_ident #type_generics #where_clause
|
||||||
{
|
{
|
||||||
#( #bounds )*
|
const LAYOUT: ::#crate_ident::buffer::BufferContentsLayout = #layout;
|
||||||
|
|
||||||
// HACK: Very depressingly, `Layout::extend` is not const.
|
#[inline(always)]
|
||||||
const fn extend_layout(
|
unsafe fn ptr_from_slice(slice: ::std::ptr::NonNull<[u8]>) -> *mut Self {
|
||||||
layout: ::std::alloc::Layout,
|
#ptr_from_slice
|
||||||
next: ::std::alloc::Layout,
|
|
||||||
) -> ::std::alloc::Layout {
|
|
||||||
let padded_size = if let Some(val) =
|
|
||||||
layout.size().checked_add(next.align() - 1)
|
|
||||||
{
|
|
||||||
val & !(next.align() - 1)
|
|
||||||
} else {
|
|
||||||
::std::unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Replace with `Ord::max` once its constness is stabilized.
|
|
||||||
let align = if layout.align() >= next.align() {
|
|
||||||
layout.align()
|
|
||||||
} else {
|
|
||||||
next.align()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(size) = padded_size.checked_add(next.size()) {
|
|
||||||
if let Ok(layout) = ::std::alloc::Layout::from_size_align(size, align) {
|
|
||||||
layout
|
|
||||||
} else {
|
|
||||||
::std::unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
::std::unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(layout) = #layout {
|
|
||||||
if let Some(layout) = layout.pad_to_alignment() {
|
|
||||||
layout
|
|
||||||
} else {
|
|
||||||
::std::unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
::std::panic!("zero-sized types are not valid buffer contents")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(layout)
|
#( #bounds )*
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: This works around an inherent limitation of bytemuck, namely that an array where the
|
// HACK: This works around an inherent limitation of bytemuck, namely that an array where the
|
||||||
|
@ -471,7 +471,7 @@ impl<T> Subbuffer<T> {
|
|||||||
impl<T> Subbuffer<[T]> {
|
impl<T> Subbuffer<[T]> {
|
||||||
/// Returns the number of elements in the slice.
|
/// Returns the number of elements in the slice.
|
||||||
pub fn len(&self) -> DeviceSize {
|
pub fn len(&self) -> DeviceSize {
|
||||||
debug_assert!(self.size % size_of::<T>() as DeviceSize == 0);
|
debug_assert_eq!(self.size % size_of::<T>() as DeviceSize, 0);
|
||||||
|
|
||||||
self.size / size_of::<T>() as DeviceSize
|
self.size / size_of::<T>() as DeviceSize
|
||||||
}
|
}
|
||||||
@ -830,16 +830,11 @@ unsafe impl<T> BufferContents for T
|
|||||||
where
|
where
|
||||||
T: AnyBitPattern + Send + Sync,
|
T: AnyBitPattern + Send + Sync,
|
||||||
{
|
{
|
||||||
const LAYOUT: BufferContentsLayout =
|
const LAYOUT: BufferContentsLayout = BufferContentsLayout::from_sized(Layout::new::<T>());
|
||||||
if let Some(layout) = BufferContentsLayout::from_sized(Layout::new::<T>()) {
|
|
||||||
layout
|
|
||||||
} else {
|
|
||||||
panic!("zero-sized types are not valid buffer contents");
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
||||||
debug_assert!(slice.len() == size_of::<T>());
|
debug_assert_eq!(slice.len(), size_of::<T>());
|
||||||
|
|
||||||
<*mut [u8]>::cast::<T>(slice.as_ptr())
|
<*mut [u8]>::cast::<T>(slice.as_ptr())
|
||||||
}
|
}
|
||||||
@ -849,16 +844,13 @@ unsafe impl<T> BufferContents for [T]
|
|||||||
where
|
where
|
||||||
T: BufferContents,
|
T: BufferContents,
|
||||||
{
|
{
|
||||||
const LAYOUT: BufferContentsLayout = BufferContentsLayout(BufferContentsLayoutInner::Unsized {
|
const LAYOUT: BufferContentsLayout = BufferContentsLayout::from_slice(Layout::new::<T>());
|
||||||
head_layout: None,
|
|
||||||
element_layout: T::LAYOUT.unwrap_sized(),
|
|
||||||
});
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
||||||
let data = <*mut [u8]>::cast::<T>(slice.as_ptr());
|
let data = <*mut [u8]>::cast::<T>(slice.as_ptr());
|
||||||
let len = slice.len() / size_of::<T>();
|
let len = slice.len() / size_of::<T>();
|
||||||
debug_assert!(slice.len() % size_of::<T>() == 0);
|
debug_assert_eq!(slice.len() % size_of::<T>(), 0);
|
||||||
|
|
||||||
ptr::slice_from_raw_parts_mut(data, len)
|
ptr::slice_from_raw_parts_mut(data, len)
|
||||||
}
|
}
|
||||||
@ -961,116 +953,112 @@ impl BufferContentsLayout {
|
|||||||
/// derive macro only.
|
/// derive macro only.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn from_sized(sized: Layout) -> Option<Self> {
|
pub const fn from_sized(sized: Layout) -> Self {
|
||||||
assert!(
|
assert!(
|
||||||
sized.align() <= 64,
|
sized.align() <= 64,
|
||||||
"types with alignments above 64 are not valid buffer contents",
|
"types with alignments above 64 are not valid buffer contents",
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Ok(sized) = DeviceLayout::from_layout(sized) {
|
if let Ok(sized) = DeviceLayout::from_layout(sized) {
|
||||||
Some(Self(BufferContentsLayoutInner::Sized(sized)))
|
Self(BufferContentsLayoutInner::Sized(sized))
|
||||||
} else {
|
} else {
|
||||||
None
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BufferContentsLayout` from a head and element layout. This is intended for
|
/// Creates a new `BufferContentsLayout` from an element layout. This is intended for use by
|
||||||
/// use by the derive macro only.
|
/// the derive macro only.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn from_head_element_layout(
|
pub const fn from_slice(element_layout: Layout) -> Self {
|
||||||
head_layout: Layout,
|
|
||||||
element_layout: Layout,
|
|
||||||
) -> Option<Self> {
|
|
||||||
if head_layout.align() > 64 || element_layout.align() > 64 {
|
|
||||||
panic!("types with alignments above 64 are not valid buffer contents");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The head of a `BufferContentsLayout` can be zero-sized.
|
|
||||||
// TODO: Replace with `Result::ok` once its constness is stabilized.
|
|
||||||
let head_layout = if let Ok(head_layout) = DeviceLayout::from_layout(head_layout) {
|
|
||||||
Some(head_layout)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(element_layout) = DeviceLayout::from_layout(element_layout) {
|
|
||||||
Some(Self(BufferContentsLayoutInner::Unsized {
|
|
||||||
head_layout,
|
|
||||||
element_layout,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extends the given `previous` [`Layout`] by `self`. This is intended for use by the derive
|
|
||||||
/// macro only.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline]
|
|
||||||
pub const fn extend_from_layout(self, previous: &Layout) -> Option<Self> {
|
|
||||||
assert!(
|
assert!(
|
||||||
previous.align() <= 64,
|
element_layout.align() <= 64,
|
||||||
"types with alignments above 64 are not valid buffer contents",
|
"types with alignments above 64 are not valid buffer contents",
|
||||||
);
|
);
|
||||||
|
|
||||||
match self.0 {
|
if let Ok(element_layout) = DeviceLayout::from_layout(element_layout) {
|
||||||
BufferContentsLayoutInner::Sized(sized) => {
|
Self(BufferContentsLayoutInner::Unsized {
|
||||||
let (sized, _) = try_opt!(sized.extend_from_layout(previous));
|
|
||||||
|
|
||||||
Some(Self(BufferContentsLayoutInner::Sized(sized)))
|
|
||||||
}
|
|
||||||
BufferContentsLayoutInner::Unsized {
|
|
||||||
head_layout: None,
|
head_layout: None,
|
||||||
element_layout,
|
element_layout,
|
||||||
} => {
|
})
|
||||||
// The head of a `BufferContentsLayout` can be zero-sized.
|
} else {
|
||||||
// TODO: Replace with `Result::ok` once its constness is stabilized.
|
unreachable!()
|
||||||
let head_layout = if let Ok(head_layout) = DeviceLayout::from_layout(*previous) {
|
}
|
||||||
Some(head_layout)
|
}
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(Self(BufferContentsLayoutInner::Unsized {
|
/// Creates a new `BufferContentsLayout` from the given field layouts. This is intended for use
|
||||||
head_layout,
|
/// by the derive macro only.
|
||||||
element_layout,
|
#[doc(hidden)]
|
||||||
}))
|
#[inline]
|
||||||
}
|
pub const fn from_field_layouts(field_layouts: &[Layout], last_field_layout: Self) -> Self {
|
||||||
BufferContentsLayoutInner::Unsized {
|
const fn extend(previous: DeviceLayout, next: DeviceLayout) -> DeviceLayout {
|
||||||
head_layout: Some(head_layout),
|
match previous.extend(next) {
|
||||||
element_layout,
|
Some((layout, _)) => layout,
|
||||||
} => {
|
None => unreachable!(),
|
||||||
let (head_layout, _) = try_opt!(head_layout.extend_from_layout(previous));
|
|
||||||
|
|
||||||
Some(Self(BufferContentsLayoutInner::Unsized {
|
|
||||||
head_layout: Some(head_layout),
|
|
||||||
element_layout,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut head_layout = None;
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < field_layouts.len() {
|
||||||
|
head_layout = match DeviceLayout::from_layout(field_layouts[i]) {
|
||||||
|
Ok(field_layout) => Some(match head_layout {
|
||||||
|
Some(layout) => extend(layout, field_layout),
|
||||||
|
None => field_layout,
|
||||||
|
}),
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = Self(match last_field_layout.0 {
|
||||||
|
BufferContentsLayoutInner::Sized(field_layout) => {
|
||||||
|
BufferContentsLayoutInner::Sized(match head_layout {
|
||||||
|
Some(layout) => extend(layout, field_layout),
|
||||||
|
None => field_layout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
BufferContentsLayoutInner::Unsized {
|
||||||
|
head_layout: field_head_layout,
|
||||||
|
element_layout,
|
||||||
|
} => BufferContentsLayoutInner::Unsized {
|
||||||
|
head_layout: match (head_layout, field_head_layout) {
|
||||||
|
(Some(layout), Some(field_layout)) => Some(extend(layout, field_layout)),
|
||||||
|
(Some(layout), None) => Some(layout),
|
||||||
|
(None, Some(field_layout)) => Some(field_layout),
|
||||||
|
(None, None) => None,
|
||||||
|
},
|
||||||
|
element_layout,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
layout.alignment().as_devicesize() <= 64,
|
||||||
|
"types with alignments above 64 are not valid buffer contents",
|
||||||
|
);
|
||||||
|
|
||||||
|
layout.pad_to_alignment()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BufferContentsLayout` by rounding up the size of the head to the nearest
|
/// Creates a new `BufferContentsLayout` by rounding up the size of the head to the nearest
|
||||||
/// multiple of its alignment if the layout is sized, or by rounding up the size of the head to
|
/// multiple of its alignment if the layout is sized, or by rounding up the size of the head to
|
||||||
/// the nearest multiple of the alignment of the element type and aligning the head to the
|
/// the nearest multiple of the alignment of the element type and aligning the head to the
|
||||||
/// alignment of the element type if there is a sized part. Doesn't do anything if there is no
|
/// alignment of the element type if there is a sized part. Doesn't do anything if there is no
|
||||||
/// sized part. Returns [`None`] if the new head size would exceed [`DeviceLayout::MAX_SIZE`].
|
/// sized part.
|
||||||
/// This is intended for use by the derive macro only.
|
const fn pad_to_alignment(&self) -> Self {
|
||||||
#[doc(hidden)]
|
Self(match &self.0 {
|
||||||
#[inline]
|
BufferContentsLayoutInner::Sized(sized) => {
|
||||||
pub const fn pad_to_alignment(&self) -> Option<Self> {
|
BufferContentsLayoutInner::Sized(sized.pad_to_alignment())
|
||||||
match &self.0 {
|
}
|
||||||
BufferContentsLayoutInner::Sized(sized) => Some(Self(
|
|
||||||
BufferContentsLayoutInner::Sized(sized.pad_to_alignment()),
|
|
||||||
)),
|
|
||||||
BufferContentsLayoutInner::Unsized {
|
BufferContentsLayoutInner::Unsized {
|
||||||
head_layout: None,
|
head_layout: None,
|
||||||
element_layout,
|
element_layout,
|
||||||
} => Some(Self(BufferContentsLayoutInner::Unsized {
|
} => BufferContentsLayoutInner::Unsized {
|
||||||
head_layout: None,
|
head_layout: None,
|
||||||
element_layout: *element_layout,
|
element_layout: *element_layout,
|
||||||
})),
|
},
|
||||||
BufferContentsLayoutInner::Unsized {
|
BufferContentsLayoutInner::Unsized {
|
||||||
head_layout: Some(head_layout),
|
head_layout: Some(head_layout),
|
||||||
element_layout,
|
element_layout,
|
||||||
@ -1109,15 +1097,15 @@ impl BufferContentsLayout {
|
|||||||
DeviceAlignment::max(head_layout.alignment(), element_layout.alignment());
|
DeviceAlignment::max(head_layout.alignment(), element_layout.alignment());
|
||||||
|
|
||||||
if let Some(head_layout) = DeviceLayout::new(padded_head_size, alignment) {
|
if let Some(head_layout) = DeviceLayout::new(padded_head_size, alignment) {
|
||||||
Some(Self(BufferContentsLayoutInner::Unsized {
|
BufferContentsLayoutInner::Unsized {
|
||||||
head_layout: Some(head_layout),
|
head_layout: Some(head_layout),
|
||||||
element_layout: *element_layout,
|
element_layout: *element_layout,
|
||||||
}))
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sized(&self) -> bool {
|
fn is_sized(&self) -> bool {
|
||||||
@ -1169,7 +1157,7 @@ mod tests {
|
|||||||
|
|
||||||
#[derive(BufferContents)]
|
#[derive(BufferContents)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Composite1(Test1, [f32; 10], Test1);
|
struct Composite1(Test1, [f32; 9], Test1);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Composite1::LAYOUT.head_size() as usize,
|
Composite1::LAYOUT.head_size() as usize,
|
||||||
@ -1200,7 +1188,7 @@ mod tests {
|
|||||||
|
|
||||||
#[derive(BufferContents)]
|
#[derive(BufferContents)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Composite2(Test1, [f32; 10], Test2);
|
struct Composite2(Test1, [f32; 9], Test2);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Composite2::LAYOUT.head_size() as usize,
|
Composite2::LAYOUT.head_size() as usize,
|
||||||
|
@ -287,15 +287,10 @@ unsafe impl<T, const N: usize> BufferContents for Padded<T, N>
|
|||||||
where
|
where
|
||||||
T: BufferContents,
|
T: BufferContents,
|
||||||
{
|
{
|
||||||
const LAYOUT: BufferContentsLayout =
|
const LAYOUT: BufferContentsLayout = BufferContentsLayout::from_sized(Layout::new::<Self>());
|
||||||
if let Some(layout) = BufferContentsLayout::from_sized(Layout::new::<Self>()) {
|
|
||||||
layout
|
|
||||||
} else {
|
|
||||||
panic!("zero-sized types are not valid buffer contents");
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
unsafe fn ptr_from_slice(slice: NonNull<[u8]>) -> *mut Self {
|
||||||
debug_assert!(slice.len() == size_of::<Padded<T, N>>());
|
debug_assert_eq!(slice.len(), size_of::<Padded<T, N>>());
|
||||||
|
|
||||||
<*mut [u8]>::cast::<Padded<T, N>>(slice.as_ptr())
|
<*mut [u8]>::cast::<Padded<T, N>>(slice.as_ptr())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user