diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index a2022a2eeb23c..1caf233fa5dbe 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -411,6 +411,115 @@ impl CString { CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) } } + /// Copies up to `maxlen` bytes from a C buffer into a new CString, and then appends + /// a null terminator. Copying stops early if a null byte is found before the end of + /// the buffer. + /// + /// # Safety + /// + /// No bytes past the end of the buffer will be accessed even if no null terminator + /// is found. + /// + /// Since the data is copied, the buffer may be freed once this function returns. + /// + /// # Examples + /// + /// Receive a string into a fixed size buffer, and then extract it as a CString. + /// + /// ```no_run + /// #![feature(cstr_of_fixed_size)] + /// + /// use std::ffi::CString; + /// use std::os::raw::c_char; + /// use std::mem; + /// + /// extern { + /// fn extern_fill_my_buf(s: *mut c_char, maxlen: usize); + /// } + /// + /// unsafe { + /// let mut buf = mem::uninitialized::<[c_char; 256]>(); + /// extern_fill_my_buf(buf.as_mut_ptr(), buf.len()); + /// let c_string = CString::of_fixed_size_raw(buf.as_ptr(), buf.len()); + /// } + /// ``` + #[unstable(feature = "cstr_of_fixed_size", issue = "20475")] + pub unsafe fn of_fixed_size_raw(ptr: *const c_char, maxlen: usize) -> CString { + let len = sys::strnlen(ptr, maxlen) as usize; // Including the NUL byte + let mut result = Vec::with_capacity(len+1); + result.extend_from_slice(slice::from_raw_parts(ptr as *const u8, len)); + CString::from_vec_unchecked(result) + } + + /// Copies up to `maxlen` bytes from a buffer into a new CString, and then appends + /// a null terminator. Copying stops early if a null byte is found before the end of + /// the buffer. + /// + /// # Examples + /// + /// Receive a string into a fixed size buffer, and then extract it as a CString. + /// + /// ```no_run + /// #![feature(cstr_of_fixed_size)] + /// + /// use std::ffi::CString; + /// use std::os::raw::c_char; + /// use std::mem; + /// + /// extern { + /// fn extern_fill_my_buf(s: *mut c_char, maxlen: usize); + /// } + /// + /// unsafe { + /// let mut buf = mem::uninitialized::<[u8; 256]>(); + /// extern_fill_my_buf(buf.as_mut_ptr() as *mut c_char, buf.len()); + /// let c_string = CString::of_fixed_size(&buf[..]); + /// } + /// ``` + #[unstable(feature = "cstr_of_fixed_size", issue = "20475")] + pub fn of_fixed_size(slice: &[u8]) -> CString { + unsafe { + CString::of_fixed_size_raw(slice.as_ptr() as *const c_char, slice.len()) + } + } + + /// Creates a new C-compatible string from a container of bytes. + /// + /// The string is terminated at the first null byte present within the + /// container. + /// + /// # Examples + /// + /// Receive a string into a fixed size buffer, and then extract it as a CString. + /// + /// ```no_run + /// #![feature(cstr_of_fixed_size)] + /// + /// use std::ffi::CString; + /// use std::os::raw::c_char; + /// + /// extern { + /// fn extern_fill_my_buf(s: *mut c_char, maxlen: usize); + /// } + /// + /// unsafe { + /// let mut buf = vec![0u8; 256]; + /// extern_fill_my_buf(buf.as_mut_ptr() as *mut c_char, buf.len()); + /// let c_string = CString::from_fixed_size(buf); + /// } + /// ``` + #[unstable(feature = "cstr_of_fixed_size", issue = "20475")] + pub fn from_fixed_size>>(vec: T) -> CString { + Self::_from_fixed_size(vec.into()) + } + + fn _from_fixed_size(mut bytes: Vec) -> CString { + if let Some(i) = memchr::memchr(0, &bytes) { + bytes.truncate(i); + } + unsafe { CString::from_vec_unchecked(bytes) } + } + /// Consumes the `CString` and transfers ownership of the string to a C caller. /// /// The pointer which this function returns must be returned to Rust and reconstituted using @@ -1276,6 +1385,22 @@ mod tests { } } + #[test] + fn build_with_zero4() { + let s = CString::of_fixed_size(&b"1234\05678"[..]); + assert_eq!(s.as_bytes(), b"1234"); + let s = CString::of_fixed_size(&b"1234"[..]); + assert_eq!(s.as_bytes(), b"1234"); + } + + #[test] + fn build_with_zero5() { + let s = CString::from_fixed_size(b"1234\05678".to_vec()); + assert_eq!(s.as_bytes(), b"1234"); + let s = CString::from_fixed_size(b"1234".to_vec()); + assert_eq!(s.as_bytes(), b"1234"); + } + #[test] fn formatted() { let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs index 4352b72c30773..37a6e58e42a9f 100644 --- a/src/libstd/sys/redox/mod.rs +++ b/src/libstd/sys/redox/mod.rs @@ -12,7 +12,7 @@ use io::{self, ErrorKind}; -pub use libc::strlen; +pub use libc::{strlen, strnlen}; pub use self::rand::hashmap_random_keys; pub mod args; diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 9bdea945ea42e..e255410205226 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -30,7 +30,7 @@ use libc; #[cfg(all(not(dox), target_os = "l4re"))] pub use os::linux as platform; pub use self::rand::hashmap_random_keys; -pub use libc::strlen; +pub use libc::{strlen, strnlen}; #[macro_use] pub mod weak; diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs index b838dbafd6f0c..5770b6991e795 100644 --- a/src/libstd/sys/wasm/mod.rs +++ b/src/libstd/sys/wasm/mod.rs @@ -90,6 +90,15 @@ pub unsafe fn strlen(mut s: *const c_char) -> usize { return n } +pub unsafe fn strnlen(mut s: *const c_char, maxlen: usize) -> usize { + let mut n = 0; + while n < maxlen && *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + pub unsafe fn abort_internal() -> ! { ::intrinsics::abort(); } diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 0d12ecf8fe3a1..1f1296132da36 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -17,7 +17,7 @@ use os::windows::ffi::{OsStrExt, OsStringExt}; use path::PathBuf; use time::Duration; -pub use libc::strlen; +pub use libc::{strlen, strnlen}; pub use self::rand::hashmap_random_keys; #[macro_use] pub mod compat;