98 lines
2.6 KiB
Rust
98 lines
2.6 KiB
Rust
use std::fs::File;
|
|
use std::io::{self, Read, Seek, SeekFrom};
|
|
use std::path::Path;
|
|
use std::sync::{Arc, Condvar, Mutex};
|
|
|
|
#[derive(Default)]
|
|
struct State {
|
|
available: u64,
|
|
complete: bool,
|
|
}
|
|
#[derive(Default)]
|
|
struct Shared {
|
|
state: Mutex<State>,
|
|
changed: Condvar,
|
|
}
|
|
pub struct GrowingFileReader {
|
|
file: File,
|
|
pos: u64,
|
|
shared: Arc<Shared>,
|
|
}
|
|
pub struct GrowingFileWriter {
|
|
shared: Arc<Shared>,
|
|
}
|
|
|
|
pub fn growing_file(path: &Path) -> io::Result<(GrowingFileReader, GrowingFileWriter)> {
|
|
let shared = Arc::new(Shared::default());
|
|
Ok((
|
|
GrowingFileReader {
|
|
file: File::open(path)?,
|
|
pos: 0,
|
|
shared: Arc::clone(&shared),
|
|
},
|
|
GrowingFileWriter { shared },
|
|
))
|
|
}
|
|
impl GrowingFileWriter {
|
|
pub fn add_available(&self, bytes: u64) {
|
|
let mut state = self
|
|
.shared
|
|
.state
|
|
.lock()
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
|
state.available = state.available.saturating_add(bytes);
|
|
self.shared.changed.notify_all();
|
|
}
|
|
pub fn finish(&self) {
|
|
let mut state = self
|
|
.shared
|
|
.state
|
|
.lock()
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
|
state.complete = true;
|
|
self.shared.changed.notify_all();
|
|
}
|
|
}
|
|
impl Drop for GrowingFileWriter {
|
|
fn drop(&mut self) {
|
|
self.finish();
|
|
}
|
|
}
|
|
impl Read for GrowingFileReader {
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
loop {
|
|
let mut state = self
|
|
.shared
|
|
.state
|
|
.lock()
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
|
while self.pos >= state.available && !state.complete {
|
|
state = self
|
|
.shared
|
|
.changed
|
|
.wait(state)
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
|
}
|
|
if self.pos >= state.available {
|
|
return Ok(0);
|
|
}
|
|
let n = usize::try_from((state.available - self.pos).min(buf.len() as u64))
|
|
.unwrap_or(buf.len());
|
|
drop(state);
|
|
let read = self.file.read(&mut buf[..n])?;
|
|
self.pos += read as u64;
|
|
if read > 0 {
|
|
return Ok(read);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl Seek for GrowingFileReader {
|
|
fn seek(&mut self, _: SeekFrom) -> io::Result<u64> {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Unsupported,
|
|
"federated stream is not seekable while downloading",
|
|
))
|
|
}
|
|
}
|