1#[cfg(test)]
8#[macro_use]
9extern crate lazy_static;
10
11extern crate memchr;
12extern crate objpool;
13extern crate take_mut;
14
15use std::error::Error;
16use std::ffi::{CStr, CString};
17use std::fmt;
18use std::sync::Arc;
19
20use objpool::{Item, Pool};
21
22
23#[derive(Debug, Clone, Copy)]
26pub struct NulError {
27 pub position: usize,
28}
29
30
31impl fmt::Display for NulError {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 write!(f, "nul byte found in provided data at position: {}", self.position)
34 }
35}
36
37
38impl Error for NulError {
39 fn description(&self) -> &str { "nul byte found in data" }
40}
41
42
43#[derive(Debug, Clone)]
45pub struct CStringPool {
46 pool: Arc<Pool<CString>>,
47}
48
49
50impl CStringPool {
51 pub fn new(default_string_capacity: usize) -> CStringPool {
53 CStringPool {
54 pool: Pool::new(move || {
55 let vec = Vec::with_capacity(default_string_capacity);
56
57 unsafe { CString::from_vec_unchecked(vec) }
59 }),
60 }
61 }
62
63
64 pub fn with_capacity(pool_capacity: usize, default_string_capacity: usize) -> CStringPool {
67 CStringPool {
68 pool: Pool::with_capacity(pool_capacity, move || {
69 let vec = Vec::with_capacity(default_string_capacity);
70
71 unsafe { CString::from_vec_unchecked(vec) }
73 }),
74 }
75 }
76
77
78 pub fn get_str<T: AsRef<str>>(&self, s: T) -> Result<Item<CString>, NulError> {
81 let str_ref = s.as_ref();
82
83 if let Some(i) = memchr::memchr(0, str_ref.as_bytes()) {
85 return Err(NulError { position: i });
86 }
87
88 let mut item = self.pool.get();
89 take_mut::take(&mut *item, |cstring| {
90 let mut string = unsafe { String::from_utf8_unchecked(cstring.into_bytes()) };
93
94 string.clear();
95 string.push_str(str_ref);
96
97 unsafe { CString::from_vec_unchecked(string.into_bytes()) }
100 });
101
102 Ok(item)
103 }
104
105
106 pub fn get_c_str<T: AsRef<CStr>>(&self, s: T) -> Item<CString> {
108 let str_ref = s.as_ref();
109
110 let mut item = self.pool.get();
111 take_mut::take(&mut *item, |cstring| {
112 let mut bytes = cstring.into_bytes();
113
114 bytes.clear();
115 bytes.extend(str_ref.to_bytes());
116
117 unsafe { CString::from_vec_unchecked(bytes) }
119 });
120
121 item
122 }
123}
124
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 lazy_static! {
131 static ref POOL: CStringPool = CStringPool::new(128);
132 }
133
134 #[test]
135 fn round_trip() {
136 let s = "foo";
137 let cstr = POOL.get_str(s).unwrap();
138
139 assert_eq!(cstr.to_str().unwrap(), s);
140 }
141
142
143 #[test]
144 #[should_panic]
145 fn bad_string() {
146 let s = "fo\0o";
147 let _cstr = POOL.get_str(s).unwrap();
148 }
149}