Skip to main content

ferroui_vulkan/unmanaged_interop/
utf8_buffer_array.rs

1use std::ffi::{c_char, CString};
2
3/// The strings of a list as an array of pointers to zero-terminated UTF-8
4/// strings, the form in which Vulkan takes the names of layers and
5/// extensions. The array is valid for as long as the value lives.
6pub struct Utf8BufferArray {
7    buffers: Vec<CString>,
8    buffer_array: Vec<*const c_char>,
9}
10
11impl Utf8BufferArray {
12    /// Copies the strings. A string is cut at its first zero character,
13    /// which is where a reader of the zero-terminated copy stops.
14    pub fn new<S: AsRef<str>>(strings: impl IntoIterator<Item = S>) -> Self {
15        let buffers: Vec<CString> = strings
16            .into_iter()
17            .map(|s| {
18                let s = s.as_ref();
19                let end = s.find('\0').unwrap_or(s.len());
20                CString::new(&s[..end]).expect("the string was cut at its first zero character")
21            })
22            .collect();
23        // The pointers are into the heap blocks of the strings, which do not
24        // move when the vector that holds the strings does.
25        let buffer_array = buffers.iter().map(|buffer| buffer.as_ptr()).collect();
26        Self { buffers, buffer_array }
27    }
28
29    /// The array of pointers.
30    pub fn as_slice(&self) -> &[*const c_char] {
31        &self.buffer_array
32    }
33
34    /// The number of strings.
35    pub fn count(&self) -> i32 {
36        self.buffers.len() as i32
37    }
38
39    /// The number of strings, as Vulkan counts.
40    pub fn u_count(&self) -> u32 {
41        self.count() as u32
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use std::ffi::CStr;
49
50    #[test]
51    fn the_array_points_at_zero_terminated_copies() {
52        let array = Utf8BufferArray::new(["VK_KHR_surface", "VK_KHR_xlib_surface", "zażółć"]);
53        assert_eq!(array.count(), 3);
54        assert_eq!(array.u_count(), 3);
55        let read: Vec<String> = array
56            .as_slice()
57            .iter()
58            // SAFETY: each pointer is to a zero-terminated string the array owns.
59            .map(|pointer| unsafe { CStr::from_ptr(*pointer) }.to_str().unwrap().to_string())
60            .collect();
61        assert_eq!(read, ["VK_KHR_surface", "VK_KHR_xlib_surface", "zażółć"]);
62    }
63
64    #[test]
65    fn an_empty_list_is_an_empty_array() {
66        let array = Utf8BufferArray::new(Vec::<String>::new());
67        assert_eq!(array.count(), 0);
68        assert!(array.as_slice().is_empty());
69    }
70
71    #[test]
72    fn a_string_ends_at_its_first_zero_character() {
73        let array = Utf8BufferArray::new(["ab\0cd"]);
74        // SAFETY: as above.
75        let read = unsafe { CStr::from_ptr(array.as_slice()[0]) };
76        assert_eq!(read.to_str().unwrap(), "ab");
77    }
78}