mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Less interior mutability in system world
This commit is contained in:
parent
3b104e2ba8
commit
2dc0478ffe
@ -1,7 +1,7 @@
|
|||||||
use std::cell::{Cell, OnceCell, RefCell, RefMut};
|
use std::cell::{OnceCell, RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{fs, mem};
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Local};
|
use chrono::{DateTime, Datelike, Local};
|
||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
@ -105,7 +105,7 @@ impl SystemWorld {
|
|||||||
.get_mut()
|
.get_mut()
|
||||||
.values()
|
.values()
|
||||||
.filter(|slot| slot.accessed())
|
.filter(|slot| slot.accessed())
|
||||||
.filter_map(|slot| slot.system_path(&self.root).ok())
|
.filter_map(|slot| system_path(&self.root, slot.id).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the compilation state in preparation of a new compilation.
|
/// Reset the compilation state in preparation of a new compilation.
|
||||||
@ -209,15 +209,15 @@ impl FileSlot {
|
|||||||
|
|
||||||
/// Marks the file as not yet accessed in preparation of the next
|
/// Marks the file as not yet accessed in preparation of the next
|
||||||
/// compilation.
|
/// compilation.
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.source.reset();
|
self.source.reset();
|
||||||
self.file.reset();
|
self.file.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the source for this file.
|
/// Retrieve the source for this file.
|
||||||
fn source(&self, root: &Path) -> FileResult<Source> {
|
fn source(&mut self, project_root: &Path) -> FileResult<Source> {
|
||||||
self.source.get_or_init(
|
self.source.get_or_init(
|
||||||
|| self.system_path(root),
|
|| system_path(project_root, self.id),
|
||||||
|data, prev| {
|
|data, prev| {
|
||||||
let text = decode_utf8(&data)?;
|
let text = decode_utf8(&data)?;
|
||||||
if let Some(mut prev) = prev {
|
if let Some(mut prev) = prev {
|
||||||
@ -231,70 +231,48 @@ impl FileSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the file's bytes.
|
/// Retrieve the file's bytes.
|
||||||
fn file(&self, root: &Path) -> FileResult<Bytes> {
|
fn file(&mut self, project_root: &Path) -> FileResult<Bytes> {
|
||||||
self.file
|
self.file
|
||||||
.get_or_init(|| self.system_path(root), |data, _| Ok(data.into()))
|
.get_or_init(|| system_path(project_root, self.id), |data, _| Ok(data.into()))
|
||||||
}
|
|
||||||
|
|
||||||
/// The path of the slot on the system.
|
|
||||||
fn system_path(&self, root: &Path) -> FileResult<PathBuf> {
|
|
||||||
// Determine the root path relative to which the file path
|
|
||||||
// will be resolved.
|
|
||||||
let buf;
|
|
||||||
let mut root = root;
|
|
||||||
if let Some(spec) = self.id.package() {
|
|
||||||
buf = prepare_package(spec)?;
|
|
||||||
root = &buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join the path to the root. If it tries to escape, deny
|
|
||||||
// access. Note: It can still escape via symlinks.
|
|
||||||
self.id.vpath().resolve(root).ok_or(FileError::AccessDenied)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lazily processes data for a file.
|
/// Lazily processes data for a file.
|
||||||
struct SlotCell<T> {
|
struct SlotCell<T> {
|
||||||
/// The processed data.
|
/// The processed data.
|
||||||
data: RefCell<Option<FileResult<T>>>,
|
data: Option<FileResult<T>>,
|
||||||
/// A hash of the raw file contents / access error.
|
/// A hash of the raw file contents / access error.
|
||||||
fingerprint: Cell<u128>,
|
fingerprint: u128,
|
||||||
/// Whether the slot has been accessed in the current compilation.
|
/// Whether the slot has been accessed in the current compilation.
|
||||||
accessed: Cell<bool>,
|
accessed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> SlotCell<T> {
|
impl<T: Clone> SlotCell<T> {
|
||||||
/// Creates a new, empty cell.
|
/// Creates a new, empty cell.
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self { data: None, fingerprint: 0, accessed: false }
|
||||||
data: RefCell::new(None),
|
|
||||||
fingerprint: Cell::new(0),
|
|
||||||
accessed: Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the cell was accessed in the ongoing compilation.
|
/// Whether the cell was accessed in the ongoing compilation.
|
||||||
fn accessed(&self) -> bool {
|
fn accessed(&self) -> bool {
|
||||||
self.accessed.get()
|
self.accessed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the cell as not yet accessed in preparation of the next
|
/// Marks the cell as not yet accessed in preparation of the next
|
||||||
/// compilation.
|
/// compilation.
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.accessed.set(false);
|
self.accessed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the contents of the cell or initialize them.
|
/// Gets the contents of the cell or initialize them.
|
||||||
fn get_or_init(
|
fn get_or_init(
|
||||||
&self,
|
&mut self,
|
||||||
path: impl FnOnce() -> FileResult<PathBuf>,
|
path: impl FnOnce() -> FileResult<PathBuf>,
|
||||||
f: impl FnOnce(Vec<u8>, Option<T>) -> FileResult<T>,
|
f: impl FnOnce(Vec<u8>, Option<T>) -> FileResult<T>,
|
||||||
) -> FileResult<T> {
|
) -> FileResult<T> {
|
||||||
let mut borrow = self.data.borrow_mut();
|
|
||||||
|
|
||||||
// If we accessed the file already in this compilation, retrieve it.
|
// If we accessed the file already in this compilation, retrieve it.
|
||||||
if self.accessed.replace(true) {
|
if mem::replace(&mut self.accessed, true) {
|
||||||
if let Some(data) = &*borrow {
|
if let Some(data) = &self.data {
|
||||||
return data.clone();
|
return data.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,20 +282,37 @@ impl<T: Clone> SlotCell<T> {
|
|||||||
let fingerprint = typst::util::hash128(&result);
|
let fingerprint = typst::util::hash128(&result);
|
||||||
|
|
||||||
// If the file contents didn't change, yield the old processed data.
|
// If the file contents didn't change, yield the old processed data.
|
||||||
if self.fingerprint.replace(fingerprint) == fingerprint {
|
if mem::replace(&mut self.fingerprint, fingerprint) == fingerprint {
|
||||||
if let Some(data) = &*borrow {
|
if let Some(data) = &self.data {
|
||||||
return data.clone();
|
return data.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev = borrow.take().and_then(Result::ok);
|
let prev = self.data.take().and_then(Result::ok);
|
||||||
let value = result.and_then(|data| f(data, prev));
|
let value = result.and_then(|data| f(data, prev));
|
||||||
*borrow = Some(value.clone());
|
self.data = Some(value.clone());
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the path of a file id on the system, downloading a package if
|
||||||
|
/// necessary.
|
||||||
|
fn system_path(project_root: &Path, id: FileId) -> FileResult<PathBuf> {
|
||||||
|
// Determine the root path relative to which the file path
|
||||||
|
// will be resolved.
|
||||||
|
let buf;
|
||||||
|
let mut root = project_root;
|
||||||
|
if let Some(spec) = id.package() {
|
||||||
|
buf = prepare_package(spec)?;
|
||||||
|
root = &buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join the path to the root. If it tries to escape, deny
|
||||||
|
// access. Note: It can still escape via symlinks.
|
||||||
|
id.vpath().resolve(root).ok_or(FileError::AccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a file.
|
/// Read a file.
|
||||||
fn read(path: &Path) -> FileResult<Vec<u8>> {
|
fn read(path: &Path) -> FileResult<Vec<u8>> {
|
||||||
let f = |e| FileError::from_io(e, path);
|
let f = |e| FileError::from_io(e, path);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user