Skip to content

Commit

Permalink
feat: add truncate, pop, and clear
Browse files Browse the repository at this point in the history
  • Loading branch information
polazarus committed Jul 17, 2024
1 parent 36b2084 commit d224a7d
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 21 deletions.
76 changes: 71 additions & 5 deletions src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,11 @@ where
/// assert_eq!(b"bar", slice);
/// ```
#[inline]
#[doc(alias = "make_mut")]
pub fn to_mut_slice(&mut self) -> &mut [u8] {
self.0.make_unique();
let slice = self.0.as_mut_slice();
// SAFETY: `make_unique` above ensures that it is uniquely owned
unsafe { slice.unwrap_unchecked() }
unsafe { self.0.as_mut_slice_unchecked() }
}

/// Extracts a slice as its own `HipByt`.
Expand Down Expand Up @@ -496,6 +496,66 @@ where
}
}

/// Shortens this `HipByt` to the specified length.
///
/// If the new length is greater than the current length, this has no effect.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use hipstr::HipByt;
/// let mut a = HipByt::from(b"abc");
/// a.truncate(1);
/// assert_eq!(a, b"a");
/// ```
#[inline]
pub fn truncate(&mut self, new_len: usize) {
self.0.truncate(new_len);
}

/// Truncates this `HipByt`, removing all contents.
///
/// # Examples
///
/// ```
/// # use hipstr::HipByt;
/// let mut s = HipByt::from(b"foo");
///
/// s.clear();
///
/// assert!(s.is_empty());
/// assert_eq!(0, s.len());
/// ```
#[inline]
pub fn clear(&mut self) {
self.0.truncate(0);
}

/// Removes the last element from this `HipByt` and returns it, or [`None`]
/// if it is empty.
///
/// # Examples
///
/// ```
/// # use hipstr::HipByt;
///
/// let mut h = HipByt::from(&[1, 2, 3]);
/// assert_eq!(h.pop(), Some(3));
/// assert_eq!(h, [1, 2]);
/// ```
pub fn pop(&mut self) -> Option<u8> {
let len = self.len();
if len == 0 {
None
} else {
let result = unsafe { *self.as_slice().get_unchecked(len - 1) };
self.truncate(len - 1);
Some(result)
}
}

/// Appends all bytes of the slice to this `HipByt`.
///
/// # Examples
Expand Down Expand Up @@ -531,6 +591,10 @@ where

/// Makes the data owned, copying it if the data is actually borrowed.
///
/// Returns a new `HipByt` consuming this one.
///
/// # Examples
///
/// ```
/// # use hipstr::HipByt;
/// let v = vec![42; 42];
Expand Down Expand Up @@ -721,6 +785,7 @@ where
/// let c = HipByt::concat_slices(&[b"hello", b" world", b"!"]);
/// assert_eq!(c, b"hello world!");
/// ```
#[must_use]
pub fn concat_slices(slices: &[&[u8]]) -> Self {
let new_len = slices.iter().map(|e| e.len()).sum();

Expand Down Expand Up @@ -774,6 +839,7 @@ where
/// let c3 = HipByt::concat(vec![b"hello".as_slice(), b" world", b"!"].iter());
/// assert_eq!(c3, b"hello world!");
/// ```
#[must_use]
pub fn concat<E, I>(slices: I) -> Self
where
E: AsRef<[u8]>,
Expand Down Expand Up @@ -826,6 +892,7 @@ where
/// let joined = HipByt::join_slices(slices, sep);
/// assert_eq!(joined, b"hello, world, rust");
/// ```
#[must_use]
pub fn join_slices(slices: &[&[u8]], sep: impl AsRef<[u8]>) -> Self {
let slices_len = slices.len();
if slices_len == 0 {
Expand All @@ -849,12 +916,11 @@ where
// compute the final pointer
let final_ptr = unsafe { dst_ptr.add(new_len) };

let mut iter = slices.iter();
let mut iter = slices.iter().copied();

// get first slice
// SAFETY: segments > 0 is checked above
let slice = unsafe { iter.next().unwrap_unchecked() };
let slice = slice.as_ref();
let len = slice.len();
// SAFETY: dst_ptr + len cannot overflow
let end_ptr = unsafe { dst_ptr.add(len) };
Expand All @@ -872,7 +938,6 @@ where
}
dst_ptr = end_ptr;

let slice = slice.as_ref();
let len = slice.len();
let end_ptr = unsafe { dst_ptr.add(len) };
debug_assert!(end_ptr <= final_ptr, "slices changed during concat");
Expand Down Expand Up @@ -918,6 +983,7 @@ where
/// let joined = HipByt::join(slices.to_vec().iter(), sep);
/// assert_eq!(joined, b"hello, world, rust");
/// ```
#[must_use]
pub fn join<E, I>(slices: I, sep: impl AsRef<[u8]>) -> Self
where
E: AsRef<[u8]>,
Expand Down
76 changes: 72 additions & 4 deletions src/bytes/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,35 +259,49 @@ fn test_as_mut_slice() {
}

#[test]
fn test_to_mut_slice_static() {
fn test_to_mut_slice_borrowed() {
let mut a = H::borrowed(ABC);
assert!(a.is_borrowed());
assert_eq!(a.to_mut_slice(), ABC.to_vec().as_mut_slice());
assert_eq!(a.to_mut_slice(), ABC);
assert!(a.is_inline());

let mut a = H::borrowed(MEDIUM);
assert!(a.is_borrowed());
assert_eq!(a.to_mut_slice(), MEDIUM);
assert!(a.is_allocated());
}

#[test]
fn test_to_mut_slice_inline() {
let mut a = H::from(ABC);
let p = a.as_ptr();
assert!(a.is_inline());
assert_eq!(a.to_mut_slice(), ABC);
assert!(a.is_inline());
assert_eq!(a.as_ptr(), p);
}

#[test]
fn test_to_mut_slice_allocated() {
let mut a = H::from(MEDIUM);
let p = a.as_ptr();
assert!(a.is_allocated());
{
let sl = a.to_mut_slice();
assert_eq!(sl, MEDIUM);
assert_eq!(sl.as_ptr(), p);
sl[0] = 43;
}

let mut b = a.clone();
assert_eq!(b[0], 43);
let _ = b.to_mut_slice();
{
let sl = b.to_mut_slice();
sl[0] = 42;
}
assert_eq!(b, MEDIUM);
assert!(b.is_allocated());
assert_ne!(b.as_ptr(), a.as_ptr());
assert_ne!(b.as_ptr(), p);
}

#[test]
Expand Down Expand Up @@ -620,6 +634,60 @@ fn test_mutate_allocated() {
}
}

#[test]
fn test_truncate() {
let mut h = H::borrowed(MEDIUM);
h.truncate(MEDIUM.len() + 1);
assert_eq!(h, MEDIUM);

let mut h = H::borrowed(MEDIUM);
h.truncate(1);
assert_eq!(h, &MEDIUM[..1]);

let mut h = H::from(MEDIUM);
h.truncate(1);
assert_eq!(h, &MEDIUM[..1]);

let mut h = H::from(&MEDIUM[..INLINE_CAPACITY]);
h.truncate(1);
assert_eq!(h, &MEDIUM[..1]);
}

#[test]
fn test_clear() {
let mut h = H::borrowed(MEDIUM);
h.clear();
assert!(h.is_empty());
assert!(!h.is_allocated());

let mut h = H::from(MEDIUM);
h.clear();
assert!(h.is_empty());
assert!(!h.is_allocated());

let mut h = H::from(&MEDIUM[..INLINE_CAPACITY]);
h.clear();
assert!(h.is_empty());
assert!(!h.is_allocated());
}

#[test]
fn test_pop() {
let mut h = H::borrowed(MEDIUM);
assert_eq!(h.pop(), Some(MEDIUM[0]));
assert_eq!(h, &MEDIUM[..MEDIUM.len() - 1]);

let mut h = H::from(MEDIUM);
assert_eq!(h.pop(), Some(MEDIUM[0]));
assert_eq!(h, &MEDIUM[..MEDIUM.len() - 1]);

let mut h = H::from(&MEDIUM[..INLINE_CAPACITY]);
assert_eq!(h.pop(), Some(MEDIUM[0]));
assert_eq!(h, &MEDIUM[..INLINE_CAPACITY - 1]);

assert_eq!(H::new().pop(), None);
}

#[test]
fn test_push_slice_borrowed() {
#[track_caller]
Expand Down
40 changes: 38 additions & 2 deletions src/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ impl<'borrow, B: Backend> Raw<'borrow, B> {
/// Creates a new `Raw` from a slice.
///
/// Will normalize the representation depending on the size of the slice.
#[inline]
pub fn from_slice(bytes: &[u8]) -> Self {
let len = bytes.len();
if len <= INLINE_CAPACITY {
Expand Down Expand Up @@ -460,6 +461,7 @@ impl<'borrow, B: Backend> Raw<'borrow, B> {
result
}

/// Returns a mutable slice if this `Raw` is neither borrowed nor shared.
#[inline]
pub fn as_mut_slice(&mut self) -> Option<&mut [u8]> {
match self.split_mut() {
Expand All @@ -469,6 +471,26 @@ impl<'borrow, B: Backend> Raw<'borrow, B> {
}
}

/// Returns a mutable slice of the underlying string.
///
/// # Safety
///
/// This `Raw` should not be shared or borrowed.
#[inline]
pub unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [u8] {
match self.split_mut() {
RawSplitMut::Inline(inline) => inline.as_mut_slice(),
RawSplitMut::Allocated(allocated) => unsafe { allocated.as_mut_slice_unchecked() },
RawSplitMut::Borrowed(_) => {
if cfg!(debug_assertions) {
panic!("mutable slice of borrowed string");
} else {
unsafe { unreachable_unchecked() }
}
}
}
}

/// Push a slice at the end of this raw byte string.
#[inline]
pub fn push_slice(&mut self, addition: &[u8]) {
Expand Down Expand Up @@ -510,8 +532,6 @@ impl<'borrow, B: Backend> Raw<'borrow, B> {

// SAFETY: vec's len (new_len) is checked above to be > INLINE_CAPACITY
*self = Self::from_vec(vec);

return;
}

/// Takes a vector representation of this raw byte string.
Expand Down Expand Up @@ -748,6 +768,22 @@ impl<'borrow, B: Backend> Raw<'borrow, B> {
RawSplitMut::Allocated(allocated) => unsafe { allocated.set_len(new_len) },
}
}

/// Shortens and normalizes the vector keeping the first `new_len` elements.
///
/// Do nothing is `new_len` is greater than the current length.
pub fn truncate(&mut self, new_len: usize) {
if new_len < self.len() {
if self.is_allocated() && new_len <= INLINE_CAPACITY {
let new =
unsafe { Self::inline_unchecked(self.as_slice().get_unchecked(..new_len)) };
*self = new;
} else {
unsafe { self.set_len(new_len) }
}
}
debug_assert!(self.is_normalized());
}
}

impl<'borrow, B: Backend> Drop for Raw<'borrow, B> {
Expand Down
10 changes: 10 additions & 0 deletions src/raw/allocated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,16 @@ impl<B: Backend> Allocated<B> {
}
}

/// Returns a mutable slice.
///
/// # Safety
///
/// The caller must ensure that the `Allocated` is actually uniquely shared.
#[inline]
pub unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast_mut(), self.len) }
}

/// Creates a new `Allocated` for some range with the same owner.
///
/// # Safety
Expand Down
10 changes: 10 additions & 0 deletions src/raw/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ fn test_union() {
};
let _: R = union.into_raw();
}

#[cfg(debug_assertions)]
#[should_panic]
#[test]
fn test_to_mut_slice_unchecked_panic() {
let mut r = R::borrowed(b"abc");
unsafe {
let _sl = r.as_mut_slice_unchecked();
}
}
Loading

0 comments on commit d224a7d

Please sign in to comment.