2022-09-14 20:28:12 +00:00
|
|
|
// Copyright (C) 2022 Yehowshua Immanuel
|
|
|
|
// This program is distributed under both the GPLV3 license
|
|
|
|
// and the YEHOWSHUA license, both of which can be found at
|
|
|
|
// the root of the folder containing the sources for this program.
|
2022-06-03 16:06:20 +00:00
|
|
|
use std::collections::VecDeque;
|
2022-08-05 00:32:01 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io;
|
2022-10-26 05:16:39 +00:00
|
|
|
use std::io::BufRead;
|
2022-06-03 16:06:20 +00:00
|
|
|
use std::slice;
|
|
|
|
use std::str;
|
|
|
|
|
2022-07-26 01:16:15 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-07-20 02:05:00 +00:00
|
|
|
pub(super) struct Line(pub(super) usize);
|
2022-07-26 01:16:15 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-07-20 02:05:00 +00:00
|
|
|
pub(super) struct Word(pub(super) usize);
|
2022-07-26 01:16:15 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-07-20 02:05:00 +00:00
|
|
|
pub(super) struct Cursor(pub(super) Line, pub(super) Word);
|
2022-06-03 16:06:20 +00:00
|
|
|
|
2022-08-11 21:35:40 +00:00
|
|
|
pub(super) struct WordReader {
|
2022-08-05 00:32:01 +00:00
|
|
|
reader: io::BufReader<File>,
|
|
|
|
eof: bool,
|
|
|
|
buffers: Vec<String>,
|
|
|
|
curr_line: usize,
|
|
|
|
str_slices: VecDeque<(*const u8, usize, Cursor)>,
|
|
|
|
curr_slice: Option<(*const u8, usize, Cursor)>,
|
2022-06-03 16:06:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl WordReader {
|
2022-08-05 00:32:01 +00:00
|
|
|
pub(super) fn new(file: File) -> WordReader {
|
2022-08-02 23:31:35 +00:00
|
|
|
let reader = io::BufReader::new(file);
|
2022-06-03 16:06:20 +00:00
|
|
|
WordReader {
|
2022-08-05 00:32:01 +00:00
|
|
|
reader: reader,
|
|
|
|
eof: false,
|
|
|
|
buffers: vec![],
|
|
|
|
curr_line: 0,
|
|
|
|
str_slices: VecDeque::new(),
|
|
|
|
curr_slice: None,
|
2022-06-03 16:06:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 21:35:40 +00:00
|
|
|
pub(super) fn next_word(&mut self) -> Option<(&str, Cursor)> {
|
2022-08-04 17:19:52 +00:00
|
|
|
// although reaching the eof is not technically an error, in most cases,
|
|
|
|
// we treat it like one in the rest of the codebase.
|
|
|
|
|
|
|
|
// if there are no more words in the buffer, attempt to read more content
|
2022-06-03 16:06:20 +00:00
|
|
|
// from the file
|
|
|
|
if self.str_slices.is_empty() {
|
|
|
|
self.buffers.clear();
|
|
|
|
|
2022-08-05 00:32:01 +00:00
|
|
|
if self.eof {
|
2022-08-11 21:35:40 +00:00
|
|
|
return None;
|
2022-08-05 00:32:01 +00:00
|
|
|
}
|
2022-06-03 16:06:20 +00:00
|
|
|
|
|
|
|
let num_buffers = 10;
|
|
|
|
|
|
|
|
for buf_idx in 0..num_buffers {
|
|
|
|
self.buffers.push(String::new());
|
|
|
|
self.curr_line += 1;
|
|
|
|
let bytes_read = self.reader.read_line(&mut self.buffers[buf_idx]).unwrap();
|
|
|
|
|
|
|
|
// if we've reached the end of the file on the first attempt to read
|
|
|
|
// a line in this for loop, no further attempts are necessary and we
|
|
|
|
if bytes_read == 0 {
|
2022-08-05 00:32:01 +00:00
|
|
|
self.eof = true;
|
2022-06-03 16:06:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-08-02 23:31:35 +00:00
|
|
|
let words = self.buffers[buf_idx].split_ascii_whitespace();
|
2022-08-05 00:32:01 +00:00
|
|
|
|
2022-06-03 16:06:20 +00:00
|
|
|
for word in words.enumerate() {
|
|
|
|
let (word_idx, word) = word;
|
|
|
|
let position = Cursor(Line(self.curr_line), Word(word_idx + 1));
|
2022-08-05 00:32:01 +00:00
|
|
|
self.str_slices
|
|
|
|
.push_back((word.as_ptr(), word.len(), position))
|
2022-06-03 16:06:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if after we've attempted to read in more content from the file,
|
|
|
|
// there are still no words...
|
|
|
|
if self.str_slices.is_empty() {
|
2022-08-11 21:35:40 +00:00
|
|
|
return None;
|
2022-06-03 16:06:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if we make it here, we return the next word
|
|
|
|
unsafe {
|
|
|
|
let (ptr, len, position) = self.str_slices.pop_front().unwrap();
|
|
|
|
let slice = slice::from_raw_parts(ptr, len);
|
2022-07-26 01:16:15 +00:00
|
|
|
self.curr_slice = Some((ptr, len, position.clone()));
|
2022-08-11 21:35:40 +00:00
|
|
|
return Some((str::from_utf8(slice).unwrap(), position));
|
2022-06-03 16:06:20 +00:00
|
|
|
};
|
|
|
|
}
|
2022-07-26 01:16:15 +00:00
|
|
|
|
2022-08-11 21:35:40 +00:00
|
|
|
pub(super) fn curr_word(&mut self) -> Option<(&str, Cursor)> {
|
2022-07-26 01:16:15 +00:00
|
|
|
match &self.curr_slice {
|
2022-08-05 00:32:01 +00:00
|
|
|
Some(slice) => unsafe {
|
|
|
|
let (ptr, len, position) = slice.clone();
|
|
|
|
let slice = slice::from_raw_parts(ptr, len);
|
2022-08-11 21:35:40 +00:00
|
|
|
return Some((str::from_utf8(slice).unwrap(), position));
|
2022-08-05 00:32:01 +00:00
|
|
|
},
|
2022-08-11 21:35:40 +00:00
|
|
|
None => None,
|
2022-08-04 17:19:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 21:35:40 +00:00
|
|
|
macro_rules! next_word {
|
|
|
|
($word_reader:ident) => {
|
|
|
|
$word_reader.next_word().ok_or(()).map_err(|_| {
|
|
|
|
format!(
|
|
|
|
"Error near {}:{}. Did not expect to reach end of file here.",
|
|
|
|
file!(),
|
|
|
|
line!()
|
|
|
|
)
|
2022-08-05 00:32:01 +00:00
|
|
|
})
|
2022-08-11 21:35:40 +00:00
|
|
|
};
|
2022-08-04 17:19:52 +00:00
|
|
|
}
|
|
|
|
|
2022-08-11 21:35:40 +00:00
|
|
|
macro_rules! curr_word {
|
|
|
|
($word_reader:ident) => {
|
|
|
|
$word_reader.curr_word().ok_or(()).map_err(|_| {
|
|
|
|
format!(
|
2022-08-20 00:13:46 +00:00
|
|
|
"Error near {}:{}. A call to curr_word! shouldn't \
|
|
|
|
fail unless next_word has not yet been invoked.",
|
2022-08-11 21:35:40 +00:00
|
|
|
file!(),
|
|
|
|
line!()
|
|
|
|
)
|
|
|
|
})
|
|
|
|
};
|
2022-08-05 00:32:01 +00:00
|
|
|
}
|
2022-08-11 21:35:40 +00:00
|
|
|
|
|
|
|
pub(super) use curr_word;
|
|
|
|
pub(super) use next_word;
|