use super::DynamicString;
use std::cmp;
pub struct DynamicStringIterator {
active_chunk: Option<Box<DynamicString>>,
chunk_index: usize,
end: Option<usize>,
to_visit: Vec<(Box<DynamicString>, Option<usize>)>,
size_hint: usize,
}
impl DynamicStringIterator {
#[inline(always)]
fn advance_chunk(&mut self) {
match self.to_visit.pop() {
None => {
self.active_chunk = None;
return;
}
Some((chunk, end)) => {
self.active_chunk = Some(chunk);
self.end = end;
self.chunk_index = 0;
}
};
}
#[inline(always)]
fn advance_slice_chunk(&mut self, root: &Box<DynamicString>, start: usize, length: usize) {
self.active_chunk = Some(root.clone());
self.chunk_index += start;
self.end = match self.end {
None => Some(self.chunk_index + length),
Some(end) => Some(cmp::min(start + end, start + length)),
};
}
#[inline(always)]
fn advance_cons_chunk(&mut self, first: &Box<DynamicString>, second: &Box<DynamicString>) {
match self.end {
None => {
self.active_chunk = Some(first.clone());
self.to_visit.push((second.clone(), None));
debug_assert!(self.chunk_index == 0);
}
Some(end) => {
let first_len = first.len();
if first_len <= self.chunk_index {
self.chunk_index -= first_len;
self.end = Some(end - first_len);
self.active_chunk = Some(second.clone());
} else {
self.active_chunk = Some(first.clone());
if end > first_len {
self.to_visit.push((second.clone(), Some(end - first_len)));
}
}
}
}
}
#[inline(always)]
fn actual_len(&self, len: usize) -> usize {
match self.end {
None => len,
Some(n) => cmp::min(n, len),
}
}
}
impl Iterator for DynamicStringIterator {
type Item = u16;
#[inline]
fn next(&mut self) -> Option<u16> {
let part = match &self.active_chunk {
None => return None,
Some(s) => s.clone(),
};
let part = part.as_ref();
match part {
DynamicString::Empty => {
self.advance_chunk();
self.next()
}
DynamicString::SlicedString {
root,
start,
length,
} => {
self.advance_slice_chunk(root, *start, *length);
self.next()
}
DynamicString::ConsString { first, second } => {
self.advance_cons_chunk(first, second);
self.next()
}
DynamicString::SingleOneByteChar(b) => {
self.advance_chunk();
Some(*b as u16)
}
DynamicString::SingleTwoByteChar(b) => {
self.advance_chunk();
Some(*b as u16)
}
DynamicString::SeqOneByteString(vec) => {
if self.chunk_index == self.actual_len(vec.len()) {
self.advance_chunk();
return self.next();
}
let byte = vec[self.chunk_index];
self.chunk_index += 1;
Some(byte as u16)
}
DynamicString::SeqTwoByteString(vec) => {
if self.chunk_index == self.actual_len(vec.len()) {
self.advance_chunk();
return self.next();
}
let byte = vec[self.chunk_index];
self.chunk_index += 1;
Some(byte)
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size_hint, Some(self.size_hint))
}
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
loop {
if n == 0 {
return self.next();
}
let part = match &self.active_chunk {
None => return None,
Some(s) => s.clone(),
};
let len = match part.as_ref() {
DynamicString::Empty => {
self.advance_chunk();
continue;
}
DynamicString::SlicedString {
root,
start,
length,
} => {
self.advance_slice_chunk(root, *start, *length);
continue;
}
DynamicString::ConsString { first, second } => {
self.advance_cons_chunk(first, second);
continue;
}
DynamicString::SingleOneByteChar(_) | DynamicString::SingleTwoByteChar(_) => {
n -= 1;
self.advance_chunk();
continue;
}
DynamicString::SeqOneByteString(v) => self.actual_len(v.len()),
DynamicString::SeqTwoByteString(v) => self.actual_len(v.len()),
};
let index = self.chunk_index + n;
if index < len {
self.chunk_index = index + 1;
return match part.as_ref() {
DynamicString::SeqOneByteString(v) => Some(v[index] as u16),
DynamicString::SeqTwoByteString(v) => Some(v[index]),
_ => unreachable!(),
};
}
let rem = len - self.chunk_index;
n -= rem;
self.advance_chunk();
}
}
}
impl IntoIterator for DynamicString {
type Item = u16;
type IntoIter = DynamicStringIterator;
fn into_iter(self) -> Self::IntoIter {
let len = self.len();
DynamicStringIterator {
active_chunk: Some(Box::new(self)),
chunk_index: 0,
end: None,
to_visit: Vec::with_capacity(4),
size_hint: len,
}
}
}