null_terminated/
lib.rs

1//! Library of [null-terminated slices](struct.Nul.html) and
2//! [UTF-8-encoded strings](struct.NulStr.html), references to which are thin pointers for
3//! efficiency and ease of use with FFI
4//!
5//! The likely primary use cases are C FFI and OS ABI (for example: on Unix, many system calls
6//! take, and the initial environment involves, null-terminated arguments).
7//!
8//! As the representation is a bare pointer to the element type, one can declare foreign
9//! functions which merely take arguments or return values of type `Nul<_>`, for example:
10//! ```no_run
11//! # extern crate null_terminated; use null_terminated::Nul; type c_int = i32;
12//! extern "C" {
13//!     fn strlen(_: &Nul<u8>) -> usize;
14//!     fn strchr(_: &Nul<u8>, _: c_int) -> &Nul<u8>;
15//! }
16//! ```
17//!
18//! For further examples, see the docs of [`Nul`](struct.Nul.html).
19
20#![no_std]
21
22#![deny(missing_debug_implementations)]
23#![deny(missing_docs)]
24
25#![feature(const_raw_ptr_deref)]
26#![feature(extern_types)]
27
28extern crate unreachable;
29
30#[cfg(feature = "utf")]
31extern crate utf;
32
33#[cfg(test)] extern crate quickcheck;
34
35#[cfg(test)] #[macro_use] extern crate quickcheck_macros;
36#[cfg(test)] extern crate std;
37
38#[doc(hidden)]
39pub extern crate byte_strings_proc_macros as __byte_strings;
40
41use core::{cmp::*, convert::TryFrom, fmt::{self, Debug, Display}, hash::{Hash, Hasher},
42           marker::PhantomData, mem, ops::*, slice};
43
44extern { type Opaque; }
45unsafe impl Send for Opaque {}
46unsafe impl Sync for Opaque {}
47
48/// Generic unsized null-terminated array
49///
50/// `&Nul<A>` is a thin pointer, so it can be readily used with FFI.
51///
52/// # Examples
53///
54/// One can safely take views of null-terminated slices with [`TryFrom::try_from`](https://doc.rust-lang.org/core/convert/trait.TryFrom.html#tymethod.try_from):
55/// ```
56/// # extern crate null_terminated; use null_terminated::Nul;
57/// extern "C" {
58///     fn c_f(path: *const u8) -> i32;
59/// }
60///
61/// fn f(path: &[u8]) -> Result<i32, ()> {
62///     <&Nul<u8> as ::std::convert::TryFrom<_>>::try_from(path)
63///         .map(|path| unsafe { c_f(path.as_ptr()) })
64/// }
65/// ```
66#[repr(C)]
67pub struct Nul<A>([A; 0], Opaque);
68
69impl<A> Nul<A> {
70    /// Return a pointer to the start of the array.
71    #[inline]
72    pub const fn as_ptr(&self) -> *const A { self as *const Self as *const A }
73
74    /// Return a mutable pointer to the start of the array.
75    #[inline]
76    pub fn as_mut_ptr(&mut self) -> *mut A { self as *mut Self as *mut A }
77
78    /// Iterate over array elements.
79    #[inline]
80    pub const fn iter(&self) -> Iter<A> { Iter(self.as_ptr(), PhantomData) }
81
82    /// Iterate over array elements, mutably.
83    #[inline]
84    pub fn iter_mut(&mut self) -> IterMut<A> { IterMut(self.as_mut_ptr(), PhantomData) }
85
86    /// Create a reference to a null-terminated array, given a pointer to its start.
87    ///
88    /// The caller must make sure the argument does, in fact, point to a null-terminated array,
89    /// and the returned reference not live longer than the array it refers to. These
90    /// requirements are not checked.
91    #[inline]
92    pub const unsafe fn new_unchecked(p: *const A) -> &'static Self { &*(p as *const Nul<A>) }
93
94    /// Create a mutable reference to a null-terminated array, given a pointer to its start.
95    ///
96    /// The caller must make sure the argument does, in fact, point to a null-terminated array,
97    /// and the returned reference not live longer than the array it refers to. These
98    /// requirements are not checked.
99    #[inline]
100    pub unsafe fn new_unchecked_mut(p: *mut A) -> &'static mut Self { &mut *(p as *mut Nul<A>) }
101
102    /// Return array length. `O(n)`
103    #[inline]
104    pub fn len(&self) -> usize { unsafe {
105        if 0 == mem::size_of::<A>() { return 0; }
106        let mut p = self.as_ptr();
107        while !is_null(&*p) { p = p.offset(1); }
108        ptr_diff(p, self.as_ptr())
109    } }
110
111    /// Get element at given position. `O(n)` to check bounds
112    #[inline]
113    pub fn get(&self, i: usize) -> Option<&A> { self[..].get(i) }
114
115    /// Get element at given position, mutably. `O(n)` to check bounds
116    #[inline]
117    pub fn get_mut(&mut self, i: usize) -> Option<&mut A> { self[..].get_mut(i) }
118
119    /// Split array at given position.
120    ///
121    /// # Panics
122    ///
123    /// Panics if index out of bounds.
124    #[inline]
125    pub fn split_at(&self, i: usize) -> (&[A], &Self) {
126        self.try_split_at(i).expect("index out of bounds")
127    }
128
129    /// Split array at given position, mutably.
130    ///
131    /// # Panics
132    ///
133    /// Panics if index out of bounds.
134    #[inline]
135    pub fn split_at_mut(&mut self, i: usize) -> (&mut [A], &mut Self) {
136        self.try_split_at_mut(i).expect("index out of bounds")
137    }
138
139    /// Split array at given position; return `None` if index out of bounds.
140    #[inline]
141    pub fn try_split_at(&self, i: usize) -> Option<(&[A], &Self)> {
142        let mut it = self.iter();
143        for _ in 0..i { if let None = it.next() { return None; } }
144        Some(unsafe { (slice::from_raw_parts(self.as_ptr(), i), <&Self>::from(it)) })
145    }
146
147    /// Split array at given position, mutably; return `None` if index out of bounds.
148    #[inline]
149    pub fn try_split_at_mut(&mut self, i: usize) -> Option<(&mut [A], &mut Self)> {
150        let p = self.as_mut_ptr();
151        let mut it = self.iter_mut();
152        for _ in 0..i { if let None = it.next() { return None; } }
153        Some(unsafe { (slice::from_raw_parts_mut(p, i), <&mut Self>::from(it)) })
154    }
155}
156
157/// Cast.
158pub const unsafe fn cast<A, B>(a: &Nul<A>) -> &Nul<B> { &*(a as *const Nul<A> as *const Nul<B>) }
159
160impl<A, I> Index<I> for Nul<A> where [A]: Index<I> {
161    type Output = <[A] as Index<I>>::Output;
162    #[inline]
163    fn index(&self, i: I) -> &Self::Output {
164        unsafe { slice::from_raw_parts(self.as_ptr(), self.len()).index(i) }
165    }
166}
167
168impl<A, I> IndexMut<I> for Nul<A> where [A]: IndexMut<I> {
169    #[inline]
170    fn index_mut(&mut self, i: I) -> &mut Self::Output {
171        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()).index_mut(i) }
172    }
173}
174
175impl<A: Debug> Debug for Nul<A> {
176    #[inline]
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self[..].fmt(f) }
178}
179
180impl<A: PartialEq> PartialEq for Nul<A> {
181    #[inline]
182    fn eq(&self, other: &Self) -> bool { self[..] == other[..] }
183}
184
185impl<A: Eq> Eq for Nul<A> {}
186
187impl<A: PartialOrd> PartialOrd for Nul<A> {
188    #[inline]
189    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190        <[A]>::partial_cmp(&self[..], &other[..])
191    }
192}
193
194impl<A: Ord> Ord for Nul<A> {
195    #[inline]
196    fn cmp(&self, other: &Self) -> Ordering { <[A]>::cmp(&self[..], &other[..]) }
197}
198
199impl<A: Hash> Hash for Nul<A> {
200    #[inline]
201    fn hash<H: Hasher>(&self, h: &mut H) { self.iter().for_each(|a| a.hash(h)) }
202}
203
204impl<'a, A> TryFrom<&'a [A]> for &'a Nul<A> {
205    type Error = ();
206    #[inline]
207    fn try_from(xs: &'a [A]) -> Result<Self, ()> {
208        if xs.last().map_or(false, is_null) { Ok(unsafe { &*(xs.as_ptr() as *const Nul<A>) }) }
209        else { Err(()) }
210    }
211}
212
213impl<'a, A> TryFrom<&'a mut [A]> for &'a mut Nul<A> {
214    type Error = ();
215    #[inline]
216    fn try_from(xs: &'a mut [A]) -> Result<Self, ()> {
217        if xs.last().map_or(false, is_null) { Ok(unsafe { &mut *(xs.as_mut_ptr() as *mut Nul<A>) }) }
218        else { Err(()) }
219    }
220}
221
222impl<'a, A> IntoIterator for &'a Nul<A> {
223    type Item = &'a A;
224    type IntoIter = Iter<'a, A>;
225    #[inline]
226    fn into_iter(self) -> Iter<'a, A> { self.iter() }
227}
228
229impl<'a, A> IntoIterator for &'a mut Nul<A> {
230    type Item = &'a mut A;
231    type IntoIter = IterMut<'a, A>;
232    #[inline]
233    fn into_iter(self) -> IterMut<'a, A> { self.iter_mut() }
234}
235
236impl<'a, A> From<Iter<'a, A>> for &'a Nul<A> {
237    #[inline]
238    fn from(it: Iter<'a, A>) -> Self { unsafe { &*(it.0 as *const Nul<A>) } }
239}
240
241impl<'a, A> From<IterMut<'a, A>> for &'a mut Nul<A> {
242    #[inline]
243    fn from(it: IterMut<'a, A>) -> Self { unsafe { &mut *(it.0 as *mut Nul<A>) } }
244}
245
246/// Iterator over the elements of a null-terminated array
247#[derive(Debug, Clone, Copy)]
248pub struct Iter<'a, A: 'a>(*const A, PhantomData<&'a A>);
249
250unsafe impl<'a, T: Sync> Send for Iter<'a, T> {}
251unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {}
252
253impl<'a, A: 'a> Iterator for Iter<'a, A> {
254    type Item = &'a A;
255    #[inline]
256    fn next(&mut self) -> Option<&'a A> { unsafe {
257        if is_null(&*self.0) { None } else {
258            let ptr = self.0;
259            self.0 = ptr.offset(1);
260            Some(&*ptr)
261        }
262    } }
263}
264
265/// Iterator over the elements of a mutable null-terminated array
266#[derive(Debug)]
267pub struct IterMut<'a, A: 'a>(*mut A, PhantomData<&'a mut A>);
268
269unsafe impl<'a, T: Send> Send for IterMut<'a, T> {}
270unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {}
271
272impl<'a, A: 'a> Iterator for IterMut<'a, A> {
273    type Item = &'a mut A;
274    #[inline]
275    fn next(&mut self) -> Option<&'a mut A> { unsafe {
276        if is_null(&*self.0) { None } else {
277            let ptr = self.0;
278            self.0 = ptr.offset(1);
279            Some(&mut *ptr)
280        }
281    } }
282}
283
284impl Display for Nul<char> {
285    #[inline]
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        use fmt::Write;
288        for &x in self { f.write_char(x)?; }
289        Ok(())
290    }
291}
292
293impl<A> AsRef<Nul<A>> for Nul<A> { #[inline] fn as_ref(&self) -> &Self { self } }
294
295/// Null-terminated UTF-8 encoded string
296///
297/// `&NulStr` is a thin pointer, so it can be readily used with FFI.
298///
299/// One can convert from `&Nul<u8>` to `&NulStr` with `try_from`, which checks whether its
300/// argument is valid UTF-8.
301#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
302#[repr(transparent)]
303pub struct NulStr(Nul<u8>);
304
305impl Debug for NulStr {
306    #[inline]
307    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{:?}", &self[..]) }
308}
309
310#[cfg(feature = "utf")]
311impl fmt::Display for NulStr {
312    #[inline]
313    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(&self[..]) }
314}
315
316impl Index<RangeFull> for NulStr {
317    type Output = str;
318
319    #[inline]
320    fn index(&self, _: RangeFull) -> &str { unsafe {
321        ::core::str::from_utf8_unchecked(&self.0[..])
322    } }
323}
324
325impl IndexMut<RangeFull> for NulStr {
326    #[inline]
327    fn index_mut(&mut self, _: RangeFull) -> &mut str { unsafe {
328        ::core::str::from_utf8_unchecked_mut(&mut self.0[..])
329    } }
330}
331
332impl NulStr {
333    /// Create a reference to a null-terminated string, given a pointer to its start.
334    ///
335    /// The caller must make sure the argument does, in fact, point to a null-terminated string;
336    /// the string is valid UTF-8; and the returned reference not live longer than the array it
337    /// refers to. These requirements are not checked.
338    #[inline]
339    pub const unsafe fn new_unchecked(p: *const u8) -> &'static Self { &*(p as *const Self) }
340
341    /// Create a mutable reference to a null-terminated string, given a pointer to its start.
342    ///
343    /// The caller must make sure the argument does, in fact, point to a null-terminated string;
344    /// the string is valid UTF-8; and the returned reference not live longer than the array it
345    /// refers to. These requirements are not checked.
346    #[inline]
347    pub unsafe fn new_unchecked_mut(p: *mut u8) -> &'static mut Self { &mut *(p as *mut Self) }
348
349    /// Return a slice of the UTF-8 code bytes of the string.
350    #[inline]
351    pub fn as_bytes(&self) -> &Nul<u8> { &self.0 }
352
353    /// Return a mutable slice of the UTF-8 code bytes of the string.
354    #[inline]
355    pub fn as_bytes_mut(&mut self) -> &mut Nul<u8> { &mut self.0 }
356
357    /// Return a pointer to the start of the string.
358    #[inline]
359    pub fn as_ptr(&self) -> *const u8 { self.0.as_ptr() }
360
361    /// Return a mutable pointer to the start of the string.
362    #[inline]
363    pub fn as_mut_ptr(&mut self) -> *const u8 { self.0.as_mut_ptr() }
364
365    /// Iterate over the characters of the string.
366    #[cfg(feature = "utf")]
367    #[inline]
368    pub fn chars(&self) -> Chars { Chars(::utf::decode_utf8(self.0.iter().cloned())) }
369
370    /// Iterate over the characters of the string and their byte positions.
371    #[cfg(feature = "utf")]
372    #[inline]
373    pub fn char_indices(&self) -> CharIndices { CharIndices(self.chars(), 0) }
374
375    /// Return whether the given byte position is a character boundary.
376    #[inline]
377    pub fn is_char_boundary(&self, k: usize) -> bool { self[..].is_char_boundary(k) }
378}
379
380/// Iterator over the characters of a null-terminated string
381#[cfg(feature = "utf")]
382#[derive(Debug, Clone)]
383pub struct Chars<'a>(::utf::DecodeUtf8<::core::iter::Cloned<Iter<'a, u8>>>);
384
385#[cfg(feature = "utf")]
386impl<'a> Iterator for Chars<'a> {
387    type Item = char;
388    #[inline]
389    fn next(&mut self) -> Option<char> {
390        use unreachable::UncheckedResultExt;
391        self.0.next().map(|r| unsafe { r.unchecked_unwrap_ok() })
392    }
393}
394
395/// Iterator over the characters of a null-terminated string and their byte positions
396#[cfg(feature = "utf")]
397#[derive(Debug, Clone)]
398pub struct CharIndices<'a>(Chars<'a>, usize);
399
400#[cfg(feature = "utf")]
401impl<'a> Iterator for CharIndices<'a> {
402    type Item = (usize, char);
403    #[inline]
404    fn next(&mut self) -> Option<(usize, char)> {
405        let x = self.0.next()?;
406        let k = self.1;
407        self.1 += x.len_utf8();
408        Some((k, x))
409    }
410}
411
412impl<'a> TryFrom<&'a Nul<u8>> for &'a NulStr {
413    type Error = ::core::str::Utf8Error;
414
415    #[inline]
416    fn try_from(s: &'a Nul<u8>) -> Result<Self, Self::Error> {
417        ::core::str::from_utf8(&s[..])?;
418        Ok(unsafe { NulStr::new_unchecked(s.as_ptr()) })
419    }
420}
421
422impl<'a> TryFrom<&'a mut Nul<u8>> for &'a mut NulStr {
423    type Error = ::core::str::Utf8Error;
424
425    #[inline]
426    fn try_from(s: &'a mut Nul<u8>) -> Result<Self, Self::Error> {
427        ::core::str::from_utf8_mut(&mut s[..])?;
428        Ok(unsafe { NulStr::new_unchecked_mut(s.as_mut_ptr()) })
429    }
430}
431
432#[inline]
433fn is_null<A>(a: &A) -> bool { unsafe {
434    let l = mem::size_of_val(a);
435    let p = a as *const A as *const u8;
436    slice::from_raw_parts(p, l).iter().all(|&b| 0 == b)
437} }
438
439#[inline]
440fn ptr_diff<A>(p: *const A, q: *const A) -> usize {
441    use ::core::num::Wrapping as w;
442    (w(p as usize) - w(q as usize)).0/mem::size_of::<A>()
443}
444
445/// Make a static `Nul<u8>`.
446///
447/// # Examples
448///
449/// ```
450/// # extern crate null_terminated; use null_terminated::{Nul, str0}; fn main() {
451/// static s: &'static Nul<u8> = str0!("Hello, world!");
452/// # }
453/// ```
454#[macro_export]
455macro_rules! str0 {
456    ($s:expr) => (unsafe {
457        $crate::Nul::<u8>::new_unchecked($crate::__byte_strings::concat_bytes!([$crate] $s, "\0").as_ptr() as *mut _)
458    })
459}
460
461/// Make a static `NulStr`.
462///
463/// # Examples
464///
465/// ```
466/// # extern crate null_terminated; use null_terminated::{NulStr, str0_utf8}; fn main() {
467/// static s: &'static NulStr = str0_utf8!("Hello, world!");
468/// # }
469/// ```
470#[macro_export]
471macro_rules! str0_utf8 {
472    ($s:expr) => (unsafe {
473        $crate::NulStr::new_unchecked(concat!($s, "\0").as_ptr() as *mut _)
474    })
475}
476
477
478/// Constructs a `&Nul<&T>`.
479///
480/// # Examples
481///
482/// ```
483/// # extern crate null_terminated;
484/// # use null_terminated::{Nul,NulStr,nul_of_ref,str0_utf8};
485///
486/// static INTS: &Nul<&u32> = nul_of_ref![&0, &1, &2, &3];
487/// static STRS: &Nul<&NulStr> =
488///     nul_of_ref![str0_utf8!("foo"), str0_utf8!("bar"), str0_utf8!("baz")];
489///
490/// # fn main() {
491/// assert_eq!( INTS[..], [&0, &1, &2, &3] );
492/// assert_eq!( STRS[..], [str0_utf8!("foo"), str0_utf8!("bar"), str0_utf8!("baz")] );
493/// # }
494/// ```
495///
496#[macro_export]
497macro_rules! nul_of_ref {
498    ($($reference:expr),* $(,)?) => (unsafe {
499        enum Opt<T> { Nil, Just(T) }
500        #[inline(always)]
501        const unsafe fn cast_helper<'a, 'b, A: ?Sized>(a: &'b Nul<Opt<&'a A>>) -> &'b Nul<&'a A> { $crate::cast(a) }
502        cast_helper($crate::Nul::new_unchecked([$(Opt::Just($reference),)* Opt::Nil].as_ptr()))
503    })
504}
505
506/// ```compile_fail
507/// # extern crate null_terminated;
508/// # use null_terminated::{Nul,nul_of_ref};
509/// static VOIDS: &Nul<&std::convert::Infallible> = nul_of_ref![&()];
510/// ```
511/// ```compile_fail
512/// # extern crate null_terminated;
513/// # use null_terminated::{Nul,nul_of_ref};
514/// static VOIDS: &Nul<&std::convert::Infallible> = nul_of_ref![&0usize];
515/// ```
516/// ```compile_fail
517/// # extern crate null_terminated;
518/// # use null_terminated::{Nul,nul_of_ref};
519/// static BOOLS: &Nul<&bool> = nul_of_ref![&()];
520/// ```
521#[cfg(doctest)]
522pub struct NoNulOfRefInvalid;
523
524#[cfg(test)]
525mod tests {
526    use super::*;
527
528    #[quickcheck]
529    fn test(mut xs: ::std::vec::Vec<usize>) -> bool {
530        xs.push(0);
531        let l = xs.iter().take_while(|&&x| 0usize != x).count();
532        xs[0..l] == <&Nul<_>>::try_from(&xs[..]).unwrap()[..]
533    }
534
535    #[quickcheck]
536    fn iter(mut xs: ::std::vec::Vec<usize>) -> bool {
537        xs.push(0);
538        let l = xs.iter().take_while(|&&x| 0usize != x).count();
539        Iterator::eq(xs.iter().take(l), <&Nul<_>>::try_from(&xs[..]).unwrap().iter())
540    }
541}