Skip to content

Commit

Permalink
fmt + StringQueue::chars
Browse files Browse the repository at this point in the history
  • Loading branch information
TimLuq committed Jun 25, 2024
1 parent 1002d8c commit 9de32eb
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 19 deletions.
13 changes: 13 additions & 0 deletions src/bytedata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,16 @@ impl core::fmt::UpperHex for ByteData<'_> {
Ok(())
}
}

impl<'a> Iterator for ByteData<'a> {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
if self.is_empty() {
return None;
}
let r = self[0];
self.make_sliced(1..);
Some(r)
}
}
7 changes: 6 additions & 1 deletion src/queue/byte_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ pub struct ByteIter<'a, 'b> {
impl<'a, 'b> ByteIter<'a, 'b> {
#[inline]
pub(super) fn new(queue: &'b ByteQueue<'a>) -> Self {
Self { inner: queue.chunks(), chunk: None, offset: 0, len: queue.len() }
Self {
inner: queue.chunks(),
chunk: None,
offset: 0,
len: queue.len(),
}
}

/// Skip the next `n` bytes.
Expand Down
4 changes: 2 additions & 2 deletions src/queue/byte_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use core::{ops::RangeBounds, panic};

use crate::ByteData;

use crate::queue::ChunkIter;
use super::linked_root::LinkedRoot;
use super::byte_iter::ByteIter;
use super::linked_root::LinkedRoot;
use crate::queue::ChunkIter;

/// A queue of byte chunks.
#[cfg_attr(docsrs, doc(cfg(feature = "queue")))]
Expand Down
38 changes: 38 additions & 0 deletions src/queue/char_iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use super::StringQueue;

/// An iterator over the characters of a [`StringQueue`].
pub struct CharIter<'a, 'b> {
bytes: super::ByteIter<'a, 'b>,
}

impl<'a, 'b> CharIter<'a, 'b> {
#[inline]
pub(super) fn new(queue: &'b StringQueue<'a>) -> Self {
Self {
bytes: super::ByteIter::new(queue.as_inner()),
}
}
}

impl<'a, 'b> Iterator for CharIter<'a, 'b> {
type Item = char;

fn next(&mut self) -> Option<Self::Item> {
let b0 = self.bytes.next()?;
let (mut ch, expects) = match b0 {
b0 if b0 & 0b1000_0000 == 0 => (b0 as u32, 0),
b0 if b0 & 0b1110_0000 == 0b1100_0000 => (b0 as u32 & 0b0001_1111, 1),
b0 if b0 & 0b1111_0000 == 0b1110_0000 => (b0 as u32 & 0b0000_1111, 2),
b0 if b0 & 0b1111_1000 == 0b1111_0000 => (b0 as u32 & 0b0000_0111, 3),
_ => return None,
};
for _ in 0..expects {
let b = self.bytes.next()?;
if b & 0b1100_0000 != 0b1000_0000 {
panic!("CharIter: Invalid UTF-8 continuation byte");
}
ch = (ch << 6) | (b as u32 & 0b0011_1111);
}
Some(unsafe { core::char::from_u32_unchecked(ch) })
}
}
12 changes: 8 additions & 4 deletions src/queue/chunk_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::ByteData;
use super::linked_root::LinkedRoot;

/// An iterator over the chunks of a [`ByteQueue`].
///
///
/// [`ByteQueue`]: crate::ByteQueue
#[cfg_attr(docsrs, doc(cfg(feature = "queue")))]
pub struct ChunkIter<'a>(LinkedRoot<'a>);
Expand Down Expand Up @@ -51,7 +51,7 @@ impl<'a> ExactSizeIterator for ChunkIter<'a> {
impl<'a> core::iter::FusedIterator for ChunkIter<'a> {}

/// An iterator over the chunks of a [`ByteQueue`].
///
///
/// [`ByteQueue`]: crate::ByteQueue
#[cfg_attr(docsrs, doc(cfg(feature = "queue")))]
pub struct StrChunkIter<'a>(LinkedRoot<'a>);
Expand All @@ -67,7 +67,9 @@ impl<'a> Iterator for StrChunkIter<'a> {
type Item = crate::StringData<'a>;

fn next(&mut self) -> Option<Self::Item> {
self.0.pop_front().map(|x| unsafe { crate::StringData::from_bytedata_unchecked(x) })
self.0
.pop_front()
.map(|x| unsafe { crate::StringData::from_bytedata_unchecked(x) })
}

#[inline]
Expand All @@ -84,7 +86,9 @@ impl<'a> Iterator for StrChunkIter<'a> {
impl<'a> DoubleEndedIterator for StrChunkIter<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.pop_back().map(|x| unsafe { crate::StringData::from_bytedata_unchecked(x) })
self.0
.pop_back()
.map(|x| unsafe { crate::StringData::from_bytedata_unchecked(x) })
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/queue/linked_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ pub struct LinkedIter<'a: 'b, 'b> {
#[cfg(feature = "alloc")]
impl<'a: 'b, 'b> LinkedIter<'a, 'b> {
#[inline]
pub(super) const fn new(chamber: Option<&'b crate::ByteData<'a>>, node: Option<&'b super::linked_node_leaf::LinkedNodeLeaf<'a>>) -> Self {
Self { chamber, node, offset: 0 }
pub(super) const fn new(
chamber: Option<&'b crate::ByteData<'a>>,
node: Option<&'b super::linked_node_leaf::LinkedNodeLeaf<'a>>,
) -> Self {
Self {
chamber,
node,
offset: 0,
}
}

fn item_len(&self) -> usize {
Expand Down
1 change: 0 additions & 1 deletion src/queue/linked_node_leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::ByteData;

use super::linked_node_data::LinkedNodeData;


pub(super) struct LinkedNodeLeaf<'a> {
pub(super) prev: *mut LinkedNodeLeaf<'a>,
pub(super) data: LinkedNodeData<'a>,
Expand Down
7 changes: 3 additions & 4 deletions src/queue/linked_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ impl<'a> LinkedRoot<'a> {
pub(super) const fn len(&self) -> usize {
self.data.len as usize
}

#[inline]
fn first_mut(&mut self) -> Option<&mut super::linked_node_data::LinkedNodeData<'a>> {
Some(&mut self.data)
}

#[inline]
fn last_mut(&mut self) -> Option<&mut super::linked_node_data::LinkedNodeData<'a>> {
Some(&mut self.data)
Expand Down Expand Up @@ -171,7 +171,7 @@ impl<'a> LinkedRoot<'a> {
}
Some(r)
}

pub(super) fn pop_front(&mut self) -> Option<ByteData<'a>> {
if self.count == 0 {
return None;
Expand Down Expand Up @@ -226,7 +226,6 @@ impl<'a> LinkedRoot<'a> {
}

impl<'a> LinkedRoot<'a> {

pub(super) fn push_back(&mut self, mut data: ByteData<'a>) {
if data.is_empty() {
return;
Expand Down
6 changes: 4 additions & 2 deletions src/queue/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! # Queue
//!
//!
//! This module contains the queue data structure and its iterators.
//!
//!
//! The queue is a list of byte slices, which allows for efficient appending and consuming of byte data.
mod byte_queue;
mod string_queue;

mod byte_iter;
mod char_iter;
mod chunk_iter;

mod linked_iter;
Expand All @@ -18,6 +19,7 @@ mod linked_root;

pub use byte_iter::ByteIter;
pub use byte_queue::ByteQueue;
pub use char_iter::CharIter;
pub use chunk_iter::{ChunkIter, StrChunkIter};
pub use linked_iter::LinkedIter;
pub use string_queue::StringQueue;
76 changes: 73 additions & 3 deletions src/queue/string_queue.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::StringData;
use super::ByteQueue;
use crate::StringData;

/// A queue of strings.
pub struct StringQueue<'a> {
Expand All @@ -10,13 +10,42 @@ impl<'a> StringQueue<'a> {
/// Create a new empty `StringQueue`.
#[inline]
pub const fn new() -> Self {
Self { queue: ByteQueue::new() }
Self {
queue: ByteQueue::new(),
}
}

/// Create a new `StringQueue` with a single item.
#[inline]
pub const fn with_item(data: StringData<'a>) -> Self {
Self { queue: ByteQueue::with_item(data.into_bytedata()) }
Self {
queue: ByteQueue::with_item(data.into_bytedata()),
}
}

#[inline]
pub(super) const fn as_inner(&self) -> &ByteQueue<'a> {
&self.queue
}

/// Checks if the queue is full. When the feature `alloc` is enabled, this will always return `false`.
#[inline]
pub const fn is_full(&self) -> bool {
self.queue.is_full()
}

/// Append string to the queue.
#[inline]
pub fn push_back(&mut self, data: impl Into<StringData<'a>>) {
let data: StringData = data.into();
self.queue.push_back(data.into_bytedata());
}

/// Prepend string into the queue.
#[inline]
pub fn push_front(&mut self, data: impl Into<StringData<'a>>) {
let data = data.into();
self.queue.push_front(data.into_bytedata());
}

/// Pop the first item from the queue.
Expand Down Expand Up @@ -72,6 +101,47 @@ impl<'a> StringQueue<'a> {
pub fn into_iter(self) -> super::StrChunkIter<'a> {
super::StrChunkIter::new(self.queue.queue)
}

/// Slices the queue and returns a new queue that represents the given range.
/// Panics if the range boundary is invalid UTF-8.
pub fn slice(&self, range: impl core::ops::RangeBounds<usize>) -> Self {
let slic = self.queue.slice(range);
if slic.is_empty() {
return Self::new();
}
let f = slic.front().unwrap();
if f[0] & 0b1100_0000 == 0b1000_0000 {
panic!("StringQueue: Invalid UTF-8 start in range");
}
let b = slic.back().unwrap();
let end_byte = b[b.len() - 1];
if end_byte & 0b1100_0000 == 0b1100_0000 {
panic!("StringQueue: Invalid UTF-8 end in range");
}
if end_byte & 0b1100_0000 == 0b1000_0000 {
// compute backwards to find the start of the char to see if the number of bytes is correct
let mut i = b.len() - 2;
while b[i] & 0b1100_0000 == 0b1000_0000 {
i -= 1;
}
let char_len = b.len() - i;
if char_len == 2 && end_byte & 0b1110_0000 != 0b1100_0000 {
panic!("StringQueue: Invalid UTF-8 end in range");
}
if char_len == 3 && end_byte & 0b1111_0000 != 0b1110_0000 {
panic!("StringQueue: Invalid UTF-8 end in range");
}
if char_len == 4 && end_byte & 0b1111_1000 != 0b1111_0000 {
panic!("StringQueue: Invalid UTF-8 end in range");
}
}
Self { queue: slic }
}

/// Iterates over each character in the queue.
pub fn chars(&self) -> super::CharIter<'a, '_> {
super::char_iter::CharIter::new(self)
}
}

impl<'a> From<StringData<'a>> for StringQueue<'a> {
Expand Down

0 comments on commit 9de32eb

Please sign in to comment.