From 02c7a8359282fa8d947fb3258e07f899bf732d14 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 29 Nov 2023 00:37:21 +0100 Subject: [PATCH] Provide `CStr` getters and setters for `c_char` pointers and arrays (#831) It is a common operation to read and write NUL-terminated C string in the Vulkan API, yet the only helpers for that were thus far open-coded in the `Debug` printing implementations. Move them to separate functions that are exposed to the user, in hopes of helping them no longer misunderstand NUL-terminated strings (see e.g. #830). Important to note is that the array-copy for a static-sized `c_char` array has also been replaced with a `CStr` wrapper: this forces the user and our implementation to have a NUL-terminator at the end of the string, and the setter returns `Err()` when the given `CStr (with NUL-terminator) is too large for the static-sized array it has to be written to. --- Changelog.md | 4 +- ash/src/vk/definitions.rs | 468 +++++++++++++++++++++++++------------- ash/src/vk/prelude.rs | 43 ++++ generator/src/lib.rs | 149 ++++++------ 4 files changed, 436 insertions(+), 228 deletions(-) diff --git a/Changelog.md b/Changelog.md index 13d9cd8..99b391b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `VK_EXT_hdr_metadata` device extension (#804) - Added `VK_NV_cuda_kernel_launch` device extension (#805) - Added `descriptor_count()` setter on `ash::vk::WriteDescriptorSet` (#809) +- Added `*_as_c_str()` getters for `c_char` pointers and `c_char` arrays (#831) ### Changed @@ -48,7 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `VK_KHR_device_group_creation`: Take borrow of `Entry` in `fn new()` (#753) - `VK_KHR_device_group_creation`: Rename `vk::Instance`-returning function from `device()` to `instance()` (#759) - Windows `HANDLE` types (`HWND`, `HINSTANCE`, `HMONITOR`) are now defined as `isize` instead of `*const c_void` (#797) -- extensions/ext/ray_tracing_pipeline: Pass indirect SBT regions as single item reference. (#829) +- extensions/ext/ray_tracing_pipeline: Pass indirect SBT regions as single item reference (#829) +- Replaced `c_char` array setters with `CStr` setters (#831) ### Removed diff --git a/ash/src/vk/definitions.rs b/ash/src/vk/definitions.rs index a3b4de4..c0b537d 100644 --- a/ash/src/vk/definitions.rs +++ b/ash/src/vk/definitions.rs @@ -817,9 +817,7 @@ impl fmt::Debug for PhysicalDeviceProperties { .field("vendor_id", &self.vendor_id) .field("device_id", &self.device_id) .field("device_type", &self.device_type) - .field("device_name", &unsafe { - ::std::ffi::CStr::from_ptr(self.device_name.as_ptr()) - }) + .field("device_name", &unsafe { self.device_name_as_c_str() }) .field("pipeline_cache_uuid", &self.pipeline_cache_uuid) .field("limits", &self.limits) .field("sparse_properties", &self.sparse_properties) @@ -869,9 +867,15 @@ impl PhysicalDeviceProperties { self } #[inline] - pub fn device_name(mut self, device_name: [c_char; MAX_PHYSICAL_DEVICE_NAME_SIZE]) -> Self { - self.device_name = device_name; - self + pub fn device_name( + mut self, + device_name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.device_name, device_name).map(|()| self) + } + #[inline] + pub unsafe fn device_name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.device_name) } #[inline] pub fn pipeline_cache_uuid(mut self, pipeline_cache_uuid: [u8; UUID_SIZE]) -> Self { @@ -900,9 +904,7 @@ pub struct ExtensionProperties { impl fmt::Debug for ExtensionProperties { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("ExtensionProperties") - .field("extension_name", &unsafe { - ::std::ffi::CStr::from_ptr(self.extension_name.as_ptr()) - }) + .field("extension_name", &unsafe { self.extension_name_as_c_str() }) .field("spec_version", &self.spec_version) .finish() } @@ -918,9 +920,15 @@ impl ::std::default::Default for ExtensionProperties { } impl ExtensionProperties { #[inline] - pub fn extension_name(mut self, extension_name: [c_char; MAX_EXTENSION_NAME_SIZE]) -> Self { - self.extension_name = extension_name; - self + pub fn extension_name( + mut self, + extension_name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.extension_name, extension_name).map(|()| self) + } + #[inline] + pub unsafe fn extension_name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.extension_name) } #[inline] pub fn spec_version(mut self, spec_version: u32) -> Self { @@ -941,14 +949,10 @@ pub struct LayerProperties { impl fmt::Debug for LayerProperties { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("LayerProperties") - .field("layer_name", &unsafe { - ::std::ffi::CStr::from_ptr(self.layer_name.as_ptr()) - }) + .field("layer_name", &unsafe { self.layer_name_as_c_str() }) .field("spec_version", &self.spec_version) .field("implementation_version", &self.implementation_version) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("description", &unsafe { self.description_as_c_str() }) .finish() } } @@ -965,9 +969,15 @@ impl ::std::default::Default for LayerProperties { } impl LayerProperties { #[inline] - pub fn layer_name(mut self, layer_name: [c_char; MAX_EXTENSION_NAME_SIZE]) -> Self { - self.layer_name = layer_name; - self + pub fn layer_name( + mut self, + layer_name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.layer_name, layer_name).map(|()| self) + } + #[inline] + pub unsafe fn layer_name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.layer_name) } #[inline] pub fn spec_version(mut self, spec_version: u32) -> Self { @@ -980,9 +990,15 @@ impl LayerProperties { self } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } } #[repr(C)] @@ -1019,21 +1035,29 @@ unsafe impl<'a> TaggedStructure for ApplicationInfo<'a> { } impl<'a> ApplicationInfo<'a> { #[inline] - pub fn application_name(mut self, application_name: &'a ::std::ffi::CStr) -> Self { + pub fn application_name(mut self, application_name: &'a std::ffi::CStr) -> Self { self.p_application_name = application_name.as_ptr(); self } #[inline] + pub unsafe fn application_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_application_name) + } + #[inline] pub fn application_version(mut self, application_version: u32) -> Self { self.application_version = application_version; self } #[inline] - pub fn engine_name(mut self, engine_name: &'a ::std::ffi::CStr) -> Self { + pub fn engine_name(mut self, engine_name: &'a std::ffi::CStr) -> Self { self.p_engine_name = engine_name.as_ptr(); self } #[inline] + pub unsafe fn engine_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_engine_name) + } + #[inline] pub fn engine_version(mut self, engine_version: u32) -> Self { self.engine_version = engine_version; self @@ -3651,11 +3675,15 @@ impl<'a> PipelineShaderStageCreateInfo<'a> { self } #[inline] - pub fn name(mut self, name: &'a ::std::ffi::CStr) -> Self { + pub fn name(mut self, name: &'a std::ffi::CStr) -> Self { self.p_name = name.as_ptr(); self } #[inline] + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_name) + } + #[inline] pub fn specialization_info(mut self, specialization_info: &'a SpecializationInfo<'a>) -> Self { self.p_specialization_info = specialization_info; self @@ -7712,11 +7740,15 @@ impl<'a> DisplayPropertiesKHR<'a> { self } #[inline] - pub fn display_name(mut self, display_name: &'a ::std::ffi::CStr) -> Self { + pub fn display_name(mut self, display_name: &'a std::ffi::CStr) -> Self { self.display_name = display_name.as_ptr(); self } #[inline] + pub unsafe fn display_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.display_name) + } + #[inline] pub fn physical_dimensions(mut self, physical_dimensions: Extent2D) -> Self { self.physical_dimensions = physical_dimensions; self @@ -8985,10 +9017,14 @@ impl<'a> DebugMarkerObjectNameInfoEXT<'a> { self } #[inline] - pub fn object_name(mut self, object_name: &'a ::std::ffi::CStr) -> Self { + pub fn object_name(mut self, object_name: &'a std::ffi::CStr) -> Self { self.p_object_name = object_name.as_ptr(); self } + #[inline] + pub unsafe fn object_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_object_name) + } } #[repr(C)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -9073,11 +9109,15 @@ unsafe impl<'a> TaggedStructure for DebugMarkerMarkerInfoEXT<'a> { } impl<'a> DebugMarkerMarkerInfoEXT<'a> { #[inline] - pub fn marker_name(mut self, marker_name: &'a ::std::ffi::CStr) -> Self { + pub fn marker_name(mut self, marker_name: &'a std::ffi::CStr) -> Self { self.p_marker_name = marker_name.as_ptr(); self } #[inline] + pub unsafe fn marker_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_marker_name) + } + #[inline] pub fn color(mut self, color: [f32; 4]) -> Self { self.color = color; self @@ -10908,12 +10948,8 @@ impl fmt::Debug for PhysicalDeviceDriverProperties<'_> { .field("s_type", &self.s_type) .field("p_next", &self.p_next) .field("driver_id", &self.driver_id) - .field("driver_name", &unsafe { - ::std::ffi::CStr::from_ptr(self.driver_name.as_ptr()) - }) - .field("driver_info", &unsafe { - ::std::ffi::CStr::from_ptr(self.driver_info.as_ptr()) - }) + .field("driver_name", &unsafe { self.driver_name_as_c_str() }) + .field("driver_info", &unsafe { self.driver_info_as_c_str() }) .field("conformance_version", &self.conformance_version) .finish() } @@ -10943,14 +10979,26 @@ impl<'a> PhysicalDeviceDriverProperties<'a> { self } #[inline] - pub fn driver_name(mut self, driver_name: [c_char; MAX_DRIVER_NAME_SIZE]) -> Self { - self.driver_name = driver_name; - self + pub fn driver_name( + mut self, + driver_name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.driver_name, driver_name).map(|()| self) } #[inline] - pub fn driver_info(mut self, driver_info: [c_char; MAX_DRIVER_INFO_SIZE]) -> Self { - self.driver_info = driver_info; - self + pub unsafe fn driver_name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.driver_name) + } + #[inline] + pub fn driver_info( + mut self, + driver_info: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.driver_info, driver_info).map(|()| self) + } + #[inline] + pub unsafe fn driver_info_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.driver_info) } #[inline] pub fn conformance_version(mut self, conformance_version: ConformanceVersion) -> Self { @@ -18302,10 +18350,14 @@ impl<'a> DebugUtilsObjectNameInfoEXT<'a> { self } #[inline] - pub fn object_name(mut self, object_name: &'a ::std::ffi::CStr) -> Self { + pub fn object_name(mut self, object_name: &'a std::ffi::CStr) -> Self { self.p_object_name = object_name.as_ptr(); self } + #[inline] + pub unsafe fn object_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_object_name) + } } #[repr(C)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -18386,11 +18438,15 @@ unsafe impl<'a> TaggedStructure for DebugUtilsLabelEXT<'a> { } impl<'a> DebugUtilsLabelEXT<'a> { #[inline] - pub fn label_name(mut self, label_name: &'a ::std::ffi::CStr) -> Self { + pub fn label_name(mut self, label_name: &'a std::ffi::CStr) -> Self { self.p_label_name = label_name.as_ptr(); self } #[inline] + pub unsafe fn label_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_label_name) + } + #[inline] pub fn color(mut self, color: [f32; 4]) -> Self { self.color = color; self @@ -18525,21 +18581,29 @@ impl<'a> DebugUtilsMessengerCallbackDataEXT<'a> { self } #[inline] - pub fn message_id_name(mut self, message_id_name: &'a ::std::ffi::CStr) -> Self { + pub fn message_id_name(mut self, message_id_name: &'a std::ffi::CStr) -> Self { self.p_message_id_name = message_id_name.as_ptr(); self } #[inline] + pub unsafe fn message_id_name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_message_id_name) + } + #[inline] pub fn message_id_number(mut self, message_id_number: i32) -> Self { self.message_id_number = message_id_number; self } #[inline] - pub fn message(mut self, message: &'a ::std::ffi::CStr) -> Self { + pub fn message(mut self, message: &'a std::ffi::CStr) -> Self { self.p_message = message.as_ptr(); self } #[inline] + pub unsafe fn message_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_message) + } + #[inline] pub fn queue_labels(mut self, queue_labels: &'a [DebugUtilsLabelEXT<'a>]) -> Self { self.queue_label_count = queue_labels.len() as _; self.p_queue_labels = queue_labels.as_ptr(); @@ -27064,15 +27128,9 @@ impl fmt::Debug for PerformanceCounterDescriptionKHR<'_> { .field("s_type", &self.s_type) .field("p_next", &self.p_next) .field("flags", &self.flags) - .field("name", &unsafe { - ::std::ffi::CStr::from_ptr(self.name.as_ptr()) - }) - .field("category", &unsafe { - ::std::ffi::CStr::from_ptr(self.category.as_ptr()) - }) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("name", &unsafe { self.name_as_c_str() }) + .field("category", &unsafe { self.category_as_c_str() }) + .field("description", &unsafe { self.description_as_c_str() }) .finish() } } @@ -27100,19 +27158,37 @@ impl<'a> PerformanceCounterDescriptionKHR<'a> { self } #[inline] - pub fn name(mut self, name: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.name = name; - self + pub fn name( + mut self, + name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.name, name).map(|()| self) } #[inline] - pub fn category(mut self, category: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.category = category; - self + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.name) } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn category( + mut self, + category: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.category, category).map(|()| self) + } + #[inline] + pub unsafe fn category_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.category) + } + #[inline] + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } } #[repr(C)] @@ -28151,12 +28227,8 @@ impl fmt::Debug for PipelineExecutablePropertiesKHR<'_> { .field("s_type", &self.s_type) .field("p_next", &self.p_next) .field("stages", &self.stages) - .field("name", &unsafe { - ::std::ffi::CStr::from_ptr(self.name.as_ptr()) - }) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("name", &unsafe { self.name_as_c_str() }) + .field("description", &unsafe { self.description_as_c_str() }) .field("subgroup_size", &self.subgroup_size) .finish() } @@ -28185,14 +28257,26 @@ impl<'a> PipelineExecutablePropertiesKHR<'a> { self } #[inline] - pub fn name(mut self, name: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.name = name; - self + pub fn name( + mut self, + name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.name, name).map(|()| self) } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.name) + } + #[inline] + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn subgroup_size(mut self, subgroup_size: u32) -> Self { @@ -28271,12 +28355,8 @@ impl fmt::Debug for PipelineExecutableStatisticKHR<'_> { fmt.debug_struct("PipelineExecutableStatisticKHR") .field("s_type", &self.s_type) .field("p_next", &self.p_next) - .field("name", &unsafe { - ::std::ffi::CStr::from_ptr(self.name.as_ptr()) - }) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("name", &unsafe { self.name_as_c_str() }) + .field("description", &unsafe { self.description_as_c_str() }) .field("format", &self.format) .field("value", &"union") .finish() @@ -28301,14 +28381,26 @@ unsafe impl<'a> TaggedStructure for PipelineExecutableStatisticKHR<'a> { } impl<'a> PipelineExecutableStatisticKHR<'a> { #[inline] - pub fn name(mut self, name: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.name = name; - self + pub fn name( + mut self, + name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.name, name).map(|()| self) } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.name) + } + #[inline] + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn format(mut self, format: PipelineExecutableStatisticFormatKHR) -> Self { @@ -28340,12 +28432,8 @@ impl fmt::Debug for PipelineExecutableInternalRepresentationKHR<'_> { fmt.debug_struct("PipelineExecutableInternalRepresentationKHR") .field("s_type", &self.s_type) .field("p_next", &self.p_next) - .field("name", &unsafe { - ::std::ffi::CStr::from_ptr(self.name.as_ptr()) - }) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("name", &unsafe { self.name_as_c_str() }) + .field("description", &unsafe { self.description_as_c_str() }) .field("is_text", &self.is_text) .field("data_size", &self.data_size) .field("p_data", &self.p_data) @@ -28373,14 +28461,26 @@ unsafe impl<'a> TaggedStructure for PipelineExecutableInternalRepresentationKHR< } impl<'a> PipelineExecutableInternalRepresentationKHR<'a> { #[inline] - pub fn name(mut self, name: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.name = name; - self + pub fn name( + mut self, + name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.name, name).map(|()| self) } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.name) + } + #[inline] + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn is_text(mut self, is_text: bool) -> Self { @@ -29848,12 +29948,8 @@ impl fmt::Debug for PhysicalDeviceVulkan12Properties<'_> { .field("s_type", &self.s_type) .field("p_next", &self.p_next) .field("driver_id", &self.driver_id) - .field("driver_name", &unsafe { - ::std::ffi::CStr::from_ptr(self.driver_name.as_ptr()) - }) - .field("driver_info", &unsafe { - ::std::ffi::CStr::from_ptr(self.driver_info.as_ptr()) - }) + .field("driver_name", &unsafe { self.driver_name_as_c_str() }) + .field("driver_info", &unsafe { self.driver_info_as_c_str() }) .field("conformance_version", &self.conformance_version) .field( "denorm_behavior_independence", @@ -30117,14 +30213,26 @@ impl<'a> PhysicalDeviceVulkan12Properties<'a> { self } #[inline] - pub fn driver_name(mut self, driver_name: [c_char; MAX_DRIVER_NAME_SIZE]) -> Self { - self.driver_name = driver_name; - self + pub fn driver_name( + mut self, + driver_name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.driver_name, driver_name).map(|()| self) } #[inline] - pub fn driver_info(mut self, driver_info: [c_char; MAX_DRIVER_INFO_SIZE]) -> Self { - self.driver_info = driver_info; - self + pub unsafe fn driver_name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.driver_name) + } + #[inline] + pub fn driver_info( + mut self, + driver_info: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.driver_info, driver_info).map(|()| self) + } + #[inline] + pub unsafe fn driver_info_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.driver_info) } #[inline] pub fn conformance_version(mut self, conformance_version: ConformanceVersion) -> Self { @@ -31204,19 +31312,11 @@ impl fmt::Debug for PhysicalDeviceToolProperties<'_> { fmt.debug_struct("PhysicalDeviceToolProperties") .field("s_type", &self.s_type) .field("p_next", &self.p_next) - .field("name", &unsafe { - ::std::ffi::CStr::from_ptr(self.name.as_ptr()) - }) - .field("version", &unsafe { - ::std::ffi::CStr::from_ptr(self.version.as_ptr()) - }) + .field("name", &unsafe { self.name_as_c_str() }) + .field("version", &unsafe { self.version_as_c_str() }) .field("purposes", &self.purposes) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) - .field("layer", &unsafe { - ::std::ffi::CStr::from_ptr(self.layer.as_ptr()) - }) + .field("description", &unsafe { self.description_as_c_str() }) + .field("layer", &unsafe { self.layer_as_c_str() }) .finish() } } @@ -31240,14 +31340,26 @@ unsafe impl<'a> TaggedStructure for PhysicalDeviceToolProperties<'a> { } impl<'a> PhysicalDeviceToolProperties<'a> { #[inline] - pub fn name(mut self, name: [c_char; MAX_EXTENSION_NAME_SIZE]) -> Self { - self.name = name; - self + pub fn name( + mut self, + name: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.name, name).map(|()| self) } #[inline] - pub fn version(mut self, version: [c_char; MAX_EXTENSION_NAME_SIZE]) -> Self { - self.version = version; - self + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.name) + } + #[inline] + pub fn version( + mut self, + version: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.version, version).map(|()| self) + } + #[inline] + pub unsafe fn version_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.version) } #[inline] pub fn purposes(mut self, purposes: ToolPurposeFlags) -> Self { @@ -31255,14 +31367,26 @@ impl<'a> PhysicalDeviceToolProperties<'a> { self } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) } #[inline] - pub fn layer(mut self, layer: [c_char; MAX_EXTENSION_NAME_SIZE]) -> Self { - self.layer = layer; - self + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) + } + #[inline] + pub fn layer( + mut self, + layer: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.layer, layer).map(|()| self) + } + #[inline] + pub unsafe fn layer_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.layer) } } #[repr(C)] @@ -41397,10 +41521,14 @@ impl<'a> CuFunctionCreateInfoNVX<'a> { self } #[inline] - pub fn name(mut self, name: &'a ::std::ffi::CStr) -> Self { + pub fn name(mut self, name: &'a std::ffi::CStr) -> Self { self.p_name = name.as_ptr(); self } + #[inline] + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_name) + } } #[repr(C)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -43812,10 +43940,14 @@ impl<'a> CudaFunctionCreateInfoNV<'a> { self } #[inline] - pub fn name(mut self, name: &'a ::std::ffi::CStr) -> Self { + pub fn name(mut self, name: &'a std::ffi::CStr) -> Self { self.p_name = name.as_ptr(); self } + #[inline] + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_name) + } } #[repr(C)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -45685,9 +45817,7 @@ impl fmt::Debug for RenderPassSubpassFeedbackInfoEXT { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("RenderPassSubpassFeedbackInfoEXT") .field("subpass_merge_status", &self.subpass_merge_status) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("description", &unsafe { self.description_as_c_str() }) .field("post_merge_index", &self.post_merge_index) .finish() } @@ -45709,9 +45839,15 @@ impl RenderPassSubpassFeedbackInfoEXT { self } #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn post_merge_index(mut self, post_merge_index: u32) -> Self { @@ -48456,9 +48592,7 @@ pub struct DeviceFaultVendorInfoEXT { impl fmt::Debug for DeviceFaultVendorInfoEXT { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("DeviceFaultVendorInfoEXT") - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("description", &unsafe { self.description_as_c_str() }) .field("vendor_fault_code", &self.vendor_fault_code) .field("vendor_fault_data", &self.vendor_fault_data) .finish() @@ -48476,9 +48610,15 @@ impl ::std::default::Default for DeviceFaultVendorInfoEXT { } impl DeviceFaultVendorInfoEXT { #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn vendor_fault_code(mut self, vendor_fault_code: u64) -> Self { @@ -48554,9 +48694,7 @@ impl fmt::Debug for DeviceFaultInfoEXT<'_> { fmt.debug_struct("DeviceFaultInfoEXT") .field("s_type", &self.s_type) .field("p_next", &self.p_next) - .field("description", &unsafe { - ::std::ffi::CStr::from_ptr(self.description.as_ptr()) - }) + .field("description", &unsafe { self.description_as_c_str() }) .field("p_address_infos", &self.p_address_infos) .field("p_vendor_infos", &self.p_vendor_infos) .field("p_vendor_binary_data", &self.p_vendor_binary_data) @@ -48582,9 +48720,15 @@ unsafe impl<'a> TaggedStructure for DeviceFaultInfoEXT<'a> { } impl<'a> DeviceFaultInfoEXT<'a> { #[inline] - pub fn description(mut self, description: [c_char; MAX_DESCRIPTION_SIZE]) -> Self { - self.description = description; - self + pub fn description( + mut self, + description: &std::ffi::CStr, + ) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.description, description).map(|()| self) + } + #[inline] + pub unsafe fn description_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.description) } #[inline] pub fn address_infos(mut self, address_infos: &'a mut DeviceFaultAddressInfoEXT) -> Self { @@ -50311,11 +50455,15 @@ impl<'a> ShaderCreateInfoEXT<'a> { self } #[inline] - pub fn name(mut self, name: &'a ::std::ffi::CStr) -> Self { + pub fn name(mut self, name: &'a std::ffi::CStr) -> Self { self.p_name = name.as_ptr(); self } #[inline] + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_name) + } + #[inline] pub fn set_layouts(mut self, set_layouts: &'a [DescriptorSetLayout]) -> Self { self.set_layout_count = set_layouts.len() as _; self.p_set_layouts = set_layouts.as_ptr(); @@ -51105,11 +51253,15 @@ unsafe impl<'a> TaggedStructure for PipelineShaderStageNodeCreateInfoAMDX<'a> { unsafe impl ExtendsPipelineShaderStageCreateInfo for PipelineShaderStageNodeCreateInfoAMDX<'_> {} impl<'a> PipelineShaderStageNodeCreateInfoAMDX<'a> { #[inline] - pub fn name(mut self, name: &'a ::std::ffi::CStr) -> Self { + pub fn name(mut self, name: &'a std::ffi::CStr) -> Self { self.p_name = name.as_ptr(); self } #[inline] + pub unsafe fn name_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.p_name) + } + #[inline] pub fn index(mut self, index: u32) -> Self { self.index = index; self diff --git a/ash/src/vk/prelude.rs b/ash/src/vk/prelude.rs index cb2f177..c91892f 100644 --- a/ash/src/vk/prelude.rs +++ b/ash/src/vk/prelude.rs @@ -1,3 +1,6 @@ +use std::fmt; +use std::os::raw::c_char; + use crate::vk; /// Holds 24 bits in the least significant bits of memory, @@ -59,3 +62,43 @@ impl From for vk::Rect2D { pub unsafe trait TaggedStructure { const STRUCTURE_TYPE: vk::StructureType; } + +#[inline] +pub(crate) unsafe fn wrap_c_str_slice_until_nul(str: &[c_char]) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(str.as_ptr()) +} + +#[derive(Debug)] +pub struct CStrTooLargeForStaticArray { + pub static_array_size: usize, + pub c_str_size: usize, +} +impl std::error::Error for CStrTooLargeForStaticArray {} +impl fmt::Display for CStrTooLargeForStaticArray { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "static `c_char` target array of length `{}` is too small to write a `CStr` (with `NUL`-terminator) of length `{}`", + self.static_array_size, self.c_str_size + ) + } +} + +#[inline] +pub(crate) fn write_c_str_slice_with_nul( + target: &mut [c_char], + str: &std::ffi::CStr, +) -> Result<(), CStrTooLargeForStaticArray> { + let bytes = str.to_bytes_with_nul(); + // SAFETY: The cast from c_char to u8 is ok because a c_char is always one byte. + let bytes = unsafe { std::slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) }; + let static_array_size = target.len(); + target + .get_mut(..bytes.len()) + .ok_or(CStrTooLargeForStaticArray { + static_array_size, + c_str_size: bytes.len(), + })? + .copy_from_slice(bytes); + Ok(()) +} diff --git a/generator/src/lib.rs b/generator/src/lib.rs index 5eb3bfb..806c974 100644 --- a/generator/src/lib.rs +++ b/generator/src/lib.rs @@ -1822,25 +1822,16 @@ fn derive_debug( let param_ident = field.param_ident(); let param_str = param_ident.to_string(); let debug_value = if is_static_array(field) && field.basetype == "char" { - quote! { - &unsafe { - ::std::ffi::CStr::from_ptr(self.#param_ident.as_ptr()) - } - } + let param_ident = format_ident!("{}_as_c_str", param_ident); + quote!(&unsafe { self.#param_ident() }) } else if param_str.contains("pfn") { - quote! { - &(self.#param_ident.map(|x| x as *const ())) - } + quote!(&(self.#param_ident.map(|x| x as *const ()))) } else if union_types.contains(field.basetype.as_str()) { quote!(&"union") } else { - quote! { - &self.#param_ident - } + quote!(&self.#param_ident) }; - quote! { - .field(#param_str, #debug_value) - } + quote!(.field(#param_str, #debug_value)) }); let name_str = name.to_string(); let lifetime = has_lifetime.then(|| quote!(<'_>)); @@ -1989,83 +1980,103 @@ fn derive_setters( }); } - // TODO: Improve in future when https://github.com/rust-lang/rust/issues/53667 is merged id:6 - if field.reference.is_some() { - if field.basetype == "char" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { + if field.basetype == "char" { + let param_ident_as_c_str = format_ident!("{}_as_c_str", param_ident_short); + if matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { assert!(field.null_terminate); assert_eq!(field.size, None); - return Some(quote!{ + return Some(quote! { #[inline] #deprecated - pub fn #param_ident_short(mut self, #param_ident_short: &'a ::std::ffi::CStr) -> Self { + pub fn #param_ident_short(mut self, #param_ident_short: &'a std::ffi::CStr) -> Self { self.#param_ident = #param_ident_short.as_ptr(); self } + #[inline] + #deprecated + pub unsafe fn #param_ident_as_c_str(&self) -> &std::ffi::CStr { + std::ffi::CStr::from_ptr(self.#param_ident) + } + }); + } else if is_static_array(field) { + assert_eq!(field.size, None); + return Some(quote! { + #[inline] + #deprecated + pub fn #param_ident_short(mut self, #param_ident_short: &std::ffi::CStr) -> std::result::Result { + write_c_str_slice_with_nul(&mut self.#param_ident, #param_ident_short).map(|()| self) + } + #[inline] + #deprecated + pub unsafe fn #param_ident_as_c_str(&self) -> &std::ffi::CStr { + wrap_c_str_slice_until_nul(&self.#param_ident) + } }); } + } - if matches!(field.array, Some(vkxml::ArrayType::Dynamic)) { - if let Some(ref array_size) = field.size { - let mut slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), None); + // TODO: Improve in future when https://github.com/rust-lang/rust/issues/53667 is merged id:6 + if field.reference.is_some() && matches!(field.array, Some(vkxml::ArrayType::Dynamic)) { + if let Some(ref array_size) = field.size { + let mut slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), None); - let mut ptr = if field.is_const { - quote!(.as_ptr()) - } else { - quote!(.as_mut_ptr()) - }; + let mut ptr = if field.is_const { + quote!(.as_ptr()) + } else { + quote!(.as_mut_ptr()) + }; - // Interpret void array as byte array - if field.basetype == "void" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { - slice_param_ty_tokens = quote!([u8]); + // Interpret void array as byte array + if field.basetype == "void" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { + slice_param_ty_tokens = quote!([u8]); + ptr = quote!(#ptr.cast()); + }; + + let set_size_stmt = if field.is_pointer_to_static_sized_array() { + // this is a pointer to a piece of memory with statically known size. + let array_size = field.c_size.as_ref().unwrap(); + let c_size = convert_c_expression(array_size, &BTreeMap::new()); + let inner_type = field.inner_type_tokens(None, None); + + slice_param_ty_tokens = quote!([#inner_type; #c_size]); + ptr = quote!(); + + quote!() + } else { + // Deal with a "special" 2D dynamic array with an inner size of 1 (effectively an array containing pointers to single objects) + let array_size = if let Some(array_size) = array_size.strip_suffix(",1") { + param_ident_short = format_ident!("{}_ptrs", param_ident_short); + slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), Some(1)); ptr = quote!(#ptr.cast()); + array_size + } else { + array_size }; - let set_size_stmt = if field.is_pointer_to_static_sized_array() { - // this is a pointer to a piece of memory with statically known size. - let array_size = field.c_size.as_ref().unwrap(); - let c_size = convert_c_expression(array_size, &BTreeMap::new()); - let inner_type = field.inner_type_tokens(None, None); + let array_size_ident = format_ident!("{}", array_size.to_snake_case()); - slice_param_ty_tokens = quote!([#inner_type; #c_size]); - ptr = quote!(); + let size_field = members.iter().find(|member| member.vkxml_field.name.as_deref() == Some(array_size)).unwrap(); + let cast = if size_field.vkxml_field.basetype == "size_t" { quote!() } else { - // Deal with a "special" 2D dynamic array with an inner size of 1 (effectively an array containing pointers to single objects) - let array_size = if let Some(array_size) = array_size.strip_suffix(",1") { - param_ident_short = format_ident!("{}_ptrs", param_ident_short); - slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), Some(1)); - ptr = quote!(#ptr.cast()); - array_size - } else { - array_size - }; - - let array_size_ident = format_ident!("{}", array_size.to_snake_case()); - - let size_field = members.iter().find(|member| member.vkxml_field.name.as_deref() == Some(array_size)).unwrap(); - - let cast = if size_field.vkxml_field.basetype == "size_t" { - quote!() - } else { - quote!(as _) - }; - - quote!(self.#array_size_ident = #param_ident_short.len()#cast;) + quote!(as _) }; - let mutable = if field.is_const { quote!() } else { quote!(mut) }; + quote!(self.#array_size_ident = #param_ident_short.len()#cast;) + }; - return Some(quote! { - #[inline] - #deprecated - pub fn #param_ident_short(mut self, #param_ident_short: &'a #mutable #slice_param_ty_tokens) -> Self { - #set_size_stmt - self.#param_ident = #param_ident_short #ptr; - self - } - }); - } + let mutable = if field.is_const { quote!() } else { quote!(mut) }; + + return Some(quote! { + #[inline] + #deprecated + pub fn #param_ident_short(mut self, #param_ident_short: &'a #mutable #slice_param_ty_tokens) -> Self { + #set_size_stmt + self.#param_ident = #param_ident_short #ptr; + self + } + }); } }