Skip to content

Commit

Permalink
add split helpers for ByteData and StringData
Browse files Browse the repository at this point in the history
  • Loading branch information
TimLuq committed Jul 2, 2024
1 parent cdb0945 commit fbc3f6f
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 3 deletions.
95 changes: 92 additions & 3 deletions src/bytedata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ impl<'a> ByteData<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "chunk")))]
#[inline]
pub const fn from_chunk_slice(dat: &[u8]) -> Self {
Self::Chunk(crate::byte_chunk::ByteChunk::from_slice(dat))
if dat.is_empty() {
Self::Static(&[])
} else {
Self::Chunk(crate::byte_chunk::ByteChunk::from_slice(dat))
}
}

#[cfg(feature = "chunk")]
Expand All @@ -61,13 +65,21 @@ impl<'a> ByteData<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "chunk")))]
#[inline]
pub const fn from_chunk<const L: usize>(dat: &[u8; L]) -> Self {
Self::Chunk(crate::byte_chunk::ByteChunk::from_array(dat))
if L == 0 {
Self::Static(&[])
} else {
Self::Chunk(crate::byte_chunk::ByteChunk::from_array(dat))
}
}

/// Creates a `ByteData` from a borrowed slice of bytes.
#[inline]
pub const fn from_borrowed(dat: &'a [u8]) -> Self {
Self::Borrowed(dat)
if dat.is_empty() {
Self::Static(&[])
} else {
Self::Borrowed(dat)
}
}

#[cfg(feature = "alloc")]
Expand All @@ -83,6 +95,9 @@ impl<'a> ByteData<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
pub fn from_owned(dat: Vec<u8>) -> Self {
if dat.is_empty() {
return Self::Static(&[]);
}
#[cfg(feature = "chunk")]
if dat.len() <= 12 {
return Self::Chunk(crate::byte_chunk::ByteChunk::from_slice(&dat));
Expand Down Expand Up @@ -258,6 +273,80 @@ impl<'a> ByteData<'a> {
Self::Chunk(dat) => ByteData::Chunk(dat.into_sliced(range)),
}
}

/// Split the `ByteData` at the given position.
#[inline]
pub fn take_bytes(&mut self, position: usize) -> ByteData<'a> {
let a = self.sliced(0..position);
self.make_sliced(position..);
a
}

/// Split the `ByteData` at the given position.
#[inline]
pub fn split_at(mut self, position: usize) -> (ByteData<'a>, ByteData<'a>) {
let a = self.sliced(0..position);
self.make_sliced(position..);
(a, self)
}

/// Split the `ByteData` at the first occurrence of the given byte sequence.
#[inline]
pub fn split_once_on(
self,
needle: &[u8],
) -> Result<(ByteData<'a>, ByteData<'a>), ByteData<'a>> {
let a = match crate::const_split_once_bytes(self.as_slice(), needle) {
Some((a, _)) => a.len(),
None => return Err(self),
};
Ok(self.split_at(a))
}

/// Split the `ByteData` at the first occurrence of the given byte sequence.
#[inline]
pub fn split_on<'b>(self, needle: &'b [u8]) -> impl Iterator<Item = ByteData<'a>> + Send + 'b
where
'a: 'b,
{
struct It<'a, 'b>(ByteData<'a>, &'b [u8], bool);
impl<'a, 'b> Iterator for It<'a, 'b> {
type Item = ByteData<'a>;

fn next(&mut self) -> Option<Self::Item> {
if self.0.is_empty() {
return None;
}
let a = match crate::const_split_once_bytes(self.0.as_slice(), self.1) {
Some((a, _)) => a.len(),
None => {
let r = core::mem::replace(&mut self.0, ByteData::empty());
return Some(r);
}
};
if a == 0 && self.2 {
self.2 = false;
return Some(ByteData::empty());
}
self.2 = false;
let a = self.0.take_bytes(a);
Some(a)
}

fn size_hint(&self) -> (usize, Option<usize>) {
if self.0.is_empty() {
(0, Some(0))
} else if self.0.len() < self.1.len() {
(1, Some(1))
} else if self.0.len() < self.1.len() * 2 {
(1, Some(2))
} else {
(1, None)
}
}
}
It(self, needle, true)
}
}

impl ByteData<'static> {
Expand Down
2 changes: 2 additions & 0 deletions src/queue/split.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// An iterator over chunks of a `ByteQueue` separated by a byte sequence.
pub struct SplitOn<'a, 'b> {
queue: &'b super::ByteQueue<'a>,
needle: &'b [u8],
Expand Down Expand Up @@ -87,6 +88,7 @@ impl<'a, 'b> Iterator for SplitOn<'a, 'b> {
}
}

/// An iterator over chunks of a `StringQueue` separated by a str sequence.
#[repr(transparent)]
pub struct SplitOnStr<'a, 'b> {
inner: SplitOn<'a, 'b>,
Expand Down
32 changes: 32 additions & 0 deletions src/stringdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,38 @@ impl<'a> StringData<'a> {
self.data.make_sliced(range);
}

/// Split the `StringData` at the given position.
#[inline]
pub fn split_at(mut self, position: usize) -> (StringData<'a>, StringData<'a>) {
let a = self.sliced(0..position);
self.make_sliced(position..);
(a, self)
}

/// Split the `StringData` at the first occurrence of the given byte sequence.
#[inline]
pub fn split_once_on(
self,
needle: &str,
) -> Result<(StringData<'a>, StringData<'a>), StringData<'a>> {
let a = match crate::const_split_once_bytes(self.as_bytes(), needle.as_bytes()) {
Some((a, _)) => a.len(),
None => return Err(self),
};
Ok(self.split_at(a))
}

/// Split the `StringData` at the first occurrence of the given str sequence.
#[inline]
pub fn split_on<'b>(self, needle: &'b str) -> impl Iterator<Item = StringData<'a>> + Send + 'b
where
'a: 'b,
{
self.data
.split_on(needle.as_bytes())
.map(|x| unsafe { Self::from_bytedata_unchecked(x) })
}

#[cfg(feature = "alloc")]
/// Transform any borrowed data into shared data. This is useful when you wish to change the lifetime of the data.
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
Expand Down

0 comments on commit fbc3f6f

Please sign in to comment.