diff --git a/src/buffer.rs b/src/buffer.rs index 180748a..6b3af29 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,5 +1,4 @@ -use std::cmp::min; - +use std::mem; /// A block of text, contiguous in memory pub struct TextBlock { @@ -7,14 +6,27 @@ pub struct TextBlock { } impl TextBlock { + /// Create a new empty text block. pub fn new() -> TextBlock { TextBlock { data: Vec::::new() } } + /// Return the length of the text block in bytes. + pub fn len(&self) -> uint { + self.data.len() + } + + /// Insert 'text' at byte position 'pos'. + /// NOTE: this makes no attempt to preserve utf8 validity, and may + /// insert text in the middle of multi-byte code points. + // TODO: Do checks to prevent invalidating utf8 sequences pub fn insert_text(&mut self, text: &str, pos: uint) { - let ins = min(pos, self.data.len()); + // Bounds check + if pos > self.data.len() { + panic!("TextBlock::insert_text(): attempt to insert text beyond end of text block."); + } // Grow data size self.data.grow(text.len(), 0); @@ -22,7 +34,7 @@ impl TextBlock { // Move old bytes forward let mut from = self.data.len() - text.len(); let mut to = self.data.len(); - while from > ins { + while from > pos { from -= 1; to -= 1; @@ -30,12 +42,47 @@ impl TextBlock { } // Copy new bytes in - let mut i = ins; + let mut i = pos; for b in text.bytes() { self.data[i] = b; i += 1 } } + + /// Remove the text between byte positions 'pos_a' and 'pos_b'. + /// NOTE: this makes no attempt to preserve utf8 validity, and may + /// remove parts of multi-byte code points. + // TODO: Do checks to prevent invalidating utf8 sequences + pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { + // Bounds checks + if pos_a > pos_b { + panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b."); + } + if pos_b > self.data.len() { + panic!("TextBlock::remove_text(): attempt to remove text beyond the end of text block."); + } + + // Move bytes to fill in the gap left by the removed bytes + let mut from = pos_b; + let mut to = pos_a; + while from < self.data.len() { + self.data[to] = self.data[from]; + + from += 1; + to += 1; + } + + // Remove data from the end + let final_size = self.data.len() + pos_a - pos_b; + self.data.truncate(final_size); + } + + /// Returns an immutable string slice into the text block's memory + pub fn as_str<'a>(&'a self) -> &'a str { + unsafe { + mem::transmute(self.data.as_slice()) + } + } } diff --git a/src/main.rs b/src/main.rs index 980c1e7..5796dce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,11 +32,14 @@ fn main() { let mut tb = TextBlock::new(); - for _ in range(0i, 50) { + for _ in range(0i, 1000) { tb.insert_text("Hello", 0); tb.insert_text("Goodbye", 0); tb.insert_text(args.arg_file.as_slice(), 0); + if tb.len() > 1024 { + tb.remove_text(0, 12 + args.arg_file.len()); + } } - println!("{}", std::str::from_utf8(tb.data.as_slice()).unwrap()); + println!("{}", tb.as_str()); } \ No newline at end of file