use std::borrow::BorrowMut; use std::cmp::Ordering; use std::fmt; use std::ops::{Index, IndexMut, Range, RangeTo, RangeFrom, RangeFull}; use bow::Bow; use len_trait::{Len, SplitAtMut}; use push_trait::PushBack; macro_rules! gen_impl { ($( $name:ident, $slice_name:ident, $str_name:ident, $c_str_name:ident, $os_str_name:ident, $n:expr, )*) => { $( /// Array of immutable strings stored on the heap in the same buffer. pub struct $name { buffer: ::bow::Bow<'static, T::Data>, split: [usize; $n], } impl<'a, T: $crate::StrLike + ?Sized> From<[&'a T; $n]> for $name { fn from(inner: [&'a T; $n]) -> $name { $name::new(inner) } } impl Default for $name { fn default() -> $name { let def: &'static T = ::extra_default::DefaultRef::default_ref(); let data = def.to_data(); let len = data.len(); let mut buffer = data.to_owned(); let mut split = [len; $n]; let mut acc = 0; for s in &mut split { *s = acc; acc += len; buffer.push_back(data); } $name { buffer: Bow::Boxed(buffer.into()), split } } } impl $name { /// Creates a new `Static` from the given array of values. pub fn new(inner: [&T; $n]) -> $name { let inner: &[&T] = &inner; let mut buffer: T::OwnedData = Default::default(); for item in inner.iter() { buffer.push_back(item.to_data()); } let buffer: Box = buffer.into(); let buffer: Bow<'static, T::Data> = buffer.into(); let mut split = [0; $n]; inner.iter().map(|s| s.len()).enumerate().fold(0, |mut curr, (i, len)| { curr += len; split[i] = curr; curr }); $name { buffer, split } } /// Creates a `Static` from its raw parts: a buffer and a list of split indices. #[inline] pub fn from_raw(buffer: D, split: [usize; $n]) -> $name where D: Into> { let buffer = buffer.into(); let check = $crate::Split::new(&split); check.check_valid(buffer.len()) .unwrap_or_else(|e| panic!("split indices were invalid: {}", e)); for idx in 0..$n { T::from_data(check.get(idx).index_into(&*buffer)) .unwrap_or_else(|e| panic!("string {} was not valid: {}", idx, e)); } $name { buffer, split } } /// Creates a `Static` from its raw parts (unsafe version). #[inline] pub unsafe fn from_raw_unchecked(buffer: D, split: [usize; $n]) -> $name where D: Into>, { let buffer = buffer.into(); $name { buffer, split } } /// Returns an iterator over the elements in this `Static`. #[inline] pub fn iter(&self) -> $crate::Iter { $crate::Iter::new(&*self.buffer, &self.split) } } impl Index for $name { type Output = T; fn index(&self, index: usize) -> &T { assert_ne!(index, $n); unsafe { let split = $crate::Split::new(&self.split); T::from_data_unchecked(split.get(index).index_into(&self.buffer)) } } } impl IndexMut for $name where T::Data: SplitAtMut, T::OwnedData: BorrowMut { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { assert_ne!(index, $n); unsafe { let idx = $crate::Split::new(&self.split).get(index); T::from_data_mut_unchecked( idx.index_into_mut(self.buffer.to_mut().borrow_mut()) ) } } } impl Index> for $name { type Output = T; #[inline] fn index(&self, range: Range) -> &T { unsafe { let split = $crate::Split::new(&self.split); T::from_data_unchecked( split.get_slice(range.into()).index_into(&self.buffer) ) } } } impl Index> for $name { type Output = T; #[inline] fn index(&self, range: RangeFrom) -> &T { unsafe { let split = $crate::Split::new(&self.split); T::from_data_unchecked( split.get_slice(range.into()).index_into(&self.buffer) ) } } } impl Index> for $name { type Output = T; #[inline] fn index(&self, range: RangeTo) -> &T { unsafe { let split = $crate::Split::new(&self.split); T::from_data_unchecked( split.get_slice(range.into()).index_into(&self.buffer) ) } } } impl Index for $name { type Output = T; #[inline] fn index(&self, _: RangeFull) -> &T { unsafe { T::from_data_unchecked(&self.buffer) } } } impl Clone for $name where Box: Clone { fn clone(&self) -> $name { $name { buffer: self.buffer.clone(), split: self.split.clone(), } } fn clone_from(&mut self, source: &$name) { self.buffer.clone_from(&source.buffer); self.split.clone_from(&source.split); } } impl ::std::hash::Hash for $name where T::Data: ::std::hash::Hash { fn hash(&self, state: &mut H) { self.buffer.hash(state); self.split.hash(state); } } impl PartialEq for $name { fn eq(&self, rhs: &$name) -> bool { self.iter().eq(rhs.iter()) } } impl Eq for $name {} impl PartialOrd for $name { fn partial_cmp(&self, rhs: &$name) -> Option { self.iter().partial_cmp(rhs.iter()) } fn lt(&self, rhs: &$name) -> bool { self.iter().lt(rhs.iter()) } fn le(&self, rhs: &$name) -> bool { self.iter().le(rhs.iter()) } fn gt(&self, rhs: &$name) -> bool { self.iter().gt(rhs.iter()) } fn ge(&self, rhs: &$name) -> bool { self.iter().ge(rhs.iter()) } } impl Ord for $name { fn cmp(&self, rhs: &$name) -> Ordering { self.iter().cmp(rhs.iter()) } } impl fmt::Debug for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_list() .entries(self.iter()) .finish() } } /// Array of immutable slices stored on the heap in the same buffer. pub type $slice_name = $name<[T]>; /// Array of immutable `str`s stored on the heap in the same buffer. pub type $str_name = $name; /// Array of immutable `CStr`s stored on the heap in the same buffer. pub type $c_str_name = $name<::std::ffi::CStr>; ///// Array of immutable `OsStr`s stored on the heap in the same buffer. //pub type $os_str_name = $name<::std::ffi::OsStr>; )* } } gen_impl! { Static2, SliceArray2, StringArray2, CStringArray2, OsStringArray2, 2, Static3, SliceArray3, StringArray3, CStringArray3, OsStringArray3, 3, Static4, SliceArray4, StringArray4, CStringArray4, OsStringArray4, 4, Static5, SliceArray5, StringArray5, CStringArray5, OsStringArray5, 5, Static6, SliceArray6, StringArray6, CStringArray6, OsStringArray6, 6, Static7, SliceArray7, StringArray7, CStringArray7, OsStringArray7, 7, Static8, SliceArray8, StringArray8, CStringArray8, OsStringArray8, 8, Static9, SliceArray9, StringArray9, CStringArray9, OsStringArray9, 9, Static10, SliceArray10, StringArray10, CStringArray10, OsStringArray10, 10, Static11, SliceArray11, StringArray11, CStringArray11, OsStringArray11, 11, Static12, SliceArray12, StringArray12, CStringArray12, OsStringArray12, 12, Static13, SliceArray13, StringArray13, CStringArray13, OsStringArray13, 13, Static14, SliceArray14, StringArray14, CStringArray14, OsStringArray14, 14, Static15, SliceArray15, StringArray15, CStringArray15, OsStringArray15, 15, Static16, SliceArray16, StringArray16, CStringArray16, OsStringArray16, 16, } #[cfg(test)] mod tests { use std::ffi::CStr; use super::Static3; #[test] fn debug() { let array = Static3::new(["English", "Français", "中文"]); assert_eq!( format!("{:?}", array), r#"["English", "Français", "中文"]"# ) } #[test] #[should_panic] fn panic_oob() { let array = >::default(); let _ = &array[3]; } #[test] #[should_panic] fn panic_oob_str() { let array = >::default(); let _ = &array[3]; } #[test] #[should_panic] fn panic_oob_c_str() { let array = >::default(); let _ = &array[3]; } #[test] fn index() { let array = Static3::new(["English", "Français", "中文"]); assert_eq!(&array[0], "English"); assert_eq!(&array[1], "Français"); assert_eq!(&array[2], "中文"); assert_eq!(&array[0..0], ""); assert_eq!(&array[0..1], "English"); assert_eq!(&array[0..2], "EnglishFrançais"); assert_eq!(&array[0..3], "EnglishFrançais中文"); assert_eq!(&array[1..1], ""); assert_eq!(&array[1..2], "Français"); assert_eq!(&array[1..3], "Français中文"); assert_eq!(&array[2..2], ""); assert_eq!(&array[2..3], "中文"); assert_eq!(&array[3..3], ""); assert_eq!(&array[0..], "EnglishFrançais中文"); assert_eq!(&array[1..], "Français中文"); assert_eq!(&array[2..], "中文"); assert_eq!(&array[3..], ""); assert_eq!(&array[..0], ""); assert_eq!(&array[..1], "English"); assert_eq!(&array[..2], "EnglishFrançais"); assert_eq!(&array[..3], "EnglishFrançais中文"); assert_eq!(&array[..], "EnglishFrançais中文"); } #[test] #[should_panic] fn panic_left_oob() { let array = Static3::new(["English", "Français", "中文"]); let _ = &array[4..]; } #[test] #[should_panic] fn panic_right_oob() { let array = Static3::new(["English", "Français", "中文"]); let _ = &array[..4]; } }