diff --git a/src/array_string.rs b/src/array_string.rs new file mode 100644 index 0000000..9552013 --- /dev/null +++ b/src/array_string.rs @@ -0,0 +1,177 @@ +use std::borrow::Borrow; +use std::fmt; +use std::mem; +use std::ops::Deref; +use std::str; +use std::slice; + +use array::Array; +use array::Index; +use CapacityError; + +/// A string with a fixed capacity. +/// +/// The `ArrayString` is a string backed by a fixed size array. It keeps track +/// of its length. +/// +/// The string is a contiguous value that you can store directly on the stack +/// if needed. +#[derive(Copy)] +pub struct ArrayString> { + xs: A, + len: A::Index, +} + +unsafe fn new_array>() -> A { + // Note: Returning an uninitialized value here only works + // if we can be sure the data is never used. The nullable pointer + // inside enum optimization conflicts with this this for example, + // so we need to be extra careful. See `NoDrop` enum. + mem::uninitialized() +} + +impl> ArrayString { + /// Create a new empty `ArrayString`. + /// + /// Capacity is inferred from the type parameter. + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let mut string = ArrayString::<[_; 16]>::new(); + /// string.push_str("foo"); + /// assert_eq!(&string[..], "foo"); + /// assert_eq!(string.capacity(), 16); + /// ``` + pub fn new() -> ArrayString { + unsafe { + ArrayString { + xs: new_array(), + len: Index::from(0), + } + } + } + + /// Return the capacity of the `ArrayString`. + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let string = ArrayString::<[_; 3]>::new(); + /// assert_eq!(string.capacity(), 3); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { A::capacity() } + + /// Adds the given char to the end of the string. + /// + /// Returns `Ok` if the push succeeds, and returns `Err` if the backing + /// array is not large enough to fit the additional char. + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let mut string = ArrayString::<[_; 2]>::new(); + /// + /// string.push('a').unwrap(); + /// string.push('b').unwrap(); + /// let overflow = string.push('c'); + /// + /// assert_eq!(&string[..], "ab"); + /// assert_eq!(overflow.err().map(|e| e.element), Some('c')); + /// ``` + pub fn push(&mut self, c: char) -> Result<(), CapacityError> { + use std::fmt::Write; + self.write_char(c).map_err(|_| CapacityError::new(c)) + } + + /// Adds the given string slice to the end of the string. + /// + /// Returns `Ok` if the push succeeds, and returns `Err` if the + /// backing array is not large enough to fit the string. + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let mut string = ArrayString::<[_; 2]>::new(); + /// + /// string.push_str("a").unwrap(); + /// let overflow1 = string.push_str("bc"); + /// string.push_str("d").unwrap(); + /// let overflow2 = string.push_str("ef"); + /// + /// assert_eq!(&string[..], "ad"); + /// assert_eq!(overflow1.err().map(|e| e.element), Some("bc")); + /// assert_eq!(overflow2.err().map(|e| e.element), Some("ef")); + /// ``` + pub fn push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> { + use std::io::Write; + + if self.len() + s.len() > self.capacity() { + return Err(CapacityError::new(s)); + } + unsafe { + let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), A::capacity()); + (&mut sl[self.len()..]).write(s.as_bytes()).unwrap(); + let newl = self.len() + s.len(); + self.set_len(newl); + } + Ok(()) + } + + /// Make the string empty. + pub fn clear(&mut self) { + unsafe { + self.set_len(0); + } + } + + /// Set the strings's length. + /// + /// May panic if `length` is greater than the capacity. + /// + /// This function is `unsafe` because it changes the notion of the + /// number of “valid” bytes in the string. Use with care. + #[inline] + pub unsafe fn set_len(&mut self, length: usize) { + debug_assert!(length <= self.capacity()); + self.len = Index::from(length); + } +} + +impl> Deref for ArrayString { + type Target = str; + #[inline] + fn deref(&self) -> &str { + unsafe { + let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize()); + str::from_utf8_unchecked(sl) + } + } +} + +impl> Borrow for ArrayString { + fn borrow(&self) -> &str { self } +} + +impl> AsRef for ArrayString { + fn as_ref(&self) -> &str { self } +} + +impl> fmt::Debug for ArrayString where A::Item: fmt::Debug { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } +} + +/// `Write` appends written data to the end of the string. +impl> fmt::Write for ArrayString { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push_str(s).map_err(|_| fmt::Error) + } +} + +impl + Copy> Clone for ArrayString { + fn clone(&self) -> ArrayString { + *self + } +} + diff --git a/src/lib.rs b/src/lib.rs index b1679b8..73d9324 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,12 @@ use std::hash::{Hash, Hasher}; use std::fmt; mod array; +mod array_string; + pub use array::Array; pub use odds::IndexRange as RangeArgument; use array::Index; +pub use array_string::ArrayString; unsafe fn new_array() -> A { @@ -33,6 +36,21 @@ unsafe fn new_array() -> A { mem::uninitialized() } +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct CapacityError { + _unused: (), + pub element: T, +} + +impl CapacityError { + fn new(element: T) -> CapacityError { + CapacityError { + _unused: (), + element: element, + } + } +} + /// A vector with a fixed capacity. /// /// The `ArrayVec` is a vector backed by a fixed size array. It keeps track of