1#![no_std]
24
25use core::cmp::Ordering::{self,Less,Greater,Equal};
26use core::marker::PhantomData;
27use core::result::Result;
28use core::slice;
29
30#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
35#[allow(non_camel_case_types)]
36pub type c_char = i8;
37#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "powerpc"))]
38#[allow(non_camel_case_types)]
39pub type c_char = u8;
40
41
42#[macro_export]
53macro_rules! cstr {
54 ($arg:expr) => ($crate::CString::new(concat!($arg, "\0")).unwrap());
55}
56
57pub struct CString<'a> {
59 data: *const c_char,
60 len: usize,
61 _marker: PhantomData<&'a c_char>
62}
63
64#[derive(Debug)]
67pub struct Error(&'static str);
68
69fn strlen(ptr: *const c_char) -> isize {
70 let mut ctr = 0isize;
71 loop {
72 if unsafe { *ptr.offset(ctr) == 0 as c_char } {
73 break
74 }
75
76 ctr += 1;
77 }
78
79 ctr
80}
81
82fn ascii_guard(ptr: *const c_char, len: usize) -> bool {
83 let mut ctr = 0usize;
84 while ctr < len {
85 if unsafe { *ptr.offset(ctr as isize) < 0 as c_char } {
86 return false;
87 }
88
89 ctr += 1;
90 }
91
92 true
93}
94
95impl<'a> CString<'a> {
96 pub fn new(s: &'a str) -> Result<CString<'a>, Error> {
112 if s.len() == 0 {
113 return Err(Error("0-length cstring found"));
114 }
115
116 let ret = CString {
117 data: s.as_ptr() as *const c_char,
118 len: s.len(),
119 _marker: PhantomData
120 };
121
122 if ! ascii_guard(ret.data, ret.len) {
123 return Err(Error("Invalid character in string"));
124 }
125
126 if unsafe { *ret.into_raw().offset(ret.len as isize - 1) != 0 } {
127 Err(Error("No NULL terminator present"))
128 } else {
129 Ok(ret)
130 }
131 }
132
133 pub unsafe fn from_raw(ptr: *const c_char) -> Result<CString<'a>, Error> {
137 let siz = strlen(ptr) as usize + 1usize;
138
139 let ret = CString {
140 data: ptr,
141 len: siz,
142 _marker: PhantomData
143 };
144
145 if ! ascii_guard(ret.data, ret.len) {
146 Err(Error("Invalid character in string"))
147 } else {
148 Ok(ret)
149 }
150 }
151
152 pub unsafe fn into_raw(&self) -> *const c_char {
155 self.data
156 }
157
158 pub fn len(&self) -> usize {
160 self.len
161 }
162}
163
164impl<'a> Eq for CString<'a> { }
165impl<'a> PartialEq for CString<'a> {
166 fn eq(&self, other: &CString) -> bool {
167 unsafe {
168 slice::from_raw_parts(self.data, other.len()) ==
169 slice::from_raw_parts(other.data, other.len())
170 }
171 }
172
173 fn ne(&self, other: &CString) -> bool {
174 ! self.eq(other)
175 }
176}
177
178impl<'a> PartialOrd for CString<'a> {
179 fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
180 unsafe {
181 slice::from_raw_parts(self.data, self.len())
182 .partial_cmp(slice::from_raw_parts(other.data, other.len()))
183 }
184 }
185
186 fn le(&self, other: &CString) -> bool {
187 match self.partial_cmp(other) {
188 Some(Less) => true,
189 Some(Equal) => true,
190 _ => false
191 }
192 }
193
194 fn ge(&self, other: &CString) -> bool {
195 match self.partial_cmp(other) {
196 Some(Greater) => true,
197 Some(Equal) => true,
198 _ => false
199 }
200 }
201
202 fn lt(&self, other: &CString) -> bool {
203 match self.partial_cmp(other) {
204 Some(Less) => true,
205 _ => false
206 }
207 }
208
209 fn gt(&self, other: &CString) -> bool {
210 match self.partial_cmp(other) {
211 Some(Greater) => true,
212 _ => false
213 }
214 }
215}
216
217impl<'a> Ord for CString<'a> {
218 fn cmp(&self, other: &CString) -> Ordering {
219 self.data.cmp(&other.data)
220 }
221}
222
223#[cfg(test)] #[macro_use] extern crate std;
224
225#[cfg(test)]
226mod test {
227 use super::{c_char,CString,Error};
228 use std::ffi::CString as OldString;
229 use std::slice;
230 use std::string::String;
231
232 #[test]
233 fn new_cstring() {
234 {
235 let cstr = cstr!("foo");
236 let buf = "foo\0".as_ptr() as *const c_char;
237
238 unsafe {
239 assert_eq!(slice::from_raw_parts(cstr.data, cstr.len()),
240 slice::from_raw_parts(buf, 4));
241 }
242 }
243 {
244 let raw = OldString::new("foo").unwrap().into_raw();
245 let cstr = unsafe { CString::from_raw(raw).unwrap() };
246 let buf = "foo\0".as_ptr() as *const c_char;
247
248 unsafe {
249 assert_eq!(slice::from_raw_parts(cstr.data, cstr.len()),
250 slice::from_raw_parts(buf, 4));
251 }
252 }
253 {
254 let cstr_res = CString::new("foo");
255
256 assert!(! cstr_res.is_ok());
257 }
258 {
259 let cstr_res = CString::new("");
260
261 assert!(! cstr_res.is_ok());
262 }
263 {
264 let cstr_res = CString::new("ñ\0");
265
266 assert!(! cstr_res.is_ok());
267 }
268 {
269 let cstr_res = CString::new("ай да, пирожки\0");
270
271 assert!(! cstr_res.is_ok());
272 }
273 {
274 {
275 let s = String::from("foo\0");
276 let cstr_res: Result<CString, Error> = CString::new(s.as_str());
277 assert!(cstr_res.is_ok());
278 }
279 }
280 }
281
282 #[test]
283 fn from_cstring() {
284 {
285 let cstr = cstr!("foo");
286 let raw = unsafe { cstr.into_raw() };
287 let other = OldString::new("foo").unwrap().into_raw();
288
289 unsafe {
290 assert_eq!(*raw, *other);
291 }
292 }
293 }
294
295 #[test]
296 fn cstring_len() {
297 {
298 let cstr = cstr!("foo");
299 assert_eq!(cstr.len(), 4);
300 }
301 {
302 let raw = OldString::new("foo").unwrap().into_raw();
303 let cstr = unsafe { CString::from_raw(raw).unwrap() };
304 assert_eq!(cstr.len(), 4);
305 }
306 }
307
308 #[test]
309 fn eq() {
310 {
311 let cstr = cstr!("foo");
312 let other = cstr!("bar");
313
314 assert!(cstr != other);
315 }
316 {
317 let cstr = cstr!("foo");
318 let other = cstr!("foo");
319
320 assert!(cstr == other);
321 }
322 }
323
324 #[test]
325 fn cmp() {
326 {
327 let cstr = cstr!("foo");
328 let other = cstr!("bar");
329
330 assert!(cstr > other);
331 }
332 {
333 let cstr = cstr!("foo");
334 let other = cstr!("bar");
335
336 assert!(other < cstr);
337 }
338 }
339}