#[cfg(test)]
#[macro_use]
extern crate lazy_static;
extern crate memchr;
extern crate objpool;
extern crate take_mut;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt;
use std::sync::Arc;
use objpool::{Item, Pool};
#[derive(Debug, Clone, Copy)]
pub struct NulError {
pub position: usize,
}
impl fmt::Display for NulError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "nul byte found in provided data at position: {}", self.position)
}
}
impl Error for NulError {
fn description(&self) -> &str { "nul byte found in data" }
}
#[derive(Debug, Clone)]
pub struct CStringPool {
pool: Arc<Pool<CString>>,
}
impl CStringPool {
pub fn new(default_string_capacity: usize) -> CStringPool {
CStringPool {
pool: Pool::new(move || {
let vec = Vec::with_capacity(default_string_capacity);
unsafe { CString::from_vec_unchecked(vec) }
}),
}
}
pub fn with_capacity(pool_capacity: usize, default_string_capacity: usize) -> CStringPool {
CStringPool {
pool: Pool::with_capacity(pool_capacity, move || {
let vec = Vec::with_capacity(default_string_capacity);
unsafe { CString::from_vec_unchecked(vec) }
}),
}
}
pub fn get_str<T: AsRef<str>>(&self, s: T) -> Result<Item<CString>, NulError> {
let str_ref = s.as_ref();
if let Some(i) = memchr::memchr(0, str_ref.as_bytes()) {
return Err(NulError { position: i });
}
let mut item = self.pool.get();
take_mut::take(&mut *item, |cstring| {
let mut string = unsafe { String::from_utf8_unchecked(cstring.into_bytes()) };
string.clear();
string.push_str(str_ref);
unsafe { CString::from_vec_unchecked(string.into_bytes()) }
});
Ok(item)
}
pub fn get_c_str<T: AsRef<CStr>>(&self, s: T) -> Item<CString> {
let str_ref = s.as_ref();
let mut item = self.pool.get();
take_mut::take(&mut *item, |cstring| {
let mut bytes = cstring.into_bytes();
bytes.clear();
bytes.extend(str_ref.to_bytes());
unsafe { CString::from_vec_unchecked(bytes) }
});
item
}
}
#[cfg(test)]
mod tests {
use super::*;
lazy_static! {
static ref POOL: CStringPool = CStringPool::new(128);
}
#[test]
fn round_trip() {
let s = "foo";
let cstr = POOL.get_str(s).unwrap();
assert_eq!(cstr.to_str().unwrap(), s);
}
#[test]
#[should_panic]
fn bad_string() {
let s = "fo\0o";
let _cstr = POOL.get_str(s).unwrap();
}
}