| use std::borrow::Borrow; | ||
| use std::fmt; | ||
| use std::hash::{Hash, Hasher}; | ||
| 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<A: Array<Item=u8>> { | ||
| xs: A, | ||
| len: A::Index, | ||
| } | ||
| impl<A: Array<Item=u8>> ArrayString<A> { | ||
| /// 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<A> { | ||
| 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.unwrap_err().element(), 'c'); | ||
| /// ``` | ||
| pub fn push(&mut self, c: char) -> Result<(), CapacityError<char>> { | ||
| 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.unwrap_err().element(), "bc"); | ||
| /// assert_eq!(overflow2.unwrap_err().element(), "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<A: Array<Item=u8>> Deref for ArrayString<A> { | ||
| 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<A: Array<Item=u8>> PartialEq for ArrayString<A> { | ||
| fn eq(&self, rhs: &Self) -> bool { | ||
| **self == **rhs | ||
| } | ||
| } | ||
| impl<A: Array<Item=u8>> PartialEq<str> for ArrayString<A> { | ||
| fn eq(&self, rhs: &str) -> bool { | ||
| &**self == rhs | ||
| } | ||
| } | ||
| impl<A: Array<Item=u8>> PartialEq<ArrayString<A>> for str { | ||
| fn eq(&self, rhs: &ArrayString<A>) -> bool { | ||
| self == &**rhs | ||
| } | ||
| } | ||
| impl<A: Array<Item=u8>> Eq for ArrayString<A> { } | ||
| impl<A: Array<Item=u8>> Hash for ArrayString<A> { | ||
| fn hash<H: Hasher>(&self, h: &mut H) { | ||
| (**self).hash(h) | ||
| } | ||
| } | ||
| impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> { | ||
| fn borrow(&self) -> &str { self } | ||
| } | ||
| impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> { | ||
| fn as_ref(&self) -> &str { self } | ||
| } | ||
| impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } | ||
| } | ||
| impl<A: Array<Item=u8>> fmt::Display for ArrayString<A> { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } | ||
| } | ||
| /// `Write` appends written data to the end of the string. | ||
| impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> { | ||
| fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| self.push_str(s).map_err(|_| fmt::Error) | ||
| } | ||
| } | ||
| impl<A: Array<Item=u8> + Copy> Clone for ArrayString<A> { | ||
| fn clone(&self) -> ArrayString<A> { | ||
| *self | ||
| } | ||
| } |
+3
-3
| [package] | ||
| name = "arrayvec" | ||
| version = "0.3.11" | ||
| version = "0.3.12" | ||
| authors = ["bluss"] | ||
| license = "MIT/Apache-2.0" | ||
| description = "A vector with a fixed capacity, it can be stored on the stack too." | ||
| description = "A vector with a fixed capacity, it can be stored on the stack too. Implements fixed capacity ArrayVec and ArrayString." | ||
| documentation = "http://bluss.github.io/arrayvec" | ||
| repository = "https://github.com/bluss/arrayvec" | ||
| keywords = ["stack", "vector", "array", "container", "data-structure"] | ||
| keywords = ["stack", "vector", "array", "string", "data-structure"] | ||
@@ -13,0 +13,0 @@ [dependencies.odds] |
+4
-0
@@ -25,2 +25,6 @@ | ||
| - 0.3.12 | ||
| - Added ArrayString, a fixed capacity analogy of String | ||
| - 0.3.11 | ||
@@ -27,0 +31,0 @@ |
+0
-5
@@ -18,3 +18,2 @@ | ||
| pub trait Index : PartialEq + Copy { | ||
| fn zero() -> Self; | ||
| fn to_usize(self) -> usize; | ||
@@ -26,4 +25,2 @@ fn from(usize) -> Self; | ||
| #[inline(always)] | ||
| fn zero() -> Self { 0 } | ||
| #[inline(always)] | ||
| fn to_usize(self) -> usize { self as usize } | ||
@@ -36,4 +33,2 @@ #[inline(always)] | ||
| #[inline(always)] | ||
| fn zero() -> Self { 0 } | ||
| #[inline(always)] | ||
| fn to_usize(self) -> usize { self as usize } | ||
@@ -40,0 +35,0 @@ #[inline(always)] |
+46
-2
@@ -18,3 +18,5 @@ extern crate odds; | ||
| // extra traits | ||
| use std::any::Any; | ||
| use std::borrow::{Borrow, BorrowMut}; | ||
| use std::error::Error; | ||
| use std::hash::{Hash, Hasher}; | ||
@@ -24,5 +26,8 @@ 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; | ||
@@ -83,3 +88,3 @@ | ||
| unsafe { | ||
| ArrayVec { xs: NoDrop::new(new_array()), len: Index::zero() } | ||
| ArrayVec { xs: NoDrop::new(new_array()), len: Index::from(0) } | ||
| } | ||
@@ -428,3 +433,3 @@ } | ||
| fn into_iter(self) -> IntoIter<A> { | ||
| IntoIter { index: Index::zero(), v: self, } | ||
| IntoIter { index: Index::from(0), v: self, } | ||
| } | ||
@@ -700,1 +705,40 @@ } | ||
| } | ||
| /// Error value indicating insufficient capacity | ||
| #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] | ||
| pub struct CapacityError<T> { | ||
| element: T, | ||
| } | ||
| impl<T> CapacityError<T> { | ||
| fn new(element: T) -> CapacityError<T> { | ||
| CapacityError { | ||
| element: element, | ||
| } | ||
| } | ||
| /// Extract the overflowing element | ||
| pub fn element(self) -> T { | ||
| self.element | ||
| } | ||
| } | ||
| const CAPERROR: &'static str = "insufficient capacity"; | ||
| impl<T: Any> Error for CapacityError<T> { | ||
| fn description(&self) -> &str { | ||
| CAPERROR | ||
| } | ||
| } | ||
| impl<T> fmt::Display for CapacityError<T> { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| write!(f, "{}", CAPERROR) | ||
| } | ||
| } | ||
| impl<T> fmt::Debug for CapacityError<T> { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| write!(f, "{}: {}", "CapacityError", CAPERROR) | ||
| } | ||
| } |
+31
-0
| extern crate arrayvec; | ||
| use arrayvec::ArrayVec; | ||
| use arrayvec::ArrayString; | ||
| use std::mem; | ||
| use std::collections::HashMap; | ||
| #[test] | ||
@@ -263,1 +266,29 @@ fn test_simple() { | ||
| } | ||
| #[test] | ||
| fn test_string() { | ||
| use std::error::Error; | ||
| let text = "hello world"; | ||
| let mut s = ArrayString::<[_; 16]>::new(); | ||
| s.push_str(text).unwrap(); | ||
| assert_eq!(&s, text); | ||
| assert_eq!(text, &s); | ||
| // Make sure Hash / Eq / Borrow match up so we can use HashMap | ||
| let mut map = HashMap::new(); | ||
| map.insert(s, 1); | ||
| assert_eq!(map[text], 1); | ||
| let mut t = ArrayString::<[_; 2]>::new(); | ||
| assert!(t.push_str(text).is_err()); | ||
| assert_eq!(&t, ""); | ||
| // Test Error trait / try | ||
| let t = || -> Result<(), Box<Error>> { | ||
| let mut t = ArrayString::<[_; 2]>::new(); | ||
| try!(t.push_str(text)); | ||
| Ok(()) | ||
| }(); | ||
| assert!(t.is_err()); | ||
| } |