Remove lvalues and rvalues from context

This commit is contained in:
Guojie Luo 2022-05-21 00:24:15 +08:00
parent 88632042e2
commit e53cee4959
12 changed files with 223 additions and 183 deletions

34
Cargo.lock generated
View File

@ -126,9 +126,8 @@ name = "cst-to-llhd"
version = "0.1.0"
dependencies = [
"clap",
"indexmap",
"json",
"linked-hash-map",
"linked_hash_set",
"llhd",
"log",
"num",
@ -155,6 +154,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -183,6 +188,16 @@ dependencies = [
"quick-error",
]
[[package]]
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.9.0"
@ -216,21 +231,6 @@ version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "linked_hash_set"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "llhd"
version = "0.16.0"

View File

@ -12,8 +12,7 @@ llhd = "*"
log = "*"
num = "0.3.1"
num-bigint = "0.3.3"
linked_hash_set = "*"
linked-hash-map = "*"
indexmap = "*"
pretty_env_logger = "0.4"
[[bin]]

View File

@ -1,9 +1,9 @@
use crate::{
cst::{Expression, ModuleContext, Statement, SymbolKind, Tag, UnitContext},
tools::Tools,
tools::{EventControl, Tools},
};
use indexmap::{map::IndexMap, set::IndexSet};
use json::JsonValue;
use linked_hash_map::LinkedHashMap as HashMap;
use llhd::{
ir::{Signature, UnitId, UnitKind, UnitName, Value},
ty::{int_ty, signal_ty},
@ -55,41 +55,44 @@ impl AlwaysStatement {
(json_event_control[0], &json_children[1])
};
// first traversal
assert!(context.unit_ctx.is_empty());
Self::gen_event_expression_list(json_event_expression_list, context);
Statement::codegen(json_statement, context);
let proc_names = EventControl::process_names(json);
assert!(context.unit_ctx.raw_name_to_arg.is_empty());
let proc_signature = {
let mut ret = Signature::new();
let mut sig = Signature::new();
for lvalue in &context.unit_ctx.lvalues {
for lvalue in &proc_names.lvalues {
if context.symbol.contains_key(lvalue) {
let symbol = &context.symbol[lvalue];
assert_ne!(symbol.kind, SymbolKind::Param);
let width = symbol.value.width.clone();
let arg = ret.add_output(signal_ty(int_ty(width)));
let arg = sig.add_output(signal_ty(int_ty(width)));
context.unit_ctx.raw_name_to_arg.insert(lvalue.clone(), arg);
} else {
error!("FIXME: symbol '{}' not found", lvalue);
}
}
let rvalues: Vec<String> = context
.unit_ctx
.rvalues
.iter()
.filter(|&x| !context.unit_ctx.lvalues.contains(x))
.map(|x| x.clone())
let mut rvalues = [
proc_names.posedges.clone(),
proc_names.negedges.clone(),
proc_names.levels.clone(),
proc_names.ctrl_values.clone(),
proc_names.rvalues.clone(),
]
.iter()
.fold(IndexSet::new(), |acc, hs| acc.union(hs).cloned().collect());
rvalues = rvalues
.into_iter()
.filter(|x| !proc_names.lvalues.contains(x))
.collect();
for rvalue in &rvalues {
if context.symbol.contains_key(rvalue) {
let symbol = &context.symbol[rvalue];
if symbol.kind != SymbolKind::Param {
let width = symbol.value.width.clone();
let arg = ret.add_input(signal_ty(int_ty(width)));
let arg = sig.add_input(signal_ty(int_ty(width)));
context.unit_ctx.raw_name_to_arg.insert(rvalue.clone(), arg);
}
} else {
@ -97,7 +100,7 @@ impl AlwaysStatement {
}
}
ret
sig
};
context.unit_ctx.new_data(
@ -110,8 +113,15 @@ impl AlwaysStatement {
proc_signature,
);
// second traversal
Self::gen_event_expression_list(json_event_expression_list, context);
Self::gen_event_expression_list(
json_event_expression_list,
proc_names
.rvalues
.union(&proc_names.ctrl_values)
.cloned()
.collect(),
context,
);
Statement::codegen(json_statement, context);
Tools::beautify(&mut context.unit_ctx);
@ -120,8 +130,12 @@ impl AlwaysStatement {
context.module.add_unit(context.unit_ctx.drop_data())
}
fn gen_event_expression_list<'a>(json: &'a JsonValue, context: &mut ModuleContext<'a>) {
let mut sensitivity_list = HashMap::new();
fn gen_event_expression_list<'a>(
json: &'a JsonValue,
rvalues: IndexSet<String>,
context: &mut ModuleContext<'a>,
) {
let mut sensitivity_list = IndexMap::new();
let json_expressions = Tools::match_tags(
vec![json],
@ -133,18 +147,15 @@ impl AlwaysStatement {
match json_children[0]["tag"].as_str() {
Some(Tag::POS_EDGE) => {
let symbol =
Expression::gen_reference_name(&json_children[1]["children"][0], context);
let symbol = Expression::gen_reference_name(&json_children[1]["children"][0]);
sensitivity_list.insert(symbol, Tag::POS_EDGE);
}
Some(Tag::NEG_EDGE) => {
let symbol =
Expression::gen_reference_name(&json_children[1]["children"][0], context);
let symbol = Expression::gen_reference_name(&json_children[1]["children"][0]);
sensitivity_list.insert(symbol, Tag::NEG_EDGE);
}
Some(Tag::EXPRESSION) => {
let symbol =
Expression::gen_reference_name(&json_children[0]["children"][0], context);
let symbol = Expression::gen_reference_name(&json_children[0]["children"][0]);
sensitivity_list.insert(symbol, Tag::EVENT_EXPRESSION);
}
_ => panic!("unknown error at CST node '{}'", json),
@ -156,12 +167,11 @@ impl AlwaysStatement {
let raw_name_to_value = &mut unit_ctx.raw_name_to_value;
let raw_name_to_shadow = &mut unit_ctx.raw_name_to_shadow;
let raw_name_to_arg = &unit_ctx.raw_name_to_arg;
let rvalues = &unit_ctx.rvalues;
let mut builder = UnitContext::builder(&mut unit_ctx.data);
let mut arg_value_to_raw_name = HashMap::new();
let mut raw_name_to_prb_init = HashMap::new();
let mut arg_value_to_raw_name = IndexMap::new();
let mut raw_name_to_prb_init = IndexMap::new();
for (name, arg) in raw_name_to_arg {
let value = builder.arg_value(*arg);
context
@ -202,9 +212,9 @@ impl AlwaysStatement {
builder.append_to(bb_init);
let inputs: Vec<Value> = builder.unit().input_args().collect();
let events: Vec<Value> = inputs
.iter()
.filter(|x| sensitivity_list.contains_key(&arg_value_to_raw_name[&x]))
.map(|&x| {
.into_iter()
.filter(|&x| sensitivity_list.contains_key(&arg_value_to_raw_name[&x]))
.map(|x| {
let raw_name = &arg_value_to_raw_name[&x];
let prb = builder.ins().prb(x);
raw_name_to_prb_init.insert(raw_name, prb);

View File

@ -1,6 +1,5 @@
use indexmap::map::IndexMap;
use json::JsonValue;
use linked_hash_map::LinkedHashMap;
use linked_hash_set::LinkedHashSet;
use llhd::{
ir::{
Arg, Block, Inst, Module, Signature, Unit, UnitBuilder, UnitData, UnitKind, UnitName, Value,
@ -13,15 +12,12 @@ use std::{collections::HashMap, fmt};
pub struct ModuleContext<'a> {
pub name: String,
pub module: Module,
pub symbol: LinkedHashMap<String, SymbolInfo<'a>>,
pub symbol: IndexMap<String, SymbolInfo<'a>>,
pub unit_ctx: UnitContext,
pub syntax_table: SyntaxTable<'a>,
}
pub struct UnitContext {
pub lvalues: LinkedHashSet<String>,
pub rvalues: LinkedHashSet<String>,
pub data: Option<UnitData>,
pub raw_name_to_arg: HashMap<String, Arg>,
@ -71,7 +67,7 @@ impl<'a> ModuleContext<'a> {
Self {
name: String::new(),
module: Module::new(),
symbol: LinkedHashMap::new(),
symbol: IndexMap::new(),
unit_ctx: UnitContext::new(),
syntax_table: SyntaxTable::new(json),
}
@ -81,8 +77,6 @@ impl<'a> ModuleContext<'a> {
impl UnitContext {
pub fn new() -> Self {
Self {
lvalues: LinkedHashSet::new(),
rvalues: LinkedHashSet::new(),
data: None,
raw_name_to_arg: HashMap::new(),
raw_name_to_value: HashMap::new(),
@ -93,8 +87,6 @@ impl UnitContext {
}
pub fn drop(&mut self) -> Self {
let ret = Self {
lvalues: self.lvalues.iter().map(|x| x.clone()).collect(),
rvalues: self.rvalues.iter().map(|x| x.clone()).collect(),
data: self.data.take(),
raw_name_to_arg: self.raw_name_to_arg.drain().collect(),
raw_name_to_value: self.raw_name_to_value.drain().collect(),
@ -114,8 +106,6 @@ impl UnitContext {
}
pub fn clear(&mut self) {
self.lvalues.clear();
self.rvalues.clear();
self.raw_name_to_arg.clear();
self.raw_name_to_value.clear();
self.raw_name_to_shadow.clear();
@ -125,8 +115,6 @@ impl UnitContext {
pub fn is_empty(&self) -> bool {
self.data.is_none()
&& self.lvalues.is_empty()
&& self.rvalues.is_empty()
&& self.raw_name_to_arg.is_empty()
&& self.raw_name_to_value.is_empty()
&& self.raw_name_to_shadow.is_empty()

View File

@ -15,8 +15,8 @@ use log::{debug, error, info, trace, warn};
pub struct Expression {}
impl Expression {
pub fn gen_reference_name(json: &JsonValue, context: &mut ModuleContext) -> String {
Self::gen_reference_call(json, context).0
pub fn gen_reference_name(json: &JsonValue) -> String {
Self::gen_reference_call(json).0
}
pub fn codegen<'a>(json: &'a JsonValue, context: &mut ModuleContext<'a>) -> Option<Value> {
@ -44,10 +44,7 @@ impl Expression {
}
}
fn gen_reference_call(
json: &JsonValue,
context: &mut ModuleContext,
) -> (String, Option<(usize, usize)>) {
fn gen_reference_call(json: &JsonValue) -> (String, Option<(usize, usize)>) {
let json_symbol_identifier = Tools::match_tags(
vec![json],
vec![
@ -97,13 +94,12 @@ impl Expression {
};
let symbol = &json_symbol_identifier[0]["text"];
context.unit_ctx.rvalues.insert(symbol.to_string());
(symbol.to_string(), range)
}
fn gen_reference_value(json: &JsonValue, context: &mut ModuleContext) -> Option<Value> {
let (symbol, range) = Self::gen_reference_call(json, context);
let (symbol, range) = Self::gen_reference_call(json);
if context.unit_ctx.data.is_some() {
let unit_ctx = &mut context.unit_ctx;

View File

@ -259,7 +259,7 @@ impl<'a> ModuleDeclaration {
named_port_symbol_path.to_vec(),
);
if json_symbol.len() == 1 {
let symbol = &Expression::gen_reference_name(json_symbol[0], context);
let symbol = &Expression::gen_reference_name(json_symbol[0]);
Some(context.unit_ctx.raw_name_to_value[symbol])
} else {
let json_number = Tools::match_tags(
@ -303,7 +303,7 @@ impl<'a> ModuleDeclaration {
.enumerate()
{
let json_expr = pos_port_list[&index];
let arg = &Expression::gen_reference_name(json_expr, context);
let arg = &Expression::gen_reference_name(json_expr);
let v = context.unit_ctx.raw_name_to_value[arg];
if arg_info.kind == SymbolKind::Input {
inputs.push(v);

View File

@ -43,7 +43,6 @@ impl NetVariableAssignment {
};
let lvalue_name = &json_lpvalue["text"].to_string();
context.unit_ctx.lvalues.insert(lvalue_name.clone());
let lvalue = context.unit_ctx.raw_name_to_value.get(lvalue_name);
if lvalue.is_some() {
assert!(context.unit_ctx.data.is_some());

View File

@ -36,7 +36,6 @@ impl NonblockingAssignmentStatement {
(json_symbol_identifier[0], &json_children[2])
};
let lvalue_name = &json_lpvalue["text"].to_string();
context.unit_ctx.lvalues.insert(lvalue_name.clone());
if context.unit_ctx.raw_name_to_value.contains_key(lvalue_name) {
assert!(context.unit_ctx.raw_name_to_arg.contains_key(lvalue_name));
assert!(context.unit_ctx.data.is_some());

View File

@ -0,0 +1,67 @@
use std::{
fs::File,
io::{BufRead, BufReader},
};
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
pub struct CodeReporter {
file_name: String,
line_breakers: Vec<usize>,
}
impl CodeReporter {
pub fn new(file_name: &str) -> Self {
Self {
file_name: file_name.to_string(),
line_breakers: Self::read_line_breakers(file_name),
}
}
pub fn line_no(&self, key: usize) -> usize {
self.line_no_rec(key, 0, self.line_breakers.len() - 1)
}
pub fn line(&self, line_no: usize) -> String {
let reader = BufReader::new(
File::open(self.file_name.as_str())
.expect(format!(r#"file "{}" not found"#, self.file_name.as_str()).as_str()),
);
reader.lines().nth(line_no).unwrap().unwrap()
}
pub fn line_count(&self) -> usize {
self.line_breakers.len()
}
fn read_line_breakers(file_name: &str) -> Vec<usize> {
let reader = BufReader::new(
File::open(file_name).expect(format!(r#"file "{}" not found"#, file_name).as_str()),
);
let mut line_breakers = Vec::new();
line_breakers.push(0);
for line in reader.lines() {
let line_no = 1 + line_breakers.last().unwrap() + line.unwrap().len();
line_breakers.push(line_no);
}
line_breakers
}
fn line_no_rec(&self, key: usize, left: usize, right: usize) -> usize {
if left + 1 >= right {
left
} else {
let mid = (left + right) / 2;
if key < self.line_breakers[mid] {
self.line_no_rec(key, left, mid)
} else {
self.line_no_rec(key, mid, right)
}
}
}
}

View File

@ -1,54 +1,74 @@
use crate::{cst::Tag, tools::Tools};
use indexmap::set::IndexSet;
use json::JsonValue;
use std::{collections::HashSet, iter::FromIterator};
use std::iter::FromIterator;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
pub struct ProcessNames {
pub posedges: IndexSet<String>,
pub negedges: IndexSet<String>,
pub levels: IndexSet<String>,
pub lvalues: IndexSet<String>,
pub rvalues: IndexSet<String>,
pub ctrl_values: IndexSet<String>,
}
impl ProcessNames {
pub fn new() -> Self {
Self {
posedges: IndexSet::new(),
negedges: IndexSet::new(),
levels: IndexSet::new(),
lvalues: IndexSet::new(),
rvalues: IndexSet::new(),
ctrl_values: IndexSet::new(),
}
}
}
pub struct EventControl {}
impl EventControl {
pub fn clock_and_reset_names(json: &JsonValue) -> (HashSet<String>, HashSet<String>) {
let mut clock_names: HashSet<String> = HashSet::new();
let mut reset_names: HashSet<String> = HashSet::new();
pub fn clock_and_reset_names(json: &JsonValue) -> (IndexSet<String>, IndexSet<String>) {
let mut clock_names: IndexSet<String> = IndexSet::new();
let mut reset_names: IndexSet<String> = IndexSet::new();
let json_always_statements = Tools::collect_tag(json, Tag::ALWAYS_STATEMENT);
for json_always_statement in json_always_statements {
let (json_events, json_body) = Self::events_and_body(json_always_statement);
let (json_posedge_names, json_negedge_names, json_level_names) =
Self::event_names(json_events);
let (posedge_names, negedge_names, level_names) = Self::event_names(json_events);
let (json_lvalues, json_rvalues, json_ctrl_values) = Self::value_names(json_body);
let json_top_ctrl_values = Self::top_ctrl_values(json_body);
let (lvalues, rvalues, ctrl_values) = Self::value_names(json_body);
let top_ctrl_values = Self::top_ctrl_values(json_body);
let json_edge_names = [json_posedge_names.clone(), json_negedge_names.clone()].concat();
let json_sens_names = [json_edge_names.clone(), json_level_names.clone()].concat();
let edge_names: IndexSet<_> = posedge_names.union(&negedge_names).cloned().collect();
let sens_names: IndexSet<_> = edge_names.union(&level_names).cloned().collect();
let json_data_values: HashSet<String> =
json_lvalues.union(&json_rvalues).cloned().collect();
let json_all_values: HashSet<String> =
json_data_values.union(&json_ctrl_values).cloned().collect();
let data_values: IndexSet<String> = lvalues.union(&rvalues).cloned().collect();
let all_values: IndexSet<String> = data_values.union(&ctrl_values).cloned().collect();
let local_clock_names: HashSet<String> = HashSet::from_iter(
json_edge_names
let local_clock_names: IndexSet<String> = IndexSet::from_iter(
edge_names
.clone()
.into_iter()
.filter(|x| !json_all_values.contains(x)),
.filter(|x| !all_values.contains(x)),
);
let local_reset_names: HashSet<String> = HashSet::from_iter(
json_sens_names
let local_reset_names: IndexSet<String> = IndexSet::from_iter(
sens_names
.clone()
.into_iter()
.filter(|x| !json_data_values.contains(x) && json_top_ctrl_values.contains(x)),
.filter(|x| !data_values.contains(x) && top_ctrl_values.contains(x)),
);
trace!("edge_names {:?}", json_edge_names);
trace!("sens_names {:?}", json_sens_names);
trace!("data_values {:?}", json_data_values);
trace!("all_values {:?}", json_all_values);
trace!("top_ctrl_values {:?}", json_top_ctrl_values);
trace!("edge_names {:?}", edge_names);
trace!("sens_names {:?}", sens_names);
trace!("data_values {:?}", data_values);
trace!("all_values {:?}", all_values);
trace!("top_ctrl_values {:?}", top_ctrl_values);
clock_names = clock_names.union(&local_clock_names).cloned().collect();
reset_names = reset_names.union(&local_reset_names).cloned().collect();
@ -59,6 +79,24 @@ impl EventControl {
(clock_names, reset_names)
}
pub fn process_names(json_always_statement: &JsonValue) -> ProcessNames {
let (json_events, json_body) = Self::events_and_body(json_always_statement);
let (posedge_names, negedge_names, level_names) = Self::event_names(json_events);
let (lvalues, rvalues, ctrl_values) = Self::value_names(json_body);
let mut names = ProcessNames::new();
names.posedges = posedge_names;
names.negedges = negedge_names;
names.levels = level_names;
names.lvalues = lvalues.into_iter().collect();
names.rvalues = rvalues.into_iter().collect();
names.ctrl_values = ctrl_values.into_iter().collect();
names
}
fn events_and_body(json: &JsonValue) -> (Vec<&JsonValue>, &JsonValue) {
assert_eq!(json["tag"], Tag::ALWAYS_STATEMENT);
let (json_events, json_body) = {
@ -90,10 +128,12 @@ impl EventControl {
(json_events, json_body)
}
fn event_names(json_events: Vec<&JsonValue>) -> (Vec<String>, Vec<String>, Vec<String>) {
let mut json_posedge_names = Vec::new();
let mut json_negedge_names = Vec::new();
let mut json_level_names = Vec::new();
fn event_names(
json_events: Vec<&JsonValue>,
) -> (IndexSet<String>, IndexSet<String>, IndexSet<String>) {
let mut posedge_names = IndexSet::new();
let mut negedge_names = IndexSet::new();
let mut level_names = IndexSet::new();
let json_id = |x: &JsonValue| -> String {
let json_symbol_identifiers = Tools::collect_tag(&x, Tag::SYMBOL_IDENTIFIER);
@ -104,20 +144,22 @@ impl EventControl {
for json_event in json_events {
match json_event["children"][0]["tag"].as_str() {
Some(Tag::POS_EDGE) => {
json_posedge_names.push(json_id(json_event));
posedge_names.insert(json_id(json_event));
}
Some(Tag::NEG_EDGE) => {
json_negedge_names.push(json_id(json_event));
negedge_names.insert(json_id(json_event));
}
_ => {
json_level_names.push(json_id(json_event));
level_names.insert(json_id(json_event));
}
}
}
(json_posedge_names, json_negedge_names, json_level_names)
(posedge_names, negedge_names, level_names)
}
fn value_names(json_body: &JsonValue) -> (HashSet<String>, HashSet<String>, HashSet<String>) {
fn value_names(
json_body: &JsonValue,
) -> (IndexSet<String>, IndexSet<String>, IndexSet<String>) {
let json_assignments = [
Tools::collect_tag(json_body, Tag::NONBLOCKING_ASSIGNMENT_STATEMENT),
Tools::collect_tag(json_body, Tag::NET_VARIABLE_ASSIGNMENT),
@ -136,21 +178,21 @@ impl EventControl {
]
.concat();
let json_lvalues = HashSet::from_iter(
let json_lvalues = IndexSet::from_iter(
json_assignments
.iter()
.map(|x| Tools::collect_tag(&x["children"][0], Tag::SYMBOL_IDENTIFIER))
.flatten()
.map(|x| x["text"].to_string()),
);
let json_rvalues = HashSet::from_iter(
let json_rvalues = IndexSet::from_iter(
json_assignments
.iter()
.map(|x| Tools::collect_tag(&x["children"][2], Tag::SYMBOL_IDENTIFIER))
.flatten()
.map(|x| x["text"].to_string()),
);
let json_ctrl_values = HashSet::from_iter(
let json_ctrl_values = IndexSet::from_iter(
json_conditions
.iter()
.map(|x| Tools::collect_tag(x, Tag::SYMBOL_IDENTIFIER))
@ -161,7 +203,7 @@ impl EventControl {
(json_lvalues, json_rvalues, json_ctrl_values)
}
fn top_ctrl_values(json_body: &JsonValue) -> HashSet<String> {
fn top_ctrl_values(json_body: &JsonValue) -> IndexSet<String> {
let json_statement = {
let mut ret = json_body;
loop {
@ -182,7 +224,7 @@ impl EventControl {
}
}
};
HashSet::from_iter(
IndexSet::from_iter(
Tools::match_tags(
vec![json_statement],
vec![

View File

@ -1,5 +1,7 @@
pub mod code_reporter;
pub mod event_control;
mod tools;
pub mod tools;
pub use event_control::EventControl;
pub use tools::{CodeReporter, Tools};
pub use code_reporter::CodeReporter;
pub use event_control::{EventControl, ProcessNames};
pub use tools::Tools;

View File

@ -4,8 +4,6 @@ use llhd::ir::{Block, Inst, Opcode};
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
fs::File,
io::{BufRead, BufReader},
iter::FromIterator,
process::{Command, Stdio},
rc::Rc,
@ -279,63 +277,3 @@ impl Tools {
modules
}
}
pub struct CodeReporter {
file_name: String,
line_breakers: Vec<usize>,
}
impl CodeReporter {
pub fn new(file_name: &str) -> Self {
Self {
file_name: file_name.to_string(),
line_breakers: Self::read_line_breakers(file_name),
}
}
pub fn line_no(&self, key: usize) -> usize {
self.line_no_rec(key, 0, self.line_breakers.len() - 1)
}
pub fn line(&self, line_no: usize) -> String {
let reader = BufReader::new(
File::open(self.file_name.as_str())
.expect(format!(r#"file "{}" not found"#, self.file_name.as_str()).as_str()),
);
reader.lines().nth(line_no).unwrap().unwrap()
}
pub fn line_count(&self) -> usize {
self.line_breakers.len()
}
fn read_line_breakers(file_name: &str) -> Vec<usize> {
let reader = BufReader::new(
File::open(file_name).expect(format!(r#"file "{}" not found"#, file_name).as_str()),
);
let mut line_breakers = Vec::new();
line_breakers.push(0);
for line in reader.lines() {
let line_no = 1 + line_breakers.last().unwrap() + line.unwrap().len();
line_breakers.push(line_no);
}
line_breakers
}
fn line_no_rec(&self, key: usize, left: usize, right: usize) -> usize {
if left + 1 >= right {
left
} else {
let mid = (left + right) / 2;
if key < self.line_breakers[mid] {
self.line_no_rec(key, left, mid)
} else {
self.line_no_rec(key, mid, right)
}
}
}
}