use std::fs::File;
use std::io;
use rete_core::{auto_block, BlockCacheReader, RangeReader, Rete};
use crate::http::HttpRangeReader;
pub(crate) fn is_url(source: &str) -> bool {
source.starts_with("http://") || source.starts_with("https://")
}
const DEFAULT_LAZY_ABOVE_BYTES: u64 = 1 << 30;
fn lazy_threshold_bytes() -> u64 {
std::env::var("RETE_LOCAL_LAZY_ABOVE_MB")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.map(|mb| mb.saturating_mul(1 << 20))
.unwrap_or(DEFAULT_LAZY_ABOVE_BYTES)
}
pub(crate) fn open_local(path: &str) -> anyhow::Result<Rete> {
let len = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
if std::env::var("RETE_OPEN_DEBUG").is_ok() {
eprintln!(
"[open_local] {path}: len={len} threshold={} -> {}",
lazy_threshold_bytes(),
if len > lazy_threshold_bytes() {
"LAZY"
} else {
"EAGER"
}
);
}
if len <= lazy_threshold_bytes() {
return Ok(Rete::open(&std::fs::read(path)?)?);
}
open_local_lazy(path, len, None)
}
pub(crate) fn open_local_ranged(path: &str, budget: Option<u64>) -> anyhow::Result<Rete> {
let len = std::fs::metadata(path)?.len();
if std::env::var("RETE_OPEN_DEBUG").is_ok() {
eprintln!("[open_local_ranged] {path}: len={len} -> LAZY");
}
open_local_lazy(path, len, budget)
}
pub(crate) fn open_local_eager(path: &str) -> anyhow::Result<Rete> {
if std::env::var("RETE_OPEN_DEBUG").is_ok() {
let len = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
eprintln!("[open_local_eager] {path}: len={len} -> EAGER (whole file)");
}
Ok(Rete::open(&std::fs::read(path)?)?)
}
fn open_local_lazy(path: &str, len: u64, budget: Option<u64>) -> anyhow::Result<Rete> {
let reader = std::sync::Arc::new(LocalRangeReader::open(path)?);
let block: u64 = match std::env::var("RETE_BLOCK_KB")
.ok()
.and_then(|v| v.parse::<u64>().ok())
{
Some(kb) => kb * 1024,
None => auto_block(len),
};
let block_cap = rete_core::split_memory_budget(budget.unwrap_or(u64::MAX)).block_cache;
let rete = if block == 0 {
Rete::open_ranged_lazy(reader)?
} else {
let mut bc = BlockCacheReader::new(reader, block);
if block_cap != u64::MAX {
bc = bc.with_cache_cap(block_cap);
}
Rete::open_ranged_lazy(std::sync::Arc::new(bc))?
};
rete.set_memory_budget(budget);
Ok(rete)
}
pub(crate) enum RangedSourceReader {
Local(LocalRangeReader),
Http(HttpRangeReader),
}
impl RangedSourceReader {
pub(crate) fn open(source: &str) -> anyhow::Result<Self> {
if is_url(source) {
Ok(Self::Http(HttpRangeReader::open(source)?))
} else {
Ok(Self::Local(LocalRangeReader::open(source)?))
}
}
}
impl RangeReader for RangedSourceReader {
fn len(&self) -> u64 {
match self {
Self::Local(r) => r.len(),
Self::Http(r) => r.len(),
}
}
fn read_at(&self, offset: u64, len: u64) -> io::Result<Vec<u8>> {
match self {
Self::Local(r) => r.read_at(offset, len),
Self::Http(r) => r.read_at(offset, len),
}
}
}
pub(crate) struct LocalRangeReader {
file: File,
len: u64,
}
impl LocalRangeReader {
pub(crate) fn open(path: &str) -> anyhow::Result<Self> {
let file = File::open(path)?;
let len = file.metadata()?.len();
Ok(Self { file, len })
}
}
impl RangeReader for LocalRangeReader {
fn len(&self) -> u64 {
self.len
}
fn read_at(&self, offset: u64, len: u64) -> io::Result<Vec<u8>> {
let end = offset
.checked_add(len)
.filter(|end| *end <= self.len)
.ok_or_else(|| io::Error::new(io::ErrorKind::UnexpectedEof, "range out of bounds"))?;
let size = usize::try_from(len).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "range too large for memory")
})?;
let mut buf = vec![0u8; size];
read_exact_at(&self.file, &mut buf, offset)?;
debug_assert_eq!(end - offset, len);
Ok(buf)
}
}
#[cfg(unix)]
fn read_some_at(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.read_at(buf, offset)
}
#[cfg(windows)]
fn read_some_at(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_read(buf, offset)
}
fn read_exact_at(file: &File, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
let n = read_some_at(file, buf, offset)?;
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"range out of bounds",
));
}
offset += n as u64;
let tmp = buf;
buf = &mut tmp[n..];
}
Ok(())
}