diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 420e022..9304a6f 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -2,8 +2,8 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; +use nix_util::string_return::callback_get_vec_u8; use std::ffi::CString; -use std::mem::MaybeUninit; use std::ptr::null_mut; use std::ptr::NonNull; @@ -78,22 +78,17 @@ impl Store { } pub fn get_uri(&self) -> Result { - const N: usize = 1024; - let mut buffer: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut raw_buffer: Vec = Vec::new(); unsafe { raw::nix_store_get_uri( self.context.ptr(), self.inner.ptr(), - buffer.as_mut_ptr() as *mut i8, - N as u32, + callback_get_vec_u8 as *mut std::ffi::c_void, + &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, ) }; self.context.check_err()?; - unsafe { - // copy the c string from buffer - let cstr = core::ffi::CStr::from_ptr(buffer.as_ptr() as *const i8); - cstr.to_str().map(|s| s.to_string()).map_err(|e| e.into()) - } + String::from_utf8(raw_buffer).map_err(|e| e.into()) } } diff --git a/rust/nix-util/src/lib.rs b/rust/nix-util/src/lib.rs index 9efb2ab..0306c93 100644 --- a/rust/nix-util/src/lib.rs +++ b/rust/nix-util/src/lib.rs @@ -1 +1,2 @@ pub mod context; +pub mod string_return; diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs new file mode 100644 index 0000000..ee62e22 --- /dev/null +++ b/rust/nix-util/src/string_return.rs @@ -0,0 +1,54 @@ +/// Callback for nix_store_get_uri and other functions that return a string. +/// +/// This function is used by the other nix_* crates, and you should never need to call it yourself. +pub unsafe extern "C" fn callback_get_vec_u8( + start: *const ::std::os::raw::c_char, + n: std::os::raw::c_uint, + user_data: *mut std::os::raw::c_void, +) { + let ret = user_data as *mut Vec; + let slice = std::slice::from_raw_parts(start as *const u8, n as usize); + if !(*ret).is_empty() { + panic!("callback_get_vec_u8: slice must be empty. Were we called twice?"); + } + (*ret).extend_from_slice(slice); +} + +#[cfg(test)] +mod tests { + use super::*; + use nix_c_raw as raw; + + /// Typecheck the function signature against the generated bindings in nix_c_raw. + static _CALLBACK_GET_VEC_U8: raw::nix_get_string_callback = Some(callback_get_vec_u8); + + #[test] + fn test_callback_get_vec_u8_empty() { + let mut ret: Vec = Vec::new(); + let start: *const std::os::raw::c_char = std::ptr::null(); + let n: std::os::raw::c_uint = 0; + let user_data: *mut std::os::raw::c_void = + &mut ret as *mut Vec as *mut std::os::raw::c_void; + + unsafe { + callback_get_vec_u8(start, n, user_data); + } + + assert_eq!(ret, vec![]); + } + + #[test] + fn test_callback_get_vec_u8() { + let mut ret: Vec = Vec::new(); + let start: *const std::os::raw::c_char = b"helloGARBAGE".as_ptr() as *const i8; + let n: std::os::raw::c_uint = 5; + let user_data: *mut std::os::raw::c_void = + &mut ret as *mut Vec as *mut std::os::raw::c_void; + + unsafe { + callback_get_vec_u8(start, n, user_data); + } + + assert_eq!(ret, b"hello".to_vec()); + } +}