From 69b0954d96abd923410ba033430e9e019681684e Mon Sep 17 00:00:00 2001 From: polazarus Date: Sat, 22 Jun 2024 22:23:08 +0200 Subject: [PATCH] fix impl and add more tests --- src/bytes.rs | 28 +-- src/bytes/tests.rs | 372 +++++++++++++++++++++--------------- src/string.rs | 57 +++--- src/string/tests.rs | 452 +++++++++++++++++++++++++++----------------- 4 files changed, 548 insertions(+), 361 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 4b7e7a0..f5f5cf3 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -731,8 +731,14 @@ where Self(raw) } - pub fn concat(slices: impl IntoIterator> + Clone) -> Self { - let new_len = slices.clone().into_iter().map(|e| e.as_ref().len()).sum(); + pub fn concat(slices: I) -> Self + where + E: AsRef<[u8]>, + I: IntoIterator, + I::IntoIter: Clone, + { + let mut slices = slices.into_iter(); + let new_len = slices.clone().map(|e| e.as_ref().len()).sum(); let mut raw = Raw::with_capacity(new_len); let dst = raw.spare_capacity_mut(); @@ -741,7 +747,7 @@ where // compute the final pointer let final_ptr = unsafe { dst_ptr.add(new_len) }; - let _ = slices.into_iter().fold(dst_ptr, |dst_ptr, slice| { + let _ = slices.fold(dst_ptr, |dst_ptr, slice| { let slice = slice.as_ref(); let len = slice.len(); let end_ptr = unsafe { dst_ptr.add(len) }; @@ -818,13 +824,15 @@ where Self(raw) } - pub fn join( - slices: impl IntoIterator> + Clone, - sep: impl AsRef<[u8]>, - ) -> Self { - let (segments, segments_len) = slices + pub fn join(slices: I, sep: impl AsRef<[u8]>) -> Self + where + E: AsRef<[u8]>, + I: IntoIterator, + I::IntoIter: Clone, + { + let mut iter = slices.into_iter(); + let (segments, segments_len) = iter .clone() - .into_iter() .fold((0, 0), |(n, l), e| (n + 1, l + e.as_ref().len())); if segments == 0 { return Self::new(); @@ -840,8 +848,6 @@ where // compute the final pointer let final_ptr = unsafe { dst_ptr.add(new_len) }; - let mut iter = slices.into_iter(); - if let Some(slice) = iter.next() { let slice = slice.as_ref(); let len = slice.len(); diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 3b96c35..39c7ef4 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -1,3 +1,5 @@ +use alloc::rc::Rc; +use core::cell::Cell; use core::ops::Bound; use core::ptr; #[cfg(feature = "std")] @@ -9,18 +11,24 @@ use fastrand::Rng; use super::{simplify_range, SliceErrorKind}; use crate::alloc::vec::Vec; use crate::alloc::{format, vec}; -use crate::HipByt; +use crate::HipByt as H; -const INLINE_CAPACITY: usize = HipByt::inline_capacity(); +const INLINE_CAPACITY: usize = H::inline_capacity(); + +const EMPTY_SLICE: &[u8] = &[]; +const ABC: &[u8] = b"abc"; +const A: &[u8] = b"a"; +const B: &[u8] = b"b"; +const C: &[u8] = b"c"; #[test] fn test_new_default() { - let new = HipByt::new(); - assert_eq!(new, &[]); + let new = H::new(); + assert_eq!(new, EMPTY_SLICE); assert!(new.is_empty()); - let new = HipByt::default(); - assert_eq!(new, &[]); + let new = H::default(); + assert_eq!(new, EMPTY_SLICE); assert!(new.is_empty()); } @@ -28,24 +36,24 @@ fn test_new_default() { #[cfg(feature = "std")] fn test_borrow_and_hash() { let mut set = HashSet::new(); - set.insert(HipByt::from(b"a")); - set.insert(HipByt::from(b"b")); + set.insert(H::from(A)); + set.insert(H::from(B)); - assert!(set.contains(b"a".as_slice())); - assert!(!set.contains(b"c".as_slice())); + assert!(set.contains(A)); + assert!(!set.contains(C)); } #[test] fn test_debug() { let slice = &[1, 2, 3]; - let bytes = HipByt::from(slice); + let bytes = H::from(slice); assert_eq!(format!("{slice:?}"), format!("{bytes:?}")); } #[test] fn test_from_vec() { let v = vec![42; 42]; - let bytes = HipByt::from(v); + let bytes = H::from(v); assert!(!bytes.is_inline()); assert!(!bytes.is_borrowed()); assert!(bytes.is_allocated()); @@ -53,7 +61,7 @@ fn test_from_vec() { assert_eq!(bytes.as_slice(), [42; 42]); let v = vec![0; 3]; - let bytes = HipByt::from(v); + let bytes = H::from(v); assert!(bytes.is_inline()); assert!(!bytes.is_borrowed()); assert!(!bytes.is_allocated()); @@ -66,7 +74,7 @@ fn test_borrowed() { static V: &[u8] = &[42; 1024]; for size in [0, 1, INLINE_CAPACITY, INLINE_CAPACITY + 1, 256, 1024] { - let bytes = HipByt::borrowed(&V[..size]); + let bytes = H::borrowed(&V[..size]); assert!(!bytes.is_inline()); assert!(bytes.is_borrowed()); assert!(!bytes.is_allocated()); @@ -80,17 +88,17 @@ fn test_from_static() { fn is_static_type(_: &T) {} let s = b"abcdefghijklmnopqrstuvwxyz"; - let bytes = HipByt::from_static(s); + let h = H::from_static(s); // compiler check - is_static_type(&bytes); - - assert!(bytes.is_borrowed()); - assert!(!bytes.is_inline()); - assert!(!bytes.is_allocated()); - assert_eq!(bytes.len(), s.len()); - assert_eq!(bytes.as_slice(), s); - assert_eq!(bytes.as_ptr(), s.as_ptr()); + is_static_type(&h); + + assert!(h.is_borrowed()); + assert!(!h.is_inline()); + assert!(!h.is_allocated()); + assert_eq!(h.len(), s.len()); + assert_eq!(h.as_slice(), s); + assert_eq!(h.as_ptr(), s.as_ptr()); } #[test] @@ -98,10 +106,10 @@ fn test_from_slice() { static V: &[u8] = &[42; 1024]; for size in [0, 1, INLINE_CAPACITY, INLINE_CAPACITY + 1, 256, 1024] { - let bytes = HipByt::from(&V[..size]); - assert_eq!(size <= INLINE_CAPACITY, bytes.is_inline()); - assert_eq!(size > INLINE_CAPACITY, bytes.is_allocated()); - assert_eq!(bytes.len(), size); + let h = H::from(&V[..size]); + assert_eq!(size <= INLINE_CAPACITY, h.is_inline()); + assert_eq!(size > INLINE_CAPACITY, h.is_allocated()); + assert_eq!(h.len(), size); } } @@ -109,23 +117,23 @@ fn test_from_slice() { fn test_as_slice() { // static { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); assert!(a.is_borrowed()); assert!(!a.is_inline()); assert!(!a.is_allocated()); - assert_eq!(a.as_slice(), b"abc"); + assert_eq!(a.as_slice(), ABC); } // inline { - let a = HipByt::from(b"abc".as_slice()); + let a = H::from(ABC); assert!(!a.is_borrowed()); assert!(a.is_inline()); assert!(!a.is_allocated()); - assert_eq!(a.as_slice(), b"abc"); + assert_eq!(a.as_slice(), ABC); } // allocated { - let a = HipByt::from(vec![42; 42]); + let a = H::from(vec![42; 42]); assert!(!a.is_borrowed()); assert!(!a.is_inline()); assert!(a.is_allocated()); @@ -137,29 +145,28 @@ fn test_as_slice() { fn test_clone() { // static { - let s: &'static [u8] = b"abc"; - let a = HipByt::borrowed(s); + let a = H::borrowed(ABC); assert!(a.is_borrowed()); let b = a.clone(); drop(a); - assert_eq!(b.as_slice(), s); - assert_eq!(s.as_ptr(), b.as_ptr()); + assert_eq!(b.as_slice(), ABC); + assert_eq!(b.as_ptr(), ABC.as_ptr()); } // inline { - let a = HipByt::from(b"abc".as_slice()); + let a = H::from(ABC); assert!(a.is_inline()); let b = a.clone(); drop(a); - assert_eq!(b.as_slice(), b"abc"); + assert_eq!(b.as_slice(), ABC); } // allocated { let v = vec![42; 42]; let p = v.as_ptr(); - let a = HipByt::from(v); + let a = H::from(v); assert!(a.is_allocated()); let b = a.clone(); drop(a); @@ -174,7 +181,7 @@ fn test_clone_drop() { let mut rand = Rng::with_seed(0); for n in [5, 10, 20, 100] { // println!("!n {n}"); - let mut vs = vec![HipByt::from(v.clone()); n]; + let mut vs = vec![H::from(v.clone()); n]; while !vs.is_empty() { // println!("len {}", vs.len()); @@ -199,16 +206,16 @@ fn test_clone_drop() { #[test] fn test_into_static() { // static - let a = HipByt::borrowed(b"abc"); - assert_eq!(a.into_borrowed(), Ok(b"abc".as_slice())); + let a = H::borrowed(ABC); + assert_eq!(a.into_borrowed(), Ok(ABC)); // inline - let a = HipByt::from(b"abc".as_slice()); + let a = H::from(ABC); let b = a.clone(); assert_eq!(a.into_borrowed(), Err(b)); // heap - let a = HipByt::from([42; 42].as_slice()); + let a = H::from([42; 42].as_slice()); let b = a.clone(); assert_eq!(a.into_borrowed(), Err(b)); } @@ -216,16 +223,16 @@ fn test_into_static() { #[test] fn test_as_mut_slice() { // static - let mut a = HipByt::borrowed(b"abc"); + let mut a = H::borrowed(ABC); assert_eq!(a.as_mut_slice(), None); // inline - let mut a = HipByt::from([42; 3].as_slice()); + let mut a = H::from([42; 3].as_slice()); assert!(a.is_inline()); assert_eq!(a.as_mut_slice(), Some([42; 3].as_mut_slice())); // heap - let mut a = HipByt::from([42; 42].as_slice()); + let mut a = H::from([42; 42].as_slice()); { let sl = a.as_mut_slice(); assert_eq!(sl, Some([42; 42].as_mut_slice())); @@ -240,19 +247,19 @@ fn test_as_mut_slice() { #[test] fn test_to_mut_slice() { // static - let mut a = HipByt::borrowed(b"abc"); + let mut a = H::borrowed(ABC); assert!(a.is_borrowed()); - assert_eq!(a.to_mut_slice(), b"abc".to_vec().as_mut_slice()); + assert_eq!(a.to_mut_slice(), ABC.to_vec().as_mut_slice()); assert!(a.is_inline()); // inline - let mut a = HipByt::from([42; 3].as_slice()); + let mut a = H::from([42; 3].as_slice()); assert!(a.is_inline()); assert_eq!(a.to_mut_slice(), [42; 3].as_mut_slice()); assert!(a.is_inline()); // heap - let mut a = HipByt::from([42; 42].as_slice()); + let mut a = H::from([42; 42].as_slice()); assert!(a.is_allocated()); { let sl = a.to_mut_slice(); @@ -269,7 +276,7 @@ fn test_to_mut_slice() { #[test] fn test_slice_inline() { let v: Vec<_> = (0..(INLINE_CAPACITY as u8)).collect(); - let s = HipByt::from(&v[..]); + let s = H::from(&v[..]); let sl = s.slice(0..10); assert_eq!(&sl, &v[0..10]); let sl = s.slice(..); @@ -280,7 +287,7 @@ fn test_slice_inline() { #[test] fn test_slice_borrowed() { let v: Vec<_> = (0..42).collect(); - let s = HipByt::borrowed(&v[..]); + let s = H::borrowed(&v[..]); let sl1 = s.slice(4..30); assert_eq!(&sl1, &v[4..30]); @@ -300,7 +307,7 @@ fn test_slice_borrowed() { #[test] fn test_slice_allocated() { let v: Vec<_> = (0..42).collect(); - let s = HipByt::from(&v[..]); + let s = H::from(&v[..]); assert!(s.is_allocated()); let sl1 = s.slice(4..30); @@ -319,32 +326,32 @@ fn test_slice_allocated() { #[test] #[should_panic] fn test_slice_panic_start() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _b = a.slice(4..); } #[test] #[should_panic] fn test_slice_panic_end() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _b = a.slice(..5); } #[test] #[should_panic] fn test_slice_panic_mixed() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _b = a.slice(3..2); } #[test] fn test_slice_unchecked() { use core::ops::Bound; - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); assert_eq!(unsafe { a.slice_unchecked(0..2) }, b"ab"); assert_eq!(unsafe { a.slice_unchecked(0..=1) }, b"ab"); assert_eq!(unsafe { a.slice_unchecked(..2) }, b"ab"); - assert_eq!(unsafe { a.slice_unchecked(..) }, b"abc"); + assert_eq!(unsafe { a.slice_unchecked(..) }, ABC); assert_eq!( unsafe { a.slice_unchecked((Bound::Excluded(0), Bound::Unbounded)) }, b"bc" @@ -355,7 +362,7 @@ fn test_slice_unchecked() { #[cfg(debug_assertions)] #[should_panic] fn test_slice_unchecked_debug_panic_start() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _ = unsafe { a.slice_unchecked(4..) }; } @@ -363,7 +370,7 @@ fn test_slice_unchecked_debug_panic_start() { #[cfg(debug_assertions)] #[should_panic] fn test_slice_unchecked_debug_panic_end() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _ = unsafe { a.slice_unchecked(..5) }; } @@ -371,19 +378,19 @@ fn test_slice_unchecked_debug_panic_end() { #[cfg(debug_assertions)] #[should_panic] fn test_slice_unchecked_debug_panic_mixed() { - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); let _ = unsafe { a.slice_unchecked(3..2) }; } -static ABCDEF: HipByt = HipByt::borrowed(b"abcdef"); +static ABCDEF: H = H::borrowed(b"abcdef"); #[test] fn test_slice_ok() { assert_eq!(ABCDEF.slice(..), b"abcdef"); - assert_eq!(ABCDEF.slice(..1), b"a"); + assert_eq!(ABCDEF.slice(..1), A); assert_eq!(ABCDEF.slice(..=1), b"ab"); - assert_eq!(ABCDEF.slice(1..2), b"b"); - assert_eq!(ABCDEF.slice((Bound::Excluded(0), Bound::Included(1))), b"b"); + assert_eq!(ABCDEF.slice(1..2), B); + assert_eq!(ABCDEF.slice((Bound::Excluded(0), Bound::Included(1))), B); } #[test] @@ -438,14 +445,14 @@ fn test_try_slice_ok() { ABCDEF .try_slice((Bound::Excluded(0), Bound::Included(1))) .unwrap(), - b"b" + B ); } #[test] fn test_empty_vec() { let source = vec![]; - let heap_zero = HipByt::from(source); + let heap_zero = H::from(source); assert!(heap_zero.is_normalized()); assert!(!heap_zero.is_allocated()); assert_eq!(heap_zero.len(), 0); @@ -455,13 +462,13 @@ fn test_empty_vec() { #[test] fn test_empty_slice() { // should normalize slice - let source1 = HipByt::from(vec![1, 2, 3]); + let source1 = H::from(vec![1, 2, 3]); let empty_slice1 = source1.slice(0..0); assert!(empty_slice1.is_normalized()); assert!(!empty_slice1.is_allocated()); assert!(empty_slice1.is_empty()); - let source2 = HipByt::from(&[1, 2, 3]); + let source2 = H::from(&[1, 2, 3]); let empty_slice2 = source2.slice(0..0); assert!(empty_slice2.is_normalized()); assert!(!empty_slice2.is_allocated()); @@ -472,13 +479,13 @@ fn test_empty_slice() { fn test_into_vec() { { // static - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); assert!(a.into_vec().is_err()); } { // inline - let a = HipByt::from(b"abc"); + let a = H::from(ABC); assert!(a.into_vec().is_err()); } @@ -487,7 +494,7 @@ fn test_into_vec() { // allocated, unique let v = v.clone(); let p = v.as_ptr(); - let a = HipByt::from(v); + let a = H::from(v); let v = a.into_vec().unwrap(); assert_eq!(p, v.as_ptr()); assert_eq!(INLINE_CAPACITY + 2, v.len()); @@ -495,7 +502,7 @@ fn test_into_vec() { { // allocated, shared - let a = HipByt::from(v.clone()); + let a = H::from(v.clone()); let _b = a.clone(); assert!(a.into_vec().is_err()); } @@ -504,7 +511,7 @@ fn test_into_vec() { // allocated, unique, sliced at start let v = v.clone(); let p = v.as_ptr(); - let a = HipByt::from(v).slice(0..INLINE_CAPACITY + 1); + let a = H::from(v).slice(0..INLINE_CAPACITY + 1); let v = a.into_vec().unwrap(); assert_eq!(v.len(), INLINE_CAPACITY + 1); assert_eq!(v.as_ptr(), p); @@ -512,7 +519,7 @@ fn test_into_vec() { { // allocated, unique, sliced at start - let a = HipByt::from(v).slice(1..5); + let a = H::from(v).slice(1..5); assert!(a.into_vec().is_err()); } } @@ -521,21 +528,21 @@ fn test_into_vec() { fn test_capacity() { { // static - let a = HipByt::borrowed(b"abc"); + let a = H::borrowed(ABC); assert_eq!(a.capacity(), a.len()); } { // inline - let a = HipByt::from(b"abc"); - assert_eq!(a.capacity(), HipByt::inline_capacity()); + let a = H::from(ABC); + assert_eq!(a.capacity(), H::inline_capacity()); } { // allocated let mut v = Vec::with_capacity(42); - v.extend_from_slice(&b"abc".repeat(10)); - let a = HipByt::from(v); + v.extend_from_slice(&ABC.repeat(10)); + let a = H::from(v); assert_eq!(a.capacity(), 42); let b = a.slice(1..); @@ -545,11 +552,11 @@ fn test_capacity() { #[test] fn test_mutate_borrowed() { - let mut a = HipByt::borrowed(b"abc"); + let mut a = H::borrowed(ABC); assert!(a.is_borrowed()); { let mut r = a.mutate(); - assert_eq!(r.as_slice(), b"abc"); + assert_eq!(r.as_slice(), ABC); r.extend_from_slice(b"def"); } assert!(!a.is_borrowed()); @@ -559,7 +566,7 @@ fn test_mutate_borrowed() { #[test] fn test_mutate_inline() { - let mut a = HipByt::from(b"abc"); + let mut a = H::from(ABC); assert!(a.is_inline()); a.mutate().extend_from_slice(b"def"); assert_eq!(a, b"abcdef"); @@ -573,7 +580,7 @@ fn test_mutate_allocated() { let mut v = Vec::with_capacity(42); v.extend_from_slice(b"abcdefghijklmnopqrstuvwxyz"); let p = v.as_ptr(); - let mut a = HipByt::from(v); + let mut a = H::from(v); assert!(a.is_allocated()); a.mutate().extend_from_slice(b"0123456789"); assert!(a.is_allocated()); @@ -586,7 +593,7 @@ fn test_mutate_allocated() { // allocated, shared let mut v = Vec::with_capacity(42); v.extend_from_slice(b"abcdefghijklmnopqrstuvwxyz"); - let mut a = HipByt::from(v); + let mut a = H::from(v); assert!(a.is_allocated()); let b = a.clone(); a.mutate().extend_from_slice(b"0123456789"); @@ -604,7 +611,7 @@ const FORTY_TWOS: &[u8] = &[42; 42]; fn test_push_slice_borrowed() { #[track_caller] fn should_inline(input: &[u8], addition: &[u8], expected: &[u8]) { - let mut a = HipByt::borrowed(input); + let mut a = H::borrowed(input); assert!(a.is_borrowed()); a.push_slice(addition); assert!(a.is_inline()); @@ -613,14 +620,14 @@ fn test_push_slice_borrowed() { #[track_caller] fn should_allocate(input: &[u8], addition: &[u8], expected: &[u8]) { - let mut a = HipByt::borrowed(input); + let mut a = H::borrowed(input); assert!(a.is_borrowed()); a.push_slice(addition); assert!(a.is_allocated()); assert_eq!(a, expected); } - should_inline(b"abc", b"def", b"abcdef"); + should_inline(ABC, b"def", b"abcdef"); for i in 0..(INLINE_CAPACITY - 1) { // add one byte to a byte string of variable length (< inline capacity) @@ -648,17 +655,17 @@ fn test_push_slice_borrowed() { &FORTY_TWOS[..INLINE_CAPACITY + 1], ); - let mut a = HipByt::borrowed(FORTY_TWOS); - a.push_slice(b"abc"); + let mut a = H::borrowed(FORTY_TWOS); + a.push_slice(ABC); assert_eq!(&a[..42], FORTY_TWOS); - assert_eq!(&a[42..], b"abc"); + assert_eq!(&a[42..], ABC); } #[test] fn test_push_slice_inline() { #[track_caller] fn should_stay_inline(input: &[u8], addition: &[u8], expected: &[u8]) { - let mut a = HipByt::from(input); + let mut a = H::from(input); assert!(a.is_inline()); a.push_slice(addition); assert!(a.is_inline()); @@ -666,18 +673,18 @@ fn test_push_slice_inline() { } #[track_caller] fn should_allocate(input: &[u8], addition: &[u8], expected: &[u8]) { - let mut a = HipByt::from(input); + let mut a = H::from(input); assert!(a.is_inline()); a.push_slice(addition); assert!(a.is_allocated()); assert_eq!(a, expected); } - should_stay_inline(b"abc", b"def", b"abcdef"); + should_stay_inline(ABC, b"def", b"abcdef"); - let mut a = HipByt::from(b"abc"); + let mut a = H::from(ABC); a.push_slice(FORTY_TWOS); - assert_eq!(&a[..3], b"abc"); + assert_eq!(&a[..3], ABC); assert_eq!(&a[3..], FORTY_TWOS); for i in 0..(INLINE_CAPACITY - 1) { @@ -710,34 +717,34 @@ fn test_push_slice_inline() { #[test] fn test_push_slice_allocated() { // allocated, unique - let mut a = HipByt::from(FORTY_TWOS); + let mut a = H::from(FORTY_TWOS); assert!(a.is_allocated()); - a.push_slice(b"abc"); + a.push_slice(ABC); assert_eq!(&a[0..42], FORTY_TWOS); - assert_eq!(&a[42..], b"abc"); + assert_eq!(&a[42..], ABC); // allocated, not unique - let mut a = HipByt::from(FORTY_TWOS); + let mut a = H::from(FORTY_TWOS); assert!(a.is_allocated()); let pa = a.as_ptr(); let b = a.clone(); assert_eq!(pa, b.as_ptr()); - a.push_slice(b"abc"); + a.push_slice(ABC); assert_ne!(a.as_ptr(), pa); assert_eq!(&a[0..42], FORTY_TWOS); - assert_eq!(&a[42..], b"abc"); + assert_eq!(&a[42..], ABC); assert_eq!(b, FORTY_TWOS); // allocated, unique but sliced let mut a = { - let x = HipByt::from(FORTY_TWOS); + let x = H::from(FORTY_TWOS); x.slice(1..39) }; assert!(a.is_allocated()); let p = a.as_ptr(); - a.push_slice(b"abc"); + a.push_slice(ABC); assert_eq!(&a[..38], &FORTY_TWOS[1..39]); - assert_eq!(&a[38..], b"abc"); + assert_eq!(&a[38..], ABC); assert_eq!(a.as_ptr(), p); // => the underlying vector is big enough } @@ -747,27 +754,27 @@ fn test_push() { // for now, push uses push_slice // so test can be minimal - let mut a = HipByt::from(b"abc"); + let mut a = H::from(ABC); a.push(b'd'); assert_eq!(a, b"abcd"); } #[test] fn test_to_owned() { - let b = b"abc"; - let h = HipByt::from(b); + let b = ABC; + let h = H::from(b); assert!(h.is_inline()); let h = h.into_owned(); assert!(h.is_inline()); let v = vec![42; 42]; - let a = HipByt::borrowed(&v[0..2]); + let a = H::borrowed(&v[0..2]); let a = a.into_owned(); drop(v); assert_eq!(a, [42, 42]); let v = vec![42; 42]; - let a = HipByt::from(&v[..]); + let a = H::from(&v[..]); drop(v); let p = a.as_ptr(); let a = a.into_owned(); @@ -776,72 +783,72 @@ fn test_to_owned() { #[test] fn test_make_ascii_lowercase() { - let mut h = HipByt::from(b"aB0\x80"); + let mut h = H::from(b"aB0\x80"); h.make_ascii_lowercase(); assert_eq!(h, b"ab0\x80"); let r = b"*".repeat(42); - let mut h = HipByt::from(&r[..]); + let mut h = H::from(&r[..]); h.make_ascii_lowercase(); assert_eq!(h, r); } #[test] fn test_to_ascii_lowercase() { - let h = HipByt::from(b"aB0\x80"); + let h = H::from(b"aB0\x80"); let h2 = h.to_ascii_lowercase(); assert_eq!(h2, b"ab0\x80"); assert_eq!(h, b"aB0\x80"); let r = b"*".repeat(42); - let h = HipByt::from(&r[..]); + let h = H::from(&r[..]); let h2 = h.to_ascii_lowercase(); assert_eq!(h2, r); } #[test] fn test_make_ascii_uppercase() { - let mut h = HipByt::from(b"aB0\x80"); + let mut h = H::from(b"aB0\x80"); h.make_ascii_uppercase(); assert_eq!(h, b"AB0\x80"); let r = b"*".repeat(42); - let mut h = HipByt::from(&r[..]); + let mut h = H::from(&r[..]); h.make_ascii_uppercase(); assert_eq!(h, r); } #[test] fn test_to_ascii_uppercase() { - let h = HipByt::from(b"aB0\x80"); + let h = H::from(b"aB0\x80"); let h2 = h.to_ascii_uppercase(); assert_eq!(h2, b"AB0\x80"); assert_eq!(h, b"aB0\x80"); let r = b"*".repeat(42); - let h = HipByt::from(&r[..]); + let h = H::from(&r[..]); let h2 = h.to_ascii_uppercase(); assert_eq!(h2, r); } #[test] fn test_repeat() { - let h = HipByt::new(); + let h = H::new(); let h50 = h.repeat(50); assert_eq!(h50.len(), 0); assert!(!h50.is_allocated()); - let h = HipByt::from(b"*".repeat(42)); + let h = H::from(b"*".repeat(42)); let h1 = h.repeat(1); assert_eq!(h1.len(), h.len()); assert_eq!(h.as_ptr(), h1.as_ptr()); - let h = HipByt::from(b"abc"); + let h = H::from(ABC); let h4 = h.repeat(2); - assert_eq!(h4, b"abc".repeat(2)); + assert_eq!(h4, ABC.repeat(2)); assert!(h4.is_inline()); - assert_eq!(h.repeat(50), b"abc".repeat(50)); + assert_eq!(h.repeat(50), ABC.repeat(50)); } #[test] @@ -923,25 +930,25 @@ fn test_simplify_range() { #[test] fn test_slice_ref_unchecked() { - let s = Vec::from(b"abc"); - let a = HipByt::borrowed(s.as_slice()); + let s = Vec::from(ABC); + let a = H::borrowed(s.as_slice()); unsafe { - assert_eq!(a.slice_ref_unchecked(&a[0..1]), b"a"); + assert_eq!(a.slice_ref_unchecked(&a[0..1]), A); assert_eq!(a.slice_ref_unchecked(&a[0..0]), b""); assert_eq!(a.slice_ref_unchecked(&a[3..3]), b""); } - let a = HipByt::from(s.as_slice()); + let a = H::from(s.as_slice()); unsafe { - assert_eq!(a.slice_ref_unchecked(&a[0..1]), b"a"); + assert_eq!(a.slice_ref_unchecked(&a[0..1]), A); assert_eq!(a.slice_ref_unchecked(&a[0..0]), b""); assert_eq!(a.slice_ref_unchecked(&a[3..3]), b""); } let s = b"*".repeat(42); - let a = HipByt::from(s); + let a = H::from(s); unsafe { assert_eq!(a.slice_ref_unchecked(&a[0..1]), b"*"); @@ -953,25 +960,25 @@ fn test_slice_ref_unchecked() { #[test] fn test_try_slice_ref() { - let s = Vec::from(b"abc"); - let a = HipByt::borrowed(s.as_slice()); + let s = Vec::from(ABC); + let a = H::borrowed(s.as_slice()); - assert_eq!(a.try_slice_ref(&a[0..1]).unwrap(), b"a"); + assert_eq!(a.try_slice_ref(&a[0..1]).unwrap(), A); assert_eq!(a.try_slice_ref(&a[0..0]).unwrap(), b""); assert_eq!(a.try_slice_ref(&a[3..3]).unwrap(), b""); - assert!(a.try_slice_ref(b"abc").is_none()); + assert!(a.try_slice_ref(ABC).is_none()); assert!(a.try_slice_ref(b"").is_none()); - let b = HipByt::borrowed(&s[0..2]); + let b = H::borrowed(&s[0..2]); assert!(b.try_slice_ref(&s[1..3]).is_none()); } #[test] fn test_slice_ref() { - let s = Vec::from(b"abc"); - let a = HipByt::borrowed(s.as_slice()); - assert_eq!(a.slice_ref(&a[0..1]), b"a"); + let s = Vec::from(ABC); + let a = H::borrowed(s.as_slice()); + assert_eq!(a.slice_ref(&a[0..1]), A); assert_eq!(a.slice_ref(&a[0..0]), b""); assert_eq!(a.slice_ref(&a[3..3]), b""); } @@ -979,21 +986,21 @@ fn test_slice_ref() { #[test] #[should_panic] fn test_slice_ref_panic() { - let s = Vec::from(b"abc"); - let a = HipByt::borrowed(s.as_slice()); - let _ = a.slice_ref(b"abc"); + let s = Vec::from(ABC); + let a = H::borrowed(s.as_slice()); + let _ = a.slice_ref(ABC); } #[test] fn test_concat_slices() { - let slices: &[&[_]] = &[b"a", b"b", b"c"]; - let h = HipByt::concat_slices(slices); + let slices: &[&[_]] = &[A, B, C]; + let h = H::concat_slices(slices); assert_eq!(h, slices.concat()); assert!(h.is_inline()); let long = b"*".repeat(42); - let slices: &[&[_]] = &[b"a", b"b", b"c", &long]; - let h = HipByt::concat_slices(slices); + let slices: &[&[_]] = &[A, B, C, &long]; + let h = H::concat_slices(slices); assert_eq!(h, slices.concat()); assert!(h.is_allocated()); } @@ -1001,27 +1008,43 @@ fn test_concat_slices() { #[test] fn test_concat() { let slices: &[&[_; 1]] = &[b"a", b"b", b"c"]; - let h = HipByt::concat(slices); - assert_eq!(h, b"abc"); + let h = H::concat(slices); + assert_eq!(h, ABC); assert!(h.is_inline()); let long = b"*".repeat(42); - let slices: &[Vec<_>] = &[b"a".into(), b"b".into(), b"c".into(), long.clone()]; - let h = HipByt::concat(slices); - assert_eq!(h, [b"abc".as_slice(), long.as_slice()].concat()); + let slices: &[Vec<_>] = &[A.into(), B.into(), C.into(), long.clone()]; + let h = H::concat(slices); + assert_eq!(h, [ABC, long.as_slice()].concat()); assert!(h.is_allocated()); } +#[test] +#[should_panic] +fn test_concat_bad_iter() { + #[derive(Clone)] + struct I(Option>>); + + impl Iterator for I { + type Item = &'static [u8]; + fn next(&mut self) -> Option { + self.0.take().map(|x| x.replace(b"longer")) + } + } + + let _h = H::concat(I(Some(Rc::new(Cell::new(b"long"))))); +} + #[test] fn test_join_slices() { - let slices: &[&[_]] = &[b"a", b"b", b"c"]; - let h = HipByt::join_slices(slices, b","); + let slices: &[&[_]] = &[A, B, C]; + let h = H::join_slices(slices, b","); assert_eq!(h, b"a,b,c"); assert!(h.is_inline()); let long = b"*".repeat(42); - let slices: &[&[_]] = &[b"a", b"b", b"c", &long]; - let h = HipByt::join_slices(slices, b","); + let slices: &[&[_]] = &[A, B, C, &long]; + let h = H::join_slices(slices, b","); assert_eq!(h, slices.join(b",".as_slice())); assert!(h.is_allocated()); } @@ -1029,13 +1052,48 @@ fn test_join_slices() { #[test] fn test_join() { let slices: &[&[_; 1]] = &[b"a", b"b", b"c"]; - let h = HipByt::join(slices, b","); + let h = H::join(slices, b","); assert_eq!(h, b"a,b,c"); assert!(h.is_inline()); let long = b"*".repeat(42); - let slices: &[Vec<_>] = &[b"a".into(), b"b".into(), b"c".into(), long.clone()]; - let h = HipByt::join(slices, b","); + let slices: &[Vec<_>] = &[A.into(), B.into(), C.into(), long.clone()]; + let h = H::join(slices, b","); assert_eq!(h, [b"a,b,c,".as_slice(), long.as_slice()].concat()); assert!(h.is_allocated()); } + +#[test] +#[should_panic] +fn test_join_bad_iter() { + #[derive(Clone)] + struct I(Option>>); + + impl Iterator for I { + type Item = &'static [u8]; + fn next(&mut self) -> Option { + self.0.take().map(|x| x.replace(b"longer")) + } + } + + let _h = H::join(I(Some(Rc::new(Cell::new(b"long")))), b","); +} + +#[test] +#[should_panic] +fn test_join_bad_iter2() { + #[derive(Clone)] + struct I(alloc::vec::IntoIter>>); + + impl Iterator for I { + type Item = &'static [u8]; + fn next(&mut self) -> Option { + self.0.next().map(|x| x.replace(b"ab")) + } + } + + let _h = H::join( + I(vec![Rc::new(Cell::new(b"ab".as_slice())), Rc::new(Cell::new(B))].into_iter()), + b",", + ); +} diff --git a/src/string.rs b/src/string.rs index cdbccdb..32340e7 100644 --- a/src/string.rs +++ b/src/string.rs @@ -4,6 +4,7 @@ use core::borrow::Borrow; use core::hash::Hash; +use core::mem::transmute; use core::ops::{Deref, DerefMut, Range, RangeBounds}; use core::ptr; use core::str::{Lines, SplitAsciiWhitespace, SplitWhitespace, Utf8Error}; @@ -1422,7 +1423,7 @@ where IterWrapper::new(self, self.as_str().lines()) } - pub fn concat_str(slices: &[&str]) -> Self { + pub fn concat_slices(slices: &[&str]) -> Self { let slices: &[&[u8]] = unsafe { core::mem::transmute(slices) }; Self(HipByt::concat_slices(slices)) } @@ -1439,31 +1440,29 @@ where /// /// This behavior differs from [`std::slice::Concat`] that reallocates when /// needed. - pub fn concat(slices: impl IntoIterator> + Clone) -> Self { - let new_len = slices.clone().into_iter().map(|e| e.borrow().len()).sum(); - - let mut raw = HipByt::with_capacity(new_len); - let dst = raw.spare_capacity_mut(); - let dst_ptr: *mut u8 = dst.as_mut_ptr().cast(); - - // compute the final pointer - let final_ptr = unsafe { dst_ptr.add(new_len) }; - - let _ = slices.into_iter().fold(dst_ptr, |dst, slice| { - let slice = slice.borrow(); - let len = slice.len(); - let end_ptr = unsafe { dst_ptr.add(len) }; - assert!(end_ptr <= final_ptr, "slices changed during concat"); - unsafe { - ptr::copy_nonoverlapping(slice.as_ptr(), dst, len); - end_ptr - } - }); + pub fn concat(slices: I) -> Self + where + E: AsRef, + I: IntoIterator, + I::IntoIter: Clone, + { + Self(HipByt::concat(slices.into_iter().map(AsBytes))) + } - unsafe { raw.set_len(new_len) }; - debug_assert_eq!(final_ptr.cast_const(), raw.as_slice().as_ptr_range().end); + pub fn join_slices(slices: &[&str], sep: impl AsRef) -> Self { + let slices: &[&[u8]] = unsafe { transmute(slices) }; + + Self(HipByt::join_slices(slices, sep.as_ref().as_bytes())) + } - Self(raw) + pub fn join(slices: I, sep: impl AsRef) -> Self + where + E: AsRef, + I: IntoIterator, + I::IntoIter: Clone, + { + let iter = slices.into_iter().map(AsBytes); + Self(HipByt::join(iter, sep.as_ref().as_bytes())) } } @@ -1939,3 +1938,13 @@ where &mut self.owned } } + +#[derive(Clone, Copy)] +struct AsBytes(T); + +impl> AsRef<[u8]> for AsBytes { + #[inline(always)] + fn as_ref(&self) -> &[u8] { + self.0.as_ref().as_bytes() + } +} diff --git a/src/string/tests.rs b/src/string/tests.rs index a0353cf..0d9923f 100644 --- a/src/string/tests.rs +++ b/src/string/tests.rs @@ -1,3 +1,5 @@ +use alloc::rc::Rc; +use core::cell::Cell; use core::ops::Bound; use core::ptr; #[cfg(feature = "std")] @@ -6,18 +8,24 @@ use std::collections::HashSet; use super::SliceErrorKind; use crate::alloc::format; use crate::alloc::string::{String, ToString}; -use crate::{HipByt, HipStr}; +use crate::{HipByt, HipStr as H}; -const INLINE_CAPACITY: usize = HipStr::inline_capacity(); +const INLINE_CAPACITY: usize = H::inline_capacity(); + +const EMPTY_SLICE: &str = ""; +const ABC: &str = "abc"; +const A: &str = "a"; +const B: &str = "b"; +const C: &str = "c"; #[test] fn test_new_default() { - let new = HipStr::new(); - assert_eq!(new, ""); + let new = H::new(); + assert_eq!(new, EMPTY_SLICE); assert!(new.is_empty()); - let new = HipStr::default(); - assert_eq!(new, ""); + let new = H::default(); + assert_eq!(new, EMPTY_SLICE); assert!(new.is_empty()); } @@ -25,17 +33,17 @@ fn test_new_default() { #[cfg(feature = "std")] fn test_borrow_and_hash() { let mut set = HashSet::new(); - set.insert(HipStr::from("a")); - set.insert(HipStr::from("b")); + set.insert(H::from(A)); + set.insert(H::from(B)); - assert!(set.contains("a")); - assert!(!set.contains("c")); + assert!(set.contains(A)); + assert!(!set.contains(C)); } #[test] fn test_fmt() { let source = "Rust \u{1F980}"; - let a = HipStr::borrowed(source); + let a = H::borrowed(source); assert_eq!(format!("{}", a), source); assert_eq!(format!("{:?}", a), format!("{:?}", source),); } @@ -43,7 +51,7 @@ fn test_fmt() { #[test] fn test_from_string() { let s = "A".repeat(42); - let hs = HipStr::from(s.clone()); + let hs = H::from(s.clone()); assert!(!hs.is_borrowed()); assert!(!hs.is_inline()); assert!(hs.is_allocated()); @@ -54,7 +62,7 @@ fn test_from_string() { #[test] fn test_borrowed() { let s = "0123456789"; - let string = HipStr::borrowed(s); + let string = H::borrowed(s); assert!(string.is_borrowed()); assert!(!string.is_inline()); assert_eq!(string.len(), s.len()); @@ -67,17 +75,17 @@ fn test_from_static() { fn is_static_type(_: &T) {} let s = "abcdefghijklmnopqrstuvwxyz"; - let string = HipStr::from_static(s); + let h = H::from_static(s); // compiler check - is_static_type(&string); - - assert!(string.is_borrowed()); - assert!(!string.is_inline()); - assert!(!string.is_allocated()); - assert_eq!(string.len(), s.len()); - assert_eq!(string.as_str(), s); - assert_eq!(string.as_ptr(), s.as_ptr()); + is_static_type(&h); + + assert!(h.is_borrowed()); + assert!(!h.is_inline()); + assert!(!h.is_allocated()); + assert_eq!(h.len(), s.len()); + assert_eq!(h.as_str(), s); + assert_eq!(h.as_ptr(), s.as_ptr()); } #[test] @@ -86,10 +94,10 @@ fn test_from_slice() { let s = core::str::from_utf8(V).unwrap(); for size in [0, 1, INLINE_CAPACITY, INLINE_CAPACITY + 1, 256, 1024] { - let string = HipStr::from(&s[..size]); - assert_eq!(size <= INLINE_CAPACITY, string.is_inline()); - assert_eq!(size > INLINE_CAPACITY, string.is_allocated()); - assert_eq!(string.len(), size); + let h = H::from(&s[..size]); + assert_eq!(size <= INLINE_CAPACITY, h.is_inline()); + assert_eq!(size > INLINE_CAPACITY, h.is_allocated()); + assert_eq!(h.len(), size); } } @@ -97,24 +105,24 @@ fn test_from_slice() { fn test_as_slice() { // static { - let a = HipStr::borrowed("abc"); + let a = H::borrowed(ABC); assert!(a.is_borrowed()); assert!(!a.is_inline()); assert!(!a.is_allocated()); - assert_eq!(a.as_str(), "abc"); + assert_eq!(a.as_str(), ABC); } // inline { - let a = HipStr::from("abc"); + let a = H::from(ABC); assert!(!a.is_borrowed()); assert!(a.is_inline()); assert!(!a.is_allocated()); - assert_eq!(a.as_str(), "abc"); + assert_eq!(a.as_str(), ABC); } // allocated { let s = "A".repeat(42); - let a = HipStr::from(s.as_str()); + let a = H::from(s.as_str()); assert!(!a.is_borrowed()); assert!(!a.is_inline()); assert!(a.is_allocated()); @@ -126,33 +134,32 @@ fn test_as_slice() { fn test_clone() { // static { - let s: &'static str = "abc"; - let a = HipStr::borrowed(s); + let a = H::borrowed(ABC); assert!(a.is_borrowed()); let b = a.clone(); drop(a); - assert_eq!(b.as_str(), "abc"); - assert_eq!(s.as_ptr(), b.as_ptr()); + assert_eq!(b.as_str(), ABC); + assert_eq!(b.as_ptr(), ABC.as_ptr()); } // inline { - let a = HipStr::from("abc"); + let a = H::from(ABC); assert!(a.is_inline()); let b = a.clone(); drop(a); - assert_eq!(b.as_str(), "abc"); + assert_eq!(b.as_str(), ABC); } // allocated { - let s = "a".repeat(42); - let p = s.as_ptr(); - let a = HipStr::from(s); + let v = A.repeat(42); + let p = v.as_ptr(); + let a = H::from(v); assert!(a.is_allocated()); let b = a.clone(); drop(a); - assert_eq!(b.as_str(), "a".repeat(42).as_str()); + assert_eq!(b.as_str(), A.repeat(42).as_str()); assert_eq!(b.as_ptr(), p); } } @@ -160,50 +167,42 @@ fn test_clone() { #[test] fn test_into_static() { // static - let a = HipStr::borrowed("abc"); - assert_eq!(a.into_borrowed(), Ok("abc")); + let a = H::borrowed(ABC); + assert_eq!(a.into_borrowed(), Ok(ABC)); // inline - let a = HipStr::from("abc"); + let a = H::from(ABC); let b = a.clone(); assert_eq!(a.into_borrowed(), Err(b)); // heap - let a = HipStr::from("a".repeat(42).as_str()); + let a = H::from(A.repeat(42).as_str()); let b = a.clone(); assert_eq!(a.into_borrowed(), Err(b)); } -#[test] -fn test_into_bytes() { - let s = HipStr::from("A".repeat(42)); - let bytes = s.into_bytes(); - assert_eq!(bytes.len(), 42); - assert_eq!(bytes.as_slice(), [b'A'; 42]); -} - #[test] fn test_as_mut_str() { // static - let mut a = HipStr::borrowed("abc"); + let mut a = H::borrowed(ABC); assert_eq!(a.as_mut_str(), None); // inline - let mut a = HipStr::from("abc"); + let mut a = H::from(ABC); assert!(a.is_inline()); - assert_eq!(a.as_mut_str(), Some(String::from("abc").as_mut_str())); + assert_eq!(a.as_mut_str(), Some(String::from(ABC).as_mut_str())); // heap - let mut a = HipStr::from("a".repeat(42).as_str()); + let mut a = H::from(A.repeat(42).as_str()); { let sl = a.as_mut_str(); - assert_eq!(sl, Some("a".repeat(42).as_mut_str())); + assert_eq!(sl, Some(A.repeat(42).as_mut_str())); unsafe { sl.unwrap().as_bytes_mut()[0] = b'b'; } } let mut b = a.clone(); - assert!(b.starts_with("b")); + assert!(b.starts_with(B)); assert_eq!(b.as_mut_str(), None); let _ = a.as_str(); } @@ -212,27 +211,27 @@ fn test_as_mut_str() { fn test_to_mut_str() { { // static - let mut a = HipStr::borrowed("abc"); + let mut a = H::borrowed(ABC); assert!(a.is_borrowed()); - assert_eq!(a.to_mut_str(), "abc".to_string().as_mut_str()); + assert_eq!(a.to_mut_str(), ABC.to_string().as_mut_str()); assert!(a.is_inline()); } { // inline - let mut a = HipStr::from("abc"); + let mut a = H::from(ABC); assert!(a.is_inline()); - assert_eq!(a.to_mut_str(), "abc".to_string().as_mut_str()); + assert_eq!(a.to_mut_str(), ABC.to_string().as_mut_str()); assert!(a.is_inline()); } { // heap - let mut a = HipStr::from("a".repeat(42).as_str()); + let mut a = H::from(A.repeat(42).as_str()); assert!(a.is_allocated()); { let sl = a.to_mut_str(); - assert_eq!(sl, "a".repeat(42).as_mut_str()); + assert_eq!(sl, A.repeat(42).as_mut_str()); sl.make_ascii_uppercase(); } @@ -246,16 +245,16 @@ fn test_to_mut_str() { #[test] fn test_slice_inline() { - let v = "a".repeat(INLINE_CAPACITY); - let s = HipStr::from(&v[..]); + let v = A.repeat(INLINE_CAPACITY); + let s = H::from(&v[..]); let sl = s.slice(0..10); assert_eq!(&sl, &v[0..10]); } #[test] fn test_slice_borrowed() { - let v = "a".repeat(42); - let s = HipStr::borrowed(&v); + let v = A.repeat(42); + let s = H::borrowed(&v); let sl1 = s.slice(4..30); assert_eq!(&sl1, &v[4..30]); @@ -272,8 +271,8 @@ fn test_slice_borrowed() { #[test] fn test_slice_allocated() { - let v = "a".repeat(42); - let s = HipStr::from(&v[..]); + let v = A.repeat(42); + let s = H::from(&v[..]); assert!(s.is_allocated()); let sl1 = s.slice(4..30); @@ -290,46 +289,46 @@ fn test_slice_allocated() { #[test] #[should_panic] fn test_slice_panic_start() { - let a = HipStr::borrowed("abc"); - let _b = a.slice(4..=4); + let a = H::borrowed(ABC); + let _b = a.slice(4..); } #[test] #[should_panic] fn test_slice_panic_end() { - let a = HipStr::borrowed("abc"); + let a = H::borrowed(ABC); let _b = a.slice(0..5); } #[test] #[should_panic] fn test_slice_panic_mixed() { - let a = HipStr::borrowed("abc"); + let a = H::borrowed(ABC); let _b = a.slice(3..2); } #[test] #[should_panic] fn test_slice_panic_start_char_boundary() { - let a = HipStr::borrowed("\u{1F980}"); + let a = H::borrowed("\u{1F980}"); let _b = a.slice(1..); } #[test] #[should_panic] fn test_slice_panic_end_char_boundary() { - let a = HipStr::borrowed("\u{1F980}"); + let a = H::borrowed("\u{1F980}"); let _b = a.slice(0..2); } #[test] fn test_slice_unchecked() { use core::ops::Bound; - let a = HipStr::borrowed("abc"); + let a = H::borrowed(ABC); assert_eq!(unsafe { a.slice_unchecked(0..2) }, "ab"); assert_eq!(unsafe { a.slice_unchecked(0..=1) }, "ab"); assert_eq!(unsafe { a.slice_unchecked(..2) }, "ab"); - assert_eq!(unsafe { a.slice_unchecked(..) }, "abc"); + assert_eq!(unsafe { a.slice_unchecked(..) }, ABC); assert_eq!( unsafe { a.slice_unchecked((Bound::Excluded(0), Bound::Unbounded)) }, "bc" @@ -340,7 +339,7 @@ fn test_slice_unchecked() { #[cfg(debug_assertions)] #[should_panic] fn test_slice_unchecked_debug_start_char_boundary_panic() { - let a = HipStr::borrowed("\u{1F980}"); + let a = H::borrowed("\u{1F980}"); let _ = unsafe { a.slice_unchecked(1..) }; } @@ -348,11 +347,11 @@ fn test_slice_unchecked_debug_start_char_boundary_panic() { #[cfg(debug_assertions)] #[should_panic] fn test_slice_unchecked_debug_end_char_boundary_panic() { - let a = HipStr::borrowed("\u{1F980}"); + let a = H::borrowed("\u{1F980}"); let _ = unsafe { a.slice_unchecked(..1) }; } -static RUST_CRAB: HipStr = HipStr::borrowed("Rust \u{1F980}"); +static RUST_CRAB: H = H::borrowed("Rust \u{1F980}"); #[test] fn test_try_slice_start_out_of_bounds() { @@ -451,7 +450,7 @@ fn test_try_slice_ok() { #[test] fn test_from_utf8() { let bytes = HipByt::borrowed(b"abc\x80"); - let err = HipStr::from_utf8(bytes.clone()).unwrap_err(); + let err = H::from_utf8(bytes.clone()).unwrap_err(); assert!(ptr::eq(err.as_bytes(), bytes.as_slice())); assert_eq!(err.utf8_error().valid_up_to(), 3); assert_eq!(format!("{err:?}"), "FromUtf8Error { bytes: [97, 98, 99, 128], error: Utf8Error { valid_up_to: 3, error_len: Some(1) } }"); @@ -464,46 +463,46 @@ fn test_from_utf8() { assert_eq!(bytes_clone.as_ptr(), bytes.as_ptr()); assert_eq!(bytes_clone.len(), bytes.len()); - let bytes = HipByt::from(b"abc".repeat(10)); - let string = HipStr::from_utf8(bytes.clone()).unwrap(); + let bytes = HipByt::from(b"ABC".repeat(10)); + let string = H::from_utf8(bytes.clone()).unwrap(); assert_eq!(bytes.as_ptr(), string.as_ptr()); } #[test] fn test_from_utf8_lossy() { let bytes = HipByt::borrowed(b"abc\x80"); - let string = HipStr::from_utf8_lossy(bytes.clone()); + let string = H::from_utf8_lossy(bytes.clone()); assert!(string.len() > bytes.len()); assert_eq!(string, "abc\u{FFFD}"); - let bytes = HipByt::from(b"abc".repeat(10)); - let string = HipStr::from_utf8_lossy(bytes.clone()); + let bytes = HipByt::from(b"ABC".repeat(10)); + let string = H::from_utf8_lossy(bytes.clone()); assert_eq!(bytes.as_ptr(), string.as_ptr()); } #[test] fn test_capacity() { - let a = HipStr::borrowed("abc"); + let a = H::borrowed(ABC); assert_eq!(a.capacity(), a.len()); - let a = HipStr::from("abc"); - assert_eq!(a.capacity(), HipStr::inline_capacity()); + let a = H::from(ABC); + assert_eq!(a.capacity(), H::inline_capacity()); let mut v = String::with_capacity(42); for _ in 0..10 { - v.push_str("abc"); + v.push_str(ABC); } - let a = HipStr::from(v); + let a = H::from(v); assert_eq!(a.capacity(), 42); } #[test] fn test_mutate_borrowed() { - let mut a = HipStr::borrowed("abc"); + let mut a = H::borrowed(ABC); assert!(a.is_borrowed()); { let mut r = a.mutate(); - assert_eq!(r.as_str(), "abc"); + assert_eq!(r.as_str(), ABC); r.push_str("def"); } assert!(!a.is_borrowed()); @@ -512,7 +511,7 @@ fn test_mutate_borrowed() { #[test] fn test_mutate_inline() { - let mut a = HipStr::from("abc"); + let mut a = H::from(ABC); assert!(a.is_inline()); a.mutate().push_str("def"); assert_eq!(a, "abcdef"); @@ -525,7 +524,7 @@ fn test_mutate_allocated() { let mut v = String::with_capacity(42); v.push_str("abcdefghijklmnopqrstuvwxyz"); let p = v.as_ptr(); - let mut a = HipStr::from(v); + let mut a = H::from(v); assert!(a.is_allocated()); a.mutate().push_str("0123456789"); assert!(a.is_allocated()); @@ -537,7 +536,7 @@ fn test_mutate_allocated() { // allocated, shared let mut v = String::with_capacity(42); v.push_str("abcdefghijklmnopqrstuvwxyz"); - let mut a = HipStr::from(v); + let mut a = H::from(v); assert!(a.is_allocated()); let b = a.clone(); a.mutate().push_str("0123456789"); @@ -551,17 +550,17 @@ fn test_mutate_allocated() { #[test] fn test_from_utf16() { let v = [b'a' as u16].repeat(42); - assert_eq!(HipStr::from_utf16(&v[0..4]).unwrap(), "a".repeat(4)); - assert_eq!(HipStr::from_utf16(&v).unwrap(), "a".repeat(42)); - assert!(HipStr::from_utf16(&[0xD834]).is_err()); + assert_eq!(H::from_utf16(&v[0..4]).unwrap(), A.repeat(4)); + assert_eq!(H::from_utf16(&v).unwrap(), A.repeat(42)); + assert!(H::from_utf16(&[0xD834]).is_err()); } #[test] fn test_from_utf16_lossy() { let v = [b'a' as u16].repeat(42); - assert_eq!(HipStr::from_utf16_lossy(&v[0..4]), "a".repeat(4)); - assert_eq!(HipStr::from_utf16_lossy(&v), "a".repeat(42)); - assert_eq!(HipStr::from_utf16_lossy(&[0xD834]), "\u{FFFD}"); + assert_eq!(H::from_utf16_lossy(&v[0..4]), A.repeat(4)); + assert_eq!(H::from_utf16_lossy(&v), A.repeat(42)); + assert_eq!(H::from_utf16_lossy(&[0xD834]), "\u{FFFD}"); } const FORTY_TWOS: &str = unsafe { core::str::from_utf8_unchecked(&[42; 42]) }; @@ -569,37 +568,37 @@ const FORTY_TWOS: &str = unsafe { core::str::from_utf8_unchecked(&[42; 42]) }; #[test] fn test_push_slice_allocated() { // borrowed, not unique - let mut a = HipStr::borrowed(FORTY_TWOS); - a.push_str("abc"); + let mut a = H::borrowed(FORTY_TWOS); + a.push_str(ABC); assert_eq!(&a[0..42], FORTY_TWOS); - assert_eq!(&a[42..], "abc"); + assert_eq!(&a[42..], ABC); // allocated, unique - let mut a = HipStr::from(FORTY_TWOS); - a.push_str("abc"); + let mut a = H::from(FORTY_TWOS); + a.push_str(ABC); assert_eq!(&a[0..42], FORTY_TWOS); - assert_eq!(&a[42..], "abc"); + assert_eq!(&a[42..], ABC); // allocated, not unique - let mut a = HipStr::from(FORTY_TWOS); + let mut a = H::from(FORTY_TWOS); let pa = a.as_ptr(); let b = a.clone(); assert_eq!(pa, b.as_ptr()); - a.push_str("abc"); + a.push_str(ABC); assert_ne!(a.as_ptr(), pa); assert_eq!(&a[0..42], FORTY_TWOS); - assert_eq!(&a[42..], "abc"); + assert_eq!(&a[42..], ABC); assert_eq!(b, FORTY_TWOS); // allocated, unique but shifted let mut a = { - let x = HipStr::from(FORTY_TWOS); + let x = H::from(FORTY_TWOS); x.slice(1..39) }; let p = a.as_ptr(); - a.push_str("abc"); + a.push_str(ABC); assert_eq!(&a[..38], &FORTY_TWOS[1..39]); - assert_eq!(&a[38..], "abc"); + assert_eq!(&a[38..], ABC); assert_eq!(a.as_ptr(), p); // => the underlying vector is big enough } @@ -609,7 +608,7 @@ fn test_push() { // for now, push_char uses push_slice // so test can be minimal - let mut a = HipStr::from("abc"); + let mut a = H::from(ABC); a.push('d'); assert_eq!(a, "abcd"); a.push('🦀'); @@ -618,8 +617,8 @@ fn test_push() { #[test] fn test_to_owned() { - let b = "abc"; - let h = HipStr::from(b); + let b = ABC; + let h = H::from(b); assert!(h.is_inline()); let h = h.into_owned(); assert!(h.is_inline()); @@ -627,13 +626,13 @@ fn test_to_owned() { let r = "*".repeat(42); let v = r.clone(); - let a = HipStr::borrowed(&v[0..2]); + let a = H::borrowed(&v[0..2]); let a = a.into_owned(); drop(v); assert_eq!(a, &r[0..2]); let v = r.clone(); - let a = HipStr::from(&v[..]); + let a = H::from(&v[..]); drop(v); let p = a.as_ptr(); let a = a.into_owned(); @@ -643,11 +642,11 @@ fn test_to_owned() { #[test] fn test_to_case() { for (input, a_l, l, a_u, u) in [ - ("abc", "abc", "abc", "ABC", "ABC"), + (ABC, ABC, ABC, "ABC", "ABC"), ("ὈΔΥΣΣΕΎΣ", "ὈΔΥΣΣΕΎΣ", "ὀδυσσεύς", "ὈΔΥΣΣΕΎΣ", "ὈΔΥΣΣΕΎΣ"), ("农历新年", "农历新年", "农历新年", "农历新年", "农历新年"), ] { - let h = HipStr::from(input); + let h = H::from(input); assert_eq!(h.to_ascii_lowercase(), a_l); assert_eq!(h.to_lowercase(), l); assert_eq!(h.to_ascii_uppercase(), a_u); @@ -657,7 +656,7 @@ fn test_to_case() { #[test] fn test_make_case() { - let mut h = HipStr::from("abcDEF"); + let mut h = H::from("abcDEF"); let mut h2 = h.clone(); let h_ref = h.clone(); h.make_ascii_lowercase(); @@ -671,90 +670,197 @@ fn test_make_case() { #[test] fn test_repeat() { - let h = HipStr::new(); + let h = H::new(); let h50 = h.repeat(50); assert_eq!(h50.len(), 0); assert!(!h50.is_allocated()); - let h = HipStr::from("*".repeat(42)); + let h = H::from("*".repeat(42)); let h1 = h.repeat(1); assert_eq!(h1.len(), h.len()); assert_eq!(h.as_ptr(), h1.as_ptr()); - let h = HipStr::from("abc"); + let h = H::from(ABC); let h4 = h.repeat(2); - assert_eq!(h4, "abc".repeat(2)); + assert_eq!(h4, ABC.repeat(2)); assert!(h4.is_inline()); - assert_eq!(h.repeat(50), "abc".repeat(50)); + assert_eq!(h.repeat(50), ABC.repeat(50)); } #[test] fn test_slice_ref_unchecked() { - let s = String::from("abc"); - let a = HipStr::borrowed(s.as_str()); + let s = String::from(ABC); + let a = H::borrowed(s.as_str()); unsafe { - assert_eq!(a.slice_ref_unchecked(&a[0..1]), "a"); - assert_eq!(a.slice_ref_unchecked(&a[0..0]), ""); - assert_eq!(a.slice_ref_unchecked(&a[3..3]), ""); + assert_eq!(a.slice_ref_unchecked(&a[0..1]), A); + assert_eq!(a.slice_ref_unchecked(&a[0..0]), EMPTY_SLICE); + assert_eq!(a.slice_ref_unchecked(&a[3..3]), EMPTY_SLICE); } - let a = HipStr::from(s.as_str()); + let a = H::from(s.as_str()); unsafe { - assert_eq!(a.slice_ref_unchecked(&a[0..1]), "a"); - assert_eq!(a.slice_ref_unchecked(&a[0..0]), ""); - assert_eq!(a.slice_ref_unchecked(&a[3..3]), ""); + assert_eq!(a.slice_ref_unchecked(&a[0..1]), A); + assert_eq!(a.slice_ref_unchecked(&a[0..0]), EMPTY_SLICE); + assert_eq!(a.slice_ref_unchecked(&a[3..3]), EMPTY_SLICE); } let s = "*".repeat(42); - let a = HipStr::from(s); + let a = H::from(s); unsafe { assert_eq!(a.slice_ref_unchecked(&a[0..1]), "*"); assert_eq!(a.slice_ref_unchecked(&a[0..41]).as_ptr(), a.as_ptr()); - assert_eq!(a.slice_ref_unchecked(&a[0..0]), ""); - assert_eq!(a.slice_ref_unchecked(&a[3..3]), ""); + assert_eq!(a.slice_ref_unchecked(&a[0..0]), EMPTY_SLICE); + assert_eq!(a.slice_ref_unchecked(&a[3..3]), EMPTY_SLICE); } } #[test] fn test_try_slice_ref() { - let s = String::from("abc"); - let a = HipStr::borrowed(s.as_str()); + let s = String::from(ABC); + let a = H::borrowed(s.as_str()); - assert_eq!(a.try_slice_ref(&a[0..1]).unwrap(), "a"); - assert_eq!(a.try_slice_ref(&a[0..0]).unwrap(), ""); - assert_eq!(a.try_slice_ref(&a[3..3]).unwrap(), ""); + assert_eq!(a.try_slice_ref(&a[0..1]).unwrap(), A); + assert_eq!(a.try_slice_ref(&a[0..0]).unwrap(), EMPTY_SLICE); + assert_eq!(a.try_slice_ref(&a[3..3]).unwrap(), EMPTY_SLICE); - assert!(a.try_slice_ref("abc").is_none()); - assert!(a.try_slice_ref("").is_none()); + assert!(a.try_slice_ref(ABC).is_none()); + assert!(a.try_slice_ref(EMPTY_SLICE).is_none()); - let b = HipStr::borrowed(&s[0..2]); + let b = H::borrowed(&s[0..2]); assert!(b.try_slice_ref(&s[1..3]).is_none()); } #[test] fn test_slice_ref() { - let s = String::from("abc"); - let a = HipStr::borrowed(s.as_str()); - assert_eq!(a.slice_ref(&a[0..1]), "a"); - assert_eq!(a.slice_ref(&a[0..0]), ""); - assert_eq!(a.slice_ref(&a[3..3]), ""); + let s = String::from(ABC); + let a = H::borrowed(s.as_str()); + assert_eq!(a.slice_ref(&a[0..1]), A); + assert_eq!(a.slice_ref(&a[0..0]), EMPTY_SLICE); + assert_eq!(a.slice_ref(&a[3..3]), EMPTY_SLICE); } #[test] #[should_panic] fn test_slice_ref_panic() { - let s = String::from("abc"); - let a = HipStr::borrowed(s.as_str()); - let _ = a.slice_ref("abc"); + let s = String::from(ABC); + let a = H::borrowed(s.as_str()); + let _ = a.slice_ref(ABC); +} + +#[test] +fn test_concat_slices() { + let slices: &[&str] = &[A, B, C]; + let h = H::concat_slices(slices); + assert_eq!(h, slices.concat()); + assert!(h.is_inline()); + + let long = "*".repeat(42); + let slices: &[&str] = &[A, B, C, &long]; + let h = H::concat_slices(slices); + assert_eq!(h, slices.concat()); + assert!(h.is_allocated()); +} + +#[test] +fn test_concat() { + let slices: &[&str] = &[A, B, C]; + let h = H::concat(slices); + assert_eq!(h, ABC); + assert!(h.is_inline()); + + let long = "*".repeat(42); + let slices: &[String] = &[A.into(), B.into(), C.into(), long.clone()]; + let h = H::concat(slices); + assert_eq!(h, [ABC, long.as_str()].concat()); + assert!(h.is_allocated()); +} + +#[test] +#[should_panic] +fn test_concat_bad_iter() { + #[derive(Clone)] + struct I(Option>>); + + impl Iterator for I { + type Item = &'static str; + fn next(&mut self) -> Option { + self.0.take().map(|x| x.replace("longer")) + } + } + + let _h = HipByt::concat(I(Some(Rc::new(Cell::new("long"))))); +} + +#[test] +fn test_join_slices() { + let slices: &[&str] = &[A, B, C]; + let h = H::join_slices(slices, ","); + assert_eq!(h, "a,b,c"); + assert!(h.is_inline()); + + let long = "*".repeat(42); + let slices: &[&str] = &[A, B, C, &long]; + let h = H::join_slices(slices, ","); + assert_eq!(h, slices.join(",")); + assert!(h.is_allocated()); +} + +#[test] +fn test_join() { + let slices: &[&str] = &[A, B, C]; + let h = H::join(slices, ","); + assert_eq!(h, "a,b,c"); + assert!(h.is_inline()); + + let long = "*".repeat(42); + let slices: &[String] = &[A.into(), B.into(), C.into(), long.clone()]; + let h = H::join(slices, ","); + assert_eq!(h, ["a,b,c,", long.as_str()].concat()); + assert!(h.is_allocated()); +} + +#[test] +#[should_panic] +fn test_join_bad_iter() { + #[derive(Clone)] + struct I(Option>>); + + impl Iterator for I { + type Item = &'static str; + fn next(&mut self) -> Option { + self.0.take().map(|x| x.replace("longer")) + } + } + + let _h = H::join(I(Some(Rc::new(Cell::new("long")))), ",".to_string()); +} + +#[test] +#[should_panic] +fn test_join_bad_iter2() { + #[derive(Clone)] + struct I(alloc::vec::IntoIter>>); + + impl Iterator for I { + type Item = &'static str; + fn next(&mut self) -> Option { + self.0.next().map(|x| x.replace("ab")) + } + } + + let _h = H::join( + I(vec![Rc::new(Cell::new("ab")), Rc::new(Cell::new(B))].into_iter()), + ",".to_string(), + ); } #[inline] #[track_caller] -fn value_eq<'a>(computed: HipStr<'a>, expected: &'a str) { +fn value_eq<'a>(computed: H<'a>, expected: &'a str) { assert!( core::ptr::eq(computed.as_str(), expected), "{computed:?} and {expected:?} are not the same" @@ -763,7 +869,7 @@ fn value_eq<'a>(computed: HipStr<'a>, expected: &'a str) { #[inline] #[track_caller] -fn option_eq<'a>(computed: Option>, expected: Option<&'a str>) { +fn option_eq<'a>(computed: Option>, expected: Option<&'a str>) { match (computed, expected) { (None, None) => (), (None, Some(e)) => panic!("expected value {e:?}, got none"), @@ -775,7 +881,7 @@ fn option_eq<'a>(computed: Option>, expected: Option<&'a str>) { #[inline] #[track_caller] fn iter_eq_bidir<'a>( - computed: impl Iterator> + DoubleEndedIterator + Clone, + computed: impl Iterator> + DoubleEndedIterator + Clone, expected: impl Iterator + DoubleEndedIterator + Clone, ) { iter_eq(computed.clone(), expected.clone()); @@ -784,7 +890,7 @@ fn iter_eq_bidir<'a>( #[inline] #[track_caller] fn iter_eq<'a>( - mut computed: impl Iterator>, + mut computed: impl Iterator>, mut expected: impl Iterator, ) { loop { @@ -798,7 +904,7 @@ fn iter_eq<'a>( #[inline] #[track_caller] -fn pair_eq<'a>(computed: Option<(HipStr<'a>, HipStr<'a>)>, expected: Option<(&'a str, &'a str)>) { +fn pair_eq<'a>(computed: Option<(H<'a>, H<'a>)>, expected: Option<(&'a str, &'a str)>) { match (computed, expected) { (Some((c1, c2)), Some((e1, e2))) => { value_eq(c1, e1); @@ -814,7 +920,7 @@ fn test_whitespace() { // test only with borrowed string let source = " \r\n a\t\n"; - let h = HipStr::borrowed(source); + let h = H::borrowed(source); value_eq(h.trim(), source.trim()); value_eq(h.trim_start(), source.trim_start()); value_eq(h.trim_end(), source.trim_end()); @@ -822,8 +928,8 @@ fn test_whitespace() { iter_eq_bidir(h.split_ascii_whitespace(), source.split_ascii_whitespace()); iter_eq_bidir(h.lines(), source.lines()); - let source = "abc"; - let h = HipStr::borrowed(source); + let source = ABC; + let h = H::borrowed(source); value_eq(h.trim(), source.trim()); value_eq(h.trim_start(), source.trim_start()); value_eq(h.trim_end(), source.trim_end()); @@ -840,7 +946,7 @@ fn test_pattern() { macro_rules! test_method { (>< $name:ident $( ( $p:tt ) )? $test:ident) => {{ let source = ":a:b:c:"; - let hip = HipStr::borrowed(source); + let hip = H::borrowed(source); $test(hip.$name($( $p , )? ':'), source.$name($( $p , )? ':')); $test(hip.$name($( $p , )? '.'), source.$name($( $p , )? '.')); $test(hip.$name($( $p , )? [':'].as_slice()), source.$name($( $p , )? [':'].as_slice())); @@ -848,7 +954,7 @@ fn test_pattern() { }}; ($name:ident $( ( $p:tt ) )? $test:ident) => {{ let source = ":a:b:c:"; - let hip = HipStr::borrowed(source); + let hip = H::borrowed(source); $test(hip.$name($( $p , )? ":"), source.$name($( $p , )? ":")); $test(hip.$name($( $p , )? "."), source.$name($( $p , )? ".")); $test(hip.$name($( $p , )? ':'), source.$name($( $p , )? ':')); @@ -879,7 +985,7 @@ fn test_pattern() { { let source = ":a:b:c:"; - let hip = HipStr::borrowed(source); + let hip = H::borrowed(source); iter_eq(hip.match_indices(":").map(|(_, v)| v), source.matches(":")); iter_eq(hip.match_indices(".").map(|(_, v)| v), source.matches(".")); iter_eq_bidir(hip.match_indices(':').map(|(_, v)| v), source.matches(':')); @@ -900,7 +1006,7 @@ fn test_pattern() { { let source = ":a:b:c:"; - let hip = HipStr::borrowed(source); + let hip = H::borrowed(source); iter_eq( hip.rmatch_indices(":").map(|(_, v)| v), source.rmatches(":"), @@ -938,3 +1044,11 @@ fn test_pattern() { test_method!(strip_prefix option_eq); test_method!(strip_suffix option_eq); } + +#[test] +fn test_into_bytes() { + let s = H::from("A".repeat(42)); + let bytes = s.into_bytes(); + assert_eq!(bytes.len(), 42); + assert_eq!(bytes.as_slice(), [b'A'; 42]); +}