// Copyright 2018 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ffi; use std::ops::Deref; const SIZE: usize = 38; /// Like SmallVec but for C strings. #[derive(Clone)] pub enum SmallCStr { OnStack { data: [u8; SIZE], len_with_nul: u8, }, OnHeap { data: ffi::CString, } } impl SmallCStr { #[inline] pub fn new(s: &str) -> SmallCStr { if s.len() < SIZE { let mut data = [0; SIZE]; data[.. s.len()].copy_from_slice(s.as_bytes()); let len_with_nul = s.len() + 1; // Make sure once that this is a valid CStr if let Err(e) = ffi::CStr::from_bytes_with_nul(&data[.. len_with_nul]) { panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e); } SmallCStr::OnStack { data, len_with_nul: len_with_nul as u8, } } else { SmallCStr::OnHeap { data: ffi::CString::new(s).unwrap() } } } #[inline] pub fn as_c_str(&self) -> &ffi::CStr { match *self { SmallCStr::OnStack { ref data, len_with_nul } => { unsafe { let slice = &data[.. len_with_nul as usize]; ffi::CStr::from_bytes_with_nul_unchecked(slice) } } SmallCStr::OnHeap { ref data } => { data.as_c_str() } } } #[inline] pub fn len_with_nul(&self) -> usize { match *self { SmallCStr::OnStack { len_with_nul, .. } => { len_with_nul as usize } SmallCStr::OnHeap { ref data } => { data.as_bytes_with_nul().len() } } } } impl Deref for SmallCStr { type Target = ffi::CStr; fn deref(&self) -> &ffi::CStr { self.as_c_str() } } #[test] fn short() { const TEXT: &str = "abcd"; let reference = ffi::CString::new(TEXT.to_string()).unwrap(); let scs = SmallCStr::new(TEXT); assert_eq!(scs.len_with_nul(), TEXT.len() + 1); assert_eq!(scs.as_c_str(), reference.as_c_str()); assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false }); } #[test] fn empty() { const TEXT: &str = ""; let reference = ffi::CString::new(TEXT.to_string()).unwrap(); let scs = SmallCStr::new(TEXT); assert_eq!(scs.len_with_nul(), TEXT.len() + 1); assert_eq!(scs.as_c_str(), reference.as_c_str()); assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false }); } #[test] fn long() { const TEXT: &str = "01234567890123456789012345678901234567890123456789\ 01234567890123456789012345678901234567890123456789\ 01234567890123456789012345678901234567890123456789"; let reference = ffi::CString::new(TEXT.to_string()).unwrap(); let scs = SmallCStr::new(TEXT); assert_eq!(scs.len_with_nul(), TEXT.len() + 1); assert_eq!(scs.as_c_str(), reference.as_c_str()); assert!(if let SmallCStr::OnHeap { .. } = scs { true } else { false }); } #[test] #[should_panic] fn internal_nul() { let _ = SmallCStr::new("abcd\0def"); }