vulkan_rust_sys/
string_array.rs1use core::ffi::{c_char, CStr};
9use core::{fmt, hash};
10
11use crate::constants::{
12 MAX_DESCRIPTION_SIZE, MAX_DRIVER_INFO_SIZE, MAX_DRIVER_NAME_SIZE, MAX_EXTENSION_NAME_SIZE,
13 MAX_PHYSICAL_DEVICE_NAME_SIZE,
14};
15
16pub type ExtensionName = StringArray<{ MAX_EXTENSION_NAME_SIZE as usize }>;
18
19pub type LayerName = StringArray<{ MAX_EXTENSION_NAME_SIZE as usize }>;
21
22pub type DeviceName = StringArray<{ MAX_PHYSICAL_DEVICE_NAME_SIZE as usize }>;
24
25pub type DescriptionName = StringArray<{ MAX_DESCRIPTION_SIZE as usize }>;
27
28pub type DriverName = StringArray<{ MAX_DRIVER_NAME_SIZE as usize }>;
30
31pub type DriverInfo = StringArray<{ MAX_DRIVER_INFO_SIZE as usize }>;
33
34#[derive(Copy, Clone)]
50#[repr(transparent)]
51pub struct StringArray<const N: usize>(pub [c_char; N]);
52
53impl<const N: usize> StringArray<N> {
54 #[inline]
59 pub fn as_cstr(&self) -> &CStr {
60 let bytes: &[u8] = unsafe { core::slice::from_raw_parts(self.0.as_ptr().cast(), N) };
61 let end = bytes.iter().position(|&b| b == 0).unwrap_or(N.saturating_sub(1));
62 unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..end + 1]) }
63 }
64
65 pub fn from_cstr(cstr: &CStr) -> Self {
67 let mut array = [0 as c_char; N];
68 let bytes = cstr.to_bytes();
69 let len = bytes.len().min(N.saturating_sub(1));
70 for i in 0..len {
71 array[i] = bytes[i] as c_char;
72 }
73 Self(array)
74 }
75}
76
77impl<const N: usize> core::ops::Deref for StringArray<N> {
78 type Target = [c_char; N];
79
80 #[inline]
81 fn deref(&self) -> &Self::Target {
82 &self.0
83 }
84}
85
86impl<const N: usize> Default for StringArray<N> {
87 #[inline]
88 fn default() -> Self {
89 Self([0; N])
90 }
91}
92
93impl<const N: usize> PartialEq for StringArray<N> {
94 fn eq(&self, other: &Self) -> bool {
95 self.as_cstr() == other.as_cstr()
96 }
97}
98
99impl<const N: usize> Eq for StringArray<N> {}
100
101impl<const N: usize> hash::Hash for StringArray<N> {
102 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
103 self.as_cstr().hash(hasher);
104 }
105}
106
107impl<const N: usize> PartialEq<&CStr> for StringArray<N> {
108 fn eq(&self, other: &&CStr) -> bool {
109 self.as_cstr() == *other
110 }
111}
112
113impl<const N: usize> fmt::Display for StringArray<N> {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self.as_cstr().to_str() {
116 Ok(s) => f.write_str(s),
117 Err(_) => write!(f, "{:?}", self.as_cstr()),
118 }
119 }
120}
121
122impl<const N: usize> fmt::Debug for StringArray<N> {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "\"{}\"", self)
125 }
126}
127
128impl<const N: usize> From<[c_char; N]> for StringArray<N> {
129 #[inline]
130 fn from(array: [c_char; N]) -> Self {
131 Self(array)
132 }
133}
134
135impl<const N: usize> From<StringArray<N>> for [c_char; N] {
136 #[inline]
137 fn from(array: StringArray<N>) -> Self {
138 array.0
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 extern crate alloc;
145 use alloc::format;
146 use super::*;
147 use core::hash::Hasher;
148
149 fn hash_of(v: impl hash::Hash) -> u64 {
150 let mut h = SimpleHasher(0);
151 v.hash(&mut h);
152 h.0
153 }
154
155 struct SimpleHasher(u64);
157 impl Hasher for SimpleHasher {
158 fn write(&mut self, bytes: &[u8]) {
159 for &b in bytes {
160 self.0 = self.0.wrapping_mul(31).wrapping_add(b as u64);
161 }
162 }
163 fn finish(&self) -> u64 {
164 self.0
165 }
166 }
167
168 #[test]
169 fn equal_strings_are_equal() {
170 let a = StringArray::<256>::from_cstr(c"VK_KHR_swapchain");
171 let b = StringArray::<256>::from_cstr(c"VK_KHR_swapchain");
172 assert_eq!(a, b);
173 assert_eq!(hash_of(a), hash_of(b));
174 }
175
176 #[test]
177 fn different_strings_are_not_equal() {
178 let a = StringArray::<256>::from_cstr(c"VK_KHR_swapchain");
179 let b = StringArray::<256>::from_cstr(c"VK_KHR_surface");
180 assert_ne!(a, b);
181 }
182
183 #[test]
184 fn trailing_garbage_ignored() {
185 let mut arr_a = [0 as c_char; 8];
186 let mut arr_b = [0 as c_char; 8];
187 arr_a[0] = b'a' as c_char;
188 arr_a[1] = 0;
189 arr_a[2] = b'X' as c_char;
190 arr_b[0] = b'a' as c_char;
191 arr_b[1] = 0;
192 arr_b[2] = b'Y' as c_char;
193 assert_eq!(StringArray(arr_a), StringArray(arr_b));
194 assert_eq!(hash_of(StringArray(arr_a)), hash_of(StringArray(arr_b)));
195 }
196
197 #[test]
198 fn compare_with_cstr() {
199 let a = StringArray::<256>::from_cstr(c"VK_KHR_swapchain");
200 assert_eq!(a, c"VK_KHR_swapchain");
201 }
202
203 #[test]
204 fn display_shows_string() {
205 let a = StringArray::<256>::from_cstr(c"hello");
206 let s = format!("{a}");
207 assert_eq!(s, "hello");
208 }
209
210 #[test]
211 fn default_is_empty() {
212 let a = StringArray::<32>::default();
213 assert_eq!(a, c"");
214 }
215
216 #[test]
217 fn from_cstr_truncates_long_string() {
218 let a = StringArray::<4>::from_cstr(c"abcdef");
219 assert_eq!(a.as_cstr(), c"abc");
220 }
221
222 #[test]
223 fn round_trip_array() {
224 let orig = [b'h' as c_char, b'i' as c_char, 0, 0];
225 let sa = StringArray::from(orig);
226 let back: [c_char; 4] = sa.into();
227 assert_eq!(orig, back);
228 }
229}