Improve performance of TeX file resolver

This commit is contained in:
Eric Förster 2019-06-11 15:04:33 +02:00
parent d99672c5bc
commit f67e134088
3 changed files with 44 additions and 47 deletions

View file

@ -13,7 +13,7 @@ const FNDB_TABLE_POINTER_OFFSET: usize = 4 * FNDB_WORD_SIZE;
const FNDB_TABLE_SIZE_OFFSET: usize = 6 * FNDB_WORD_SIZE;
const FNDB_ENTRY_SIZE: usize = 4 * FNDB_WORD_SIZE;
pub fn read_database(directory: &Path, root_directories: &[PathBuf]) -> Result<Vec<PathBuf>> {
pub fn read_database(directory: &Path) -> Result<Vec<PathBuf>> {
let database_directory = directory.join(DATABASE_PATH);
if !database_directory.exists() {
return Ok(Vec::new());
@ -27,15 +27,13 @@ pub fn read_database(directory: &Path, root_directories: &[PathBuf]) -> Result<V
for file in files {
let bytes = fs::read(file.path()).expect("Could not read fndb file");
database.extend(
parse_database(root_directories, &bytes).map_err(|_| Error::CorruptFileDatabase)?,
);
database.extend(parse_database(&bytes).map_err(|_| Error::CorruptFileDatabase)?);
}
Ok(database)
}
fn parse_database(root_directories: &[PathBuf], bytes: &[u8]) -> io::Result<Vec<PathBuf>> {
fn parse_database(bytes: &[u8]) -> io::Result<Vec<PathBuf>> {
let mut reader = Cursor::new(bytes);
if reader.read_u32::<LittleEndian>()? != FNDB_SIGNATURE {
return Err(io::Error::from(io::ErrorKind::InvalidData));
@ -56,12 +54,8 @@ fn parse_database(root_directories: &[PathBuf], bytes: &[u8]) -> io::Result<Vec<
let file_name = read_string(bytes, file_name_offset)?;
let directory = read_string(bytes, directory_offset)?;
for root_directory in root_directories {
let file = root_directory.join(directory).join(file_name);
if file.is_file() {
files.push(file.canonicalize().unwrap());
}
}
let file = PathBuf::from(directory).join(file_name);
files.push(file);
}
Ok(files)

View file

@ -1,7 +1,8 @@
use crate::syntax::Language;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
@ -35,9 +36,34 @@ pub struct TexResolver {
impl TexResolver {
fn load() -> Result<Self> {
let directories = Self::find_root_directories()?;
let kind = Self::detect_distribution(&directories)?;
let files_by_name = Self::read_database(&directories, kind)?;
let root_directories = Self::find_root_directories()?;
let kind = Self::detect_distribution(&root_directories)?;
let reader = match kind {
TexDistributionKind::Texlive => texlive::read_database,
TexDistributionKind::Miktex => miktex::read_database,
};
let mut files_by_name = HashMap::new();
for directory in &root_directories {
let database = reader(directory)?
.into_iter()
.filter(|path| {
path.extension()
.and_then(OsStr::to_str)
.and_then(Language::by_extension)
.is_some()
})
.filter_map(|path| {
root_directories
.iter()
.rev()
.find_map(move |dir| dir.join(&path).canonicalize().ok())
})
.map(|path| (path.file_name().unwrap().to_owned(), path));
files_by_name.extend(database);
}
Ok(Self { files_by_name })
}
@ -58,7 +84,7 @@ impl TexResolver {
Ok(String::from_utf8(output.stdout)
.expect("Could not decode output from kpsewhich")
.lines()
.nth(0)
.next()
.expect("Invalid output from kpsewhich")
.to_owned())
}
@ -74,24 +100,4 @@ impl TexResolver {
Err(Error::UnsupportedTexDistribution)
}
fn read_database(
root_directories: &[PathBuf],
kind: TexDistributionKind,
) -> Result<HashMap<OsString, PathBuf>> {
let mut files_by_name = HashMap::new();
for directory in root_directories {
let database = match kind {
TexDistributionKind::Texlive => texlive::read_database(&directory),
TexDistributionKind::Miktex => miktex::read_database(&directory, root_directories),
}?;
for file in database {
let name = file.file_name().unwrap().to_owned();
files_by_name.insert(name, file);
}
}
Ok(files_by_name)
}
}

View file

@ -13,24 +13,21 @@ pub fn read_database(directory: &Path) -> Result<Vec<PathBuf>> {
}
let text = fs::read_to_string(file).expect("Could not read ls-R file");
parse_database(&directory, text.lines()).map_err(|_| Error::CorruptFileDatabase)
parse_database(text.lines()).map_err(|_| Error::CorruptFileDatabase)
}
fn parse_database(root_directory: &Path, lines: Lines) -> io::Result<Vec<PathBuf>> {
let mut files = Vec::new();
let mut directory = PathBuf::new();
fn parse_database(lines: Lines) -> io::Result<Vec<PathBuf>> {
let mut paths = Vec::new();
let mut directory = "";
for line in lines.filter(|x| !x.trim().is_empty() && !x.starts_with('%')) {
if line.ends_with(':') {
let path = &line[..line.len() - 1];
directory = root_directory.join(path);
directory = &line[..line.len() - 1];
} else {
let file = directory.join(line).canonicalize()?;
if file.is_file() {
files.push(file);
}
let path = PathBuf::from(directory).join(line);
paths.push(path);
}
}
Ok(files)
Ok(paths)
}