From 4a9c8b62c1f79fe42558ac52989dc883607627df Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Sun, 20 Nov 2022 00:18:12 +0100 Subject: [PATCH] Add `cstr_from_bytes_until_nul` --- narcissus-core/Cargo.toml | 1 + narcissus-core/src/lib.rs | 60 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/narcissus-core/Cargo.toml b/narcissus-core/Cargo.toml index 3e7ca12..5b8808b 100644 --- a/narcissus-core/Cargo.toml +++ b/narcissus-core/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] fast-float = "0.2" +memchr = { version = "2" } stb_image-sys = { path = "../ffi/stb_image-sys" } \ No newline at end of file diff --git a/narcissus-core/src/lib.rs b/narcissus-core/src/lib.rs index 686378e..2c09509 100644 --- a/narcissus-core/src/lib.rs +++ b/narcissus-core/src/lib.rs @@ -26,7 +26,7 @@ pub use uuid::Uuid; pub use virtual_mem::{virtual_commit, virtual_free, virtual_reserve}; pub use virtual_vec::{VirtualDeque, VirtualVec}; -use std::mem::MaybeUninit; +use std::{ffi::CStr, mem::MaybeUninit}; #[macro_export] macro_rules! static_assert { @@ -438,6 +438,64 @@ pub fn mul_full_width_u8(x: u8, y: u8) -> (u8, u8) { ((result >> 8) as u8, result as u8) } +/// An error indicating that no nul byte was present. +/// +/// A slice used to create a [`CStr`] must contain a nul byte somewhere +/// within the slice. +/// +/// This error is created by the [`CStr::from_bytes_until_nul`] method. +/// +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FromBytesUntilNulError(()); + +impl std::fmt::Display for FromBytesUntilNulError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "data provided does not contain a nul") + } +} + +/// Creates a C string wrapper from a byte slice. +/// +/// This method will create a `CStr` from any byte slice that contains at +/// least one nul byte. The caller does not need to know or specify where +/// the nul byte is located. +/// +/// If the first byte is a nul character, this method will return an +/// empty `CStr`. If multiple nul characters are present, the `CStr` will +/// end at the first one. +/// +/// If the slice only has a single nul byte at the end, this method is +/// equivalent to [`CStr::from_bytes_with_nul`]. +/// +/// # Examples +/// ``` +/// use std::ffi::CStr; +/// use narcissus_core::cstr_from_bytes_until_nul; +/// +/// let mut buffer = [0u8; 16]; +/// unsafe { +/// // Here we might call an unsafe C function that writes a string +/// // into the buffer. +/// let buf_ptr = buffer.as_mut_ptr(); +/// buf_ptr.write_bytes(b'A', 8); +/// } +/// // Attempt to extract a C nul-terminated string from the buffer. +/// let c_str = cstr_from_bytes_until_nul(&buffer[..]).unwrap(); +/// assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA"); +/// ``` +pub fn cstr_from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> { + let nul_pos = memchr::memchr(0, bytes); + match nul_pos { + Some(nul_pos) => { + let subslice = &bytes[..nul_pos + 1]; + // SAFETY: We know there is a nul byte at nul_pos, so this slice + // (ending at the nul byte) is a well-formed C string. + Ok(unsafe { CStr::from_bytes_with_nul_unchecked(subslice) }) + } + None => Err(FromBytesUntilNulError(())), + } +} + #[cfg(test)] mod tests { use std::ffi::CStr; -- 2.49.0