vulkan_rust/bytecode.rs
1//! Byte-level helpers for Vulkan data: SPIR-V alignment and raw struct access.
2
3use std::fmt;
4
5/// SPIR-V instructions are 32-bit words, so both length and alignment
6/// must be multiples of this value.
7const SPIRV_WORD_SIZE: usize = 4;
8
9/// Error returned when SPIR-V bytecode has invalid alignment or size.
10///
11/// # Examples
12///
13/// ```
14/// use vulkan_rust::BytecodeError;
15///
16/// let err = BytecodeError::InvalidLength(7);
17/// assert_eq!(err.to_string(), "SPIR-V byte length 7 is not a multiple of 4");
18///
19/// let err = BytecodeError::MisalignedPointer;
20/// assert!(err.to_string().contains("not 4-byte aligned"));
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum BytecodeError {
24 /// Length is not a multiple of 4.
25 InvalidLength(usize),
26 /// Pointer is not aligned to 4 bytes.
27 MisalignedPointer,
28}
29
30impl fmt::Display for BytecodeError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::InvalidLength(len) => {
34 write!(f, "SPIR-V byte length {len} is not a multiple of 4")
35 }
36 Self::MisalignedPointer => {
37 write!(f, "SPIR-V byte slice pointer is not 4-byte aligned")
38 }
39 }
40 }
41}
42
43impl std::error::Error for BytecodeError {}
44
45/// Reinterprets a reference to a `Copy` type as a byte slice.
46///
47/// This is useful for passing `#[repr(C)]` structs to Vulkan commands
48/// that accept `&[u8]`, such as `cmd_push_constants`.
49///
50/// # Safety
51///
52/// `T` must not contain padding bytes. Reading uninitialized padding is
53/// undefined behavior. Use `#[repr(C)]` structs whose fields leave no
54/// gaps, or verify with `assert_eq!(size_of::<T>(), /* expected */)`.
55///
56/// # Examples
57///
58/// ```
59/// use vulkan_rust::as_bytes;
60///
61/// #[repr(C)]
62/// #[derive(Clone, Copy)]
63/// struct PushData { time: f32, scale: f32 }
64///
65/// let data = PushData { time: 1.0, scale: 2.0 };
66/// let bytes = unsafe { as_bytes(&data) };
67/// assert_eq!(bytes.len(), 8);
68/// ```
69pub unsafe fn as_bytes<T: Copy>(value: &T) -> &[u8] {
70 // SAFETY: caller guarantees T has no padding. Casting to u8 is always
71 // aligned, and the slice covers exactly size_of::<T>() bytes.
72 unsafe { std::slice::from_raw_parts(value as *const T as *const u8, std::mem::size_of::<T>()) }
73}
74
75/// Cast a byte slice to a `u32` slice for `ShaderModuleCreateInfo`.
76///
77/// Returns an error if `bytes.len()` is not a multiple of 4 or the
78/// pointer is not 4-byte aligned. Use `include_bytes!` or aligned
79/// allocators to ensure correctness.
80///
81/// # Examples
82///
83/// ```
84/// use vulkan_rust::cast_to_u32;
85///
86/// #[repr(align(4))]
87/// struct Aligned([u8; 8]);
88/// let spirv = Aligned([0x03, 0x02, 0x23, 0x07, 0, 0, 0, 0]);
89/// let words = cast_to_u32(&spirv.0).expect("alignment error");
90/// assert_eq!(words.len(), 2);
91/// ```
92pub fn cast_to_u32(bytes: &[u8]) -> Result<&[u32], BytecodeError> {
93 if bytes.is_empty() {
94 return Ok(&[]);
95 }
96 if bytes.len() % SPIRV_WORD_SIZE != 0 {
97 return Err(BytecodeError::InvalidLength(bytes.len()));
98 }
99 if (bytes.as_ptr() as usize) % SPIRV_WORD_SIZE != 0 {
100 return Err(BytecodeError::MisalignedPointer);
101 }
102 // SAFETY: length and alignment checked above, pointer is valid, aligned to u32, and in-bounds.
103 Ok(unsafe {
104 std::slice::from_raw_parts(bytes.as_ptr() as *const u32, bytes.len() / SPIRV_WORD_SIZE)
105 })
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn misaligned_pointer_display() {
114 let err = BytecodeError::MisalignedPointer;
115 assert_eq!(
116 err.to_string(),
117 "SPIR-V byte slice pointer is not 4-byte aligned"
118 );
119 }
120
121 #[test]
122 fn invalid_length_display() {
123 let err = BytecodeError::InvalidLength(7);
124 assert_eq!(
125 err.to_string(),
126 "SPIR-V byte length 7 is not a multiple of 4"
127 );
128 }
129
130 #[test]
131 fn misaligned_pointer_returns_error() {
132 // Create a 4-byte-aligned buffer, then take a subslice at offset 1
133 // to guarantee misalignment.
134 #[repr(align(4))]
135 struct Aligned([u8; 8]);
136 let data = Aligned([0; 8]);
137 let misaligned = &data.0[1..5];
138 assert_eq!(
139 cast_to_u32(misaligned),
140 Err(BytecodeError::MisalignedPointer)
141 );
142 }
143
144 #[test]
145 fn bytecode_error_is_std_error() {
146 let err: &dyn std::error::Error = &BytecodeError::InvalidLength(3);
147 // source() should return None (no underlying cause).
148 assert!(err.source().is_none());
149 }
150}