1use std::io::{self, Read, Seek, SeekFrom, Write};
2use std::ops::Range;
3
4#[cfg(not(feature = "shared"))]
5type File = std::fs::File;
6#[cfg(feature = "shared")]
7type File = shared_file::SharedFile;
8
9type Frame = Range<u64>;
10
11pub struct FramedFile {
12 file: File,
13 frame: Frame,
14 current_pos: u64,
15}
16
17impl FramedFile {
18 pub fn new(file: File, frame: Frame) -> io::Result<Self> {
19 Self::from_range(file, frame)
20 }
21
22 pub fn from_range(mut file: File, frame: Frame) -> io::Result<Self> {
23 file.seek(SeekFrom::Start(frame.start))?;
24
25 Ok(Self {
26 file,
27 frame,
28 current_pos: 0,
29 })
30 }
31
32 pub fn from_len(file: File, start: u64, len: u64) -> io::Result<Self> {
33 let end = start + len;
34
35 Self::from_range(file, start..end)
36 }
37
38 pub fn position(&self) -> u64 {
39 self.current_pos
40 }
41
42 pub fn frame(&self) -> &Frame {
43 &self.frame
44 }
45
46 pub fn frame_start(&self) -> u64 {
47 self.frame().start
48 }
49
50 pub fn frame_end(&self) -> u64 {
51 self.frame().end
52 }
53
54 pub fn frame_len(&self) -> u64 {
55 self.frame_end() - self.frame_start()
56 }
57
58 pub fn remaining_len(&self) -> u64 {
59 self.frame_len() - self.position()
60 }
61
62 pub fn into_raw_file(self) -> File {
63 self.file
64 }
65}
66
67impl Read for FramedFile {
68 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
69 let max_to_read = self.remaining_len() as usize;
70 let buf_len = buf.len();
71 let to_read = max_to_read.min(buf_len);
72
73 if to_read == 0 {
74 return Ok(0);
75 }
76
77 let bytes_read = self.file.read(&mut buf[..to_read])?;
78 self.current_pos += bytes_read as u64;
79 Ok(bytes_read)
80 }
81}
82
83impl Write for FramedFile {
84 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
85 let max_to_write = self.remaining_len() as usize;
86 let buf_len = buf.len();
87 let to_write = max_to_write.min(buf_len);
88
89 if to_write == 0 {
90 return Err(io::Error::new(
91 io::ErrorKind::WriteZero,
92 "Write exceeds file range",
93 ));
94 }
95
96 let bytes_written = self.file.write(&buf[..to_write])?;
97 self.current_pos += bytes_written as u64;
98 Ok(bytes_written)
99 }
100
101 fn flush(&mut self) -> io::Result<()> {
102 self.file.flush()
103 }
104}
105
106impl Seek for FramedFile {
107 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
108 let new_pos = match pos {
109 SeekFrom::Start(offset) => offset,
110 SeekFrom::End(offset) => (self.frame_len() as i64 + offset) as u64,
111 SeekFrom::Current(offset) => (self.current_pos as i64 + offset) as u64,
112 };
113
114 if new_pos > self.frame_len() {
115 return Err(io::Error::new(
116 io::ErrorKind::InvalidInput,
117 "Seek position out of bounds",
118 ));
119 }
120
121 self.file
122 .seek(SeekFrom::Start(self.frame_start() + new_pos))?;
123 self.current_pos = new_pos;
124 Ok(self.current_pos)
125 }
126}