use std::sync::Arc;
use smallvec::SmallVec;
use crate::crlf;
use crate::rope::Rope;
use crate::tree::{Node, NodeChildren, NodeText, MAX_BYTES, MAX_CHILDREN, MIN_BYTES};
#[derive(Debug, Clone)]
pub struct RopeBuilder {
stack: SmallVec<[Arc<Node>; 4]>,
buffer: String,
last_chunk_len_bytes: usize,
}
impl RopeBuilder {
pub fn new() -> Self {
RopeBuilder {
stack: {
let mut stack = SmallVec::new();
stack.push(Arc::new(Node::new()));
stack
},
buffer: String::new(),
last_chunk_len_bytes: 0,
}
}
pub fn append(&mut self, chunk: &str) {
self.append_internal(chunk, false);
}
pub fn finish(mut self) -> Rope {
self.append_internal("", true);
self.finish_internal(true)
}
pub(crate) fn build_at_once(mut self, chunk: &str) -> Rope {
self.append_internal(chunk, true);
self.finish_internal(true)
}
#[doc(hidden)]
pub fn _append_chunk(&mut self, contents: &str) {
self.append_leaf_node(Arc::new(Node::Leaf(NodeText::from_str(contents))));
}
#[doc(hidden)]
pub fn _finish_no_fix(self) -> Rope {
self.finish_internal(false)
}
fn append_internal(&mut self, chunk: &str, is_last_chunk: bool) {
let mut chunk = chunk;
while !chunk.is_empty() || (!self.buffer.is_empty() && is_last_chunk) {
let (leaf_text, remainder) = self.get_next_leaf_text(chunk, is_last_chunk);
chunk = remainder;
self.last_chunk_len_bytes = chunk.len();
match leaf_text {
NextText::None => break,
NextText::UseBuffer => {
let leaf_text = NodeText::from_str(&self.buffer);
self.append_leaf_node(Arc::new(Node::Leaf(leaf_text)));
self.buffer.clear();
}
NextText::String(s) => {
self.append_leaf_node(Arc::new(Node::Leaf(NodeText::from_str(s))));
}
}
}
}
fn finish_internal(mut self, fix_tree: bool) -> Rope {
let mut stack_idx = self.stack.len() - 1;
while stack_idx >= 1 {
let node = self.stack.pop().unwrap();
if let Node::Internal(ref mut children) = *Arc::make_mut(&mut self.stack[stack_idx - 1])
{
children.push((node.text_info(), node));
} else {
unreachable!();
}
stack_idx -= 1;
}
let mut rope = Rope {
root: self.stack.pop().unwrap(),
};
if fix_tree {
Arc::make_mut(&mut rope.root).zip_fix_right();
if self.last_chunk_len_bytes < MIN_BYTES
&& self.last_chunk_len_bytes != rope.len_bytes()
{
let idx = rope.len_chars()
- rope.byte_to_char(rope.len_bytes() - self.last_chunk_len_bytes);
Arc::make_mut(&mut rope.root).fix_tree_seam(idx);
}
rope.pull_up_singular_nodes();
}
return rope;
}
#[inline(always)]
fn get_next_leaf_text<'a>(
&mut self,
text: &'a str,
is_last_chunk: bool,
) -> (NextText<'a>, &'a str) {
assert!(
self.buffer.len() < MAX_BYTES,
"RopeBuilder: buffer is already full when receiving a chunk! \
This should never happen!",
);
if self.buffer.is_empty() && text.len() >= MAX_BYTES {
let split_idx = crlf::find_good_split(
MAX_BYTES.min(text.len() - 1), text.as_bytes(),
true,
);
return (NextText::String(&text[..split_idx]), &text[split_idx..]);
}
else if (text.len() + self.buffer.len()) >= MAX_BYTES {
let mut split_idx =
crlf::find_good_split(MAX_BYTES - self.buffer.len(), text.as_bytes(), true);
if split_idx == text.len() && text.as_bytes()[text.len() - 1] == 0x0D {
split_idx -= 1;
};
self.buffer.push_str(&text[..split_idx]);
return (NextText::UseBuffer, &text[split_idx..]);
}
else {
if is_last_chunk {
if self.buffer.is_empty() {
return if text.is_empty() {
(NextText::None, "")
} else {
(NextText::String(text), "")
};
} else {
self.buffer.push_str(text);
return (NextText::UseBuffer, "");
}
}
else {
self.buffer.push_str(text);
return (NextText::None, "");
}
}
}
fn append_leaf_node(&mut self, leaf: Arc<Node>) {
let last = self.stack.pop().unwrap();
match *last {
Node::Leaf(_) => {
if last.leaf_text().is_empty() {
self.stack.push(leaf);
} else {
let mut children = NodeChildren::new();
children.push((last.text_info(), last));
children.push((leaf.text_info(), leaf));
self.stack.push(Arc::new(Node::Internal(children)));
}
}
Node::Internal(_) => {
self.stack.push(last);
let mut left = leaf;
let mut stack_idx = (self.stack.len() - 1) as isize;
loop {
if stack_idx < 0 {
let mut children = NodeChildren::new();
children.push((left.text_info(), left));
self.stack.insert(0, Arc::new(Node::Internal(children)));
break;
} else if self.stack[stack_idx as usize].child_count() < (MAX_CHILDREN - 1) {
Arc::make_mut(&mut self.stack[stack_idx as usize])
.children_mut()
.push((left.text_info(), left));
break;
} else {
left = Arc::new(Node::Internal(
Arc::make_mut(&mut self.stack[stack_idx as usize])
.children_mut()
.push_split((left.text_info(), left)),
));
std::mem::swap(&mut left, &mut self.stack[stack_idx as usize]);
stack_idx -= 1;
}
}
}
}
}
}
impl Default for RopeBuilder {
fn default() -> Self {
Self::new()
}
}
enum NextText<'a> {
None,
UseBuffer,
String(&'a str),
}
#[cfg(test)]
mod tests {
use super::*;
const TEXT: &str = "Hello there! How're you doing?\r\nIt's \
a fine day, isn't it?\r\nAren't you glad \
we're alive?\r\nこんにちは、みんなさん!";
#[test]
fn rope_builder_01() {
let mut b = RopeBuilder::new();
b.append("Hello there! How're you doing?\r");
b.append("\nIt's a fine ");
b.append("d");
b.append("a");
b.append("y,");
b.append(" ");
b.append("isn't it?");
b.append("\r");
b.append("\nAren't you ");
b.append("glad we're alive?\r");
b.append("\n");
b.append("こんにち");
b.append("は、みんなさ");
b.append("ん!");
let r = b.finish();
assert_eq!(r, TEXT);
r.assert_integrity();
r.assert_invariants();
}
#[test]
fn rope_builder_default_01() {
let mut b = RopeBuilder::default();
b.append("Hello there! How're you doing?\r");
b.append("\nIt's a fine day, isn't it?\r\nAren't you ");
b.append("glad we're alive?\r\nこんにちは、みんなさん!");
let r = b.finish();
assert_eq!(r, TEXT);
r.assert_integrity();
r.assert_invariants();
}
}