use crate::{Cursor, CursorSink, Kind, SourceOffset};
use bumpalo::collections::Vec;
pub struct CursorOrderedSink<'a, S> {
sink: &'a mut S,
buffer: Vec<'a, Cursor>,
committed_position: SourceOffset,
#[cfg(debug_assertions)]
seen_eof: bool,
}
impl<'a, S: CursorSink> CursorOrderedSink<'a, S> {
pub fn new(bump: &'a bumpalo::Bump, sink: &'a mut S) -> Self {
Self {
sink,
buffer: Vec::new_in(bump),
committed_position: SourceOffset(0),
#[cfg(debug_assertions)]
seen_eof: false,
}
}
pub fn flush(&mut self) {
self.buffer.sort_by_key(|cursor| cursor.span().start());
for cursor in self.buffer.iter() {
self.sink.append(*cursor);
}
if let Some(last_cursor) = self.buffer.last() {
self.committed_position = last_cursor.end_offset();
}
self.buffer.clear();
}
}
impl<'a, S: CursorSink> CursorSink for CursorOrderedSink<'a, S> {
fn append(&mut self, cursor: Cursor) {
#[cfg(debug_assertions)]
{
debug_assert!(!self.seen_eof, "Received cursor after EOF: {:?}", cursor);
if cursor == Kind::Eof {
self.seen_eof = true;
}
}
let cursor_start = cursor.span().start();
if self.buffer.is_empty() || cursor_start.0 >= self.buffer.last().unwrap().span().start().0 {
self.buffer.push(cursor);
} else if cursor_start == self.committed_position {
self.sink.append(cursor);
self.committed_position = cursor.end_offset();
} else {
let insert_pos =
self.buffer.binary_search_by_key(&cursor_start, |c| c.span().start()).unwrap_or_else(|pos| pos);
self.buffer.insert(insert_pos, cursor);
}
while !self.buffer.is_empty() {
let mut overlapping_count = 0;
for cursor in self.buffer.iter() {
if cursor.span().start().0 < self.committed_position.0 {
overlapping_count += 1;
} else {
break;
}
}
if overlapping_count > 0 {
self.buffer.drain(0..overlapping_count);
}
if self.buffer.is_empty() {
break;
}
let mut current_pos = self.committed_position;
let mut emit_count = 0;
for cursor in self.buffer.iter() {
let cursor_start = cursor.span().start();
if cursor_start == current_pos {
current_pos = cursor.end_offset();
emit_count += 1;
} else {
break;
}
}
if emit_count > 0 {
for cursor in self.buffer.drain(0..emit_count) {
self.sink.append(cursor);
}
self.committed_position = current_pos;
} else {
break;
}
}
if cursor == Kind::Eof {
self.flush();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ComponentValues, EmptyAtomSet, Parser, SourceCursor, ToCursors};
use bumpalo::{Bump, collections::Vec as BumpVec};
use css_lexer::Lexer;
use std::fmt::Write;
#[test]
fn test_basic() {
let source_text = "foo bar";
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
let mut parser = Parser::new(&bump, source_text, lexer);
parser.parse_entirely::<ComponentValues>().output.unwrap().to_cursors(&mut ordered_sink);
ordered_sink.flush();
}
let mut result = String::new();
for c in output.iter() {
write!(&mut result, "{}", SourceCursor::from(*c, c.str_slice(source_text))).unwrap();
}
assert_eq!(result, "foo bar");
}
#[test]
fn test_manual_flush() {
use crate::{SourceOffset, Token};
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let cursor1 = Cursor::new(SourceOffset(0), Token::SPACE); let cursor2 = Cursor::new(SourceOffset(10), Token::SPACE); let cursor3 = Cursor::new(SourceOffset(4), Token::SPACE);
ordered_sink.append(cursor2);
ordered_sink.append(cursor1);
ordered_sink.append(cursor3);
ordered_sink.flush();
}
assert_eq!(output.len(), 3);
assert_eq!(output[0].span().start(), SourceOffset(0));
assert_eq!(output[1].span().start(), SourceOffset(4));
assert_eq!(output[2].span().start(), SourceOffset(10));
}
#[test]
fn test_contiguous_eager_emission() {
use crate::{SourceOffset, Token};
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let cursor_at_0 = Cursor::new(SourceOffset(0), Token::SPACE);
let cursor_at_1 = Cursor::new(SourceOffset(1), Token::SPACE);
ordered_sink.append(cursor_at_0);
ordered_sink.append(cursor_at_1);
}
assert_eq!(output.len(), 2);
assert_eq!(output[0].span().start(), SourceOffset(0));
assert_eq!(output[1].span().start(), SourceOffset(1));
}
#[test]
fn test_gap_filling() {
use crate::{SourceOffset, Token};
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let cursor_at_0 = Cursor::new(SourceOffset(0), Token::SPACE); let cursor_at_2 = Cursor::new(SourceOffset(2), Token::SPACE); let cursor_at_1 = Cursor::new(SourceOffset(1), Token::SPACE); ordered_sink.append(cursor_at_0);
ordered_sink.append(cursor_at_2);
ordered_sink.append(cursor_at_1);
}
assert_eq!(output.len(), 3);
assert_eq!(output[0].span().start(), SourceOffset(0));
assert_eq!(output[1].span().start(), SourceOffset(1));
assert_eq!(output[2].span().start(), SourceOffset(2));
}
#[test]
fn test_sequential() {
use crate::{SourceOffset, Token};
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let cursor1 = Cursor::new(SourceOffset(0), Token::SPACE);
let cursor2 = Cursor::new(SourceOffset(1), Token::SPACE);
let cursor3 = Cursor::new(SourceOffset(2), Token::SPACE);
ordered_sink.append(cursor1);
ordered_sink.append(cursor2);
ordered_sink.append(cursor3);
}
assert_eq!(output.len(), 3);
assert_eq!(output[0].span().start(), SourceOffset(0));
assert_eq!(output[1].span().start(), SourceOffset(1));
assert_eq!(output[2].span().start(), SourceOffset(2));
}
#[test]
fn test_varied_order() {
use crate::{SourceOffset, Token};
let bump = Bump::default();
let mut output = BumpVec::new_in(&bump);
{
let mut ordered_sink = CursorOrderedSink::new(&bump, &mut output);
let cursor_at_4 = Cursor::new(SourceOffset(4), Token::SPACE); let cursor_at_0 = Cursor::new(SourceOffset(0), Token::SPACE); let cursor_at_6 = Cursor::new(SourceOffset(6), Token::SPACE); let cursor_at_2 = Cursor::new(SourceOffset(2), Token::SPACE); let cursor_at_1 = Cursor::new(SourceOffset(1), Token::SPACE); let cursor_at_3 = Cursor::new(SourceOffset(3), Token::SPACE); let cursor_at_5 = Cursor::new(SourceOffset(5), Token::SPACE); ordered_sink.append(cursor_at_4);
ordered_sink.append(cursor_at_0);
ordered_sink.append(cursor_at_6);
ordered_sink.append(cursor_at_2);
ordered_sink.append(cursor_at_1);
ordered_sink.append(cursor_at_3);
ordered_sink.append(cursor_at_5);
}
assert_eq!(output.len(), 7);
for i in 0..7 {
assert_eq!(output[i].span().start(), SourceOffset(i as u32));
}
}
}