rust/src/librustc_data_structures/small_c_str.rs
2018-08-23 23:13:14 +02:00

123 lines
3.2 KiB
Rust

// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::ffi;
use std::ops::Deref;
use smallvec::SmallVec;
const SIZE: usize = 36;
/// Like SmallVec but for C strings.
#[derive(Clone)]
pub struct SmallCStr {
data: SmallVec<[u8; SIZE]>,
}
impl SmallCStr {
#[inline]
pub fn new(s: &str) -> SmallCStr {
let len = s.len();
let len1 = len + 1;
let data = if len < SIZE {
let mut buf = [0; SIZE];
buf[..len].copy_from_slice(s.as_bytes());
SmallVec::from_buf_and_len(buf, len1)
} else {
let mut data = Vec::with_capacity(len1);
data.extend_from_slice(s.as_bytes());
data.push(0);
SmallVec::from_vec(data)
};
if let Err(e) = ffi::CStr::from_bytes_with_nul(&data) {
panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
}
SmallCStr { data }
}
#[inline]
pub fn new_with_nul(s: &str) -> SmallCStr {
let b = s.as_bytes();
if let Err(e) = ffi::CStr::from_bytes_with_nul(b) {
panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
}
SmallCStr { data: SmallVec::from_slice(s.as_bytes()) }
}
#[inline]
pub fn as_c_str(&self) -> &ffi::CStr {
unsafe {
ffi::CStr::from_bytes_with_nul_unchecked(&self.data[..])
}
}
#[inline]
pub fn len_with_nul(&self) -> usize {
self.data.len()
}
pub fn spilled(&self) -> bool {
self.data.spilled()
}
}
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!(!scs.spilled());
}
#[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!(!scs.spilled());
}
#[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!(scs.spilled());
}
#[test]
#[should_panic]
fn internal_nul() {
let _ = SmallCStr::new("abcd\0def");
}