Sped up file loading and saving.

This commit is contained in:
Nathan Vegdahl 2015-01-01 13:14:32 -08:00
parent 02fdbcf027
commit 7099e4d0d3
4 changed files with 139 additions and 13 deletions

View File

@ -102,6 +102,92 @@ impl Line {
}
/// Creates a new Line from a string.
/// Does not check to see if the string has internal newlines.
/// This is primarily used for efficient loading of files.
pub fn new_from_string_unchecked(text: String) -> Line {
// Initialize Line
let mut tl = Line {
text: text.into_bytes(),
ending: LineEnding::None,
};
// Check for line ending
let mut le_size: uint = 0;
let text_size = tl.text.len();
if tl.text.len() >= 3 {
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-3))} {
// LS
"\u{2028}" => {
tl.ending = LineEnding::LS;
le_size = 3;
},
// PS
"\u{2029}" => {
tl.ending = LineEnding::PS;
le_size = 3;
},
_ => {}
}
}
else if le_size == 0 && tl.text.len() >= 2 {
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-2))} {
// CRLF
"\u{000D}\u{000A}" => {
tl.ending = LineEnding::CRLF;
le_size = 2;
},
_ => {}
}
}
else if le_size == 0 && tl.text.len() >= 1 {
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-1))} {
// LF or CRLF
"\u{000A}" => {
tl.ending = LineEnding::LF;
le_size = 1;
},
// VT
"\u{000B}" => {
tl.ending = LineEnding::VT;
le_size = 1;
},
// FF
"\u{000C}" => {
tl.ending = LineEnding::FF;
le_size = 1;
},
// CR
"\u{000D}" => {
tl.ending = LineEnding::CR;
le_size = 1;
},
// NEL
"\u{0085}" => {
tl.ending = LineEnding::NEL;
le_size = 1;
},
_ => {}
}
}
// Truncate off the line ending, if any
let trunc_size = text_size - le_size;
tl.text.truncate(trunc_size);
// Done!
return tl;
}
/// Returns the total number of unicode graphemes in the line
pub fn grapheme_count(&self) -> uint {
let mut count = grapheme_count(self.as_str());
@ -340,6 +426,10 @@ pub fn str_to_line_ending(g: &str) -> LineEnding {
}
}
pub fn line_ending_to_str(ending: LineEnding) -> &'static str {
LINE_ENDINGS[ending as uint]
}
/// An array of string literals corresponding to the possible
/// unicode line endings.
pub const LINE_ENDINGS: [&'static str, ..9] = ["",

View File

@ -6,7 +6,7 @@ use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
use self::line::{Line};
use string_utils::{is_line_ending};
mod line;
pub mod line;
mod node;
@ -50,6 +50,14 @@ impl Buffer {
}
/// Blindly appends a line to the end of the current text without
/// doing any sanity checks. This is primarily for efficient
/// file loading.
pub fn append_line_unchecked(&mut self, line: Line) {
self.root.append_line_unchecked_recursive(line);
}
/// Removes the lines in line indices [line_a, line_b).
pub fn remove_lines(&mut self, line_a: uint, line_b: uint) {
// Nothing to do

View File

@ -462,6 +462,27 @@ impl BufferNode {
}
pub fn append_line_unchecked_recursive(&mut self, line: Line) {
let mut other_line = Line::new();
if let BufferNodeData::Branch(_, ref mut right) = self.data {
right.append_line_unchecked_recursive(line);
}
else {
if let BufferNodeData::Leaf(ref mut this_line) = self.data {
mem::swap(this_line, &mut other_line);
}
let new_node_a = box BufferNode::new_from_line(other_line);
let new_node_b = box BufferNode::new_from_line(line);
self.data = BufferNodeData::Branch(new_node_a, new_node_b);
}
self.update_stats();
self.rebalance();
}
/// Removes lines in line number range [line_a, line_b)
pub fn remove_lines_recursive(&mut self, line_a: uint, line_b: uint) {
let mut remove_left = false;

View File

@ -2,33 +2,40 @@ use std::io::{IoResult, BufferedReader, BufferedWriter};
use std::io::fs::File;
use std::path::Path;
use buffer::line::{Line, LineEnding, line_ending_to_str};
use buffer::Buffer as TextBuffer;
pub fn load_file_to_buffer(path: &Path) -> IoResult<TextBuffer> {
let mut tb = TextBuffer::new();
let mut f = BufferedReader::new(try!(File::open(path)));
let mut last_line_breaks = true;
loop {
let line = f.read_line();
if let Ok(ref s) = line {
let tbl = tb.len();
tb.insert_text(s.as_slice(), tbl);
}
else {
break;
for line in f.lines() {
let l = Line::new_from_string_unchecked(line.unwrap());
last_line_breaks = l.ending != LineEnding::None;
tb.append_line_unchecked(l);
}
// If the last line had a line break, we need to add a final
// blank line.
if last_line_breaks {
tb.append_line_unchecked(Line::new());
}
// Remove initial blank line
tb.remove_lines(0, 1);
return Ok(tb);
}
pub fn save_buffer_to_file(tb: &TextBuffer, path: &Path) -> IoResult<()> {
// TODO: make save atomic
let mut iter = tb.grapheme_iter();
let mut iter = tb.line_iter();
let mut f = BufferedWriter::new(try!(File::create(path)));
for g in iter {
let _ = f.write_str(g);
for l in iter {
let _ = f.write_str(l.as_str());
let _ = f.write_str(line_ending_to_str(l.ending));
}
return Ok(());