Files
furumi-desktop/crates/backend/src/streaming.rs
T
2026-08-12 01:30:57 +01:00

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",
))
}
}