1use std::fs::File;
7use std::io::{self, Read, Write};
8
9use crate::error::{OjphError, Result};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SeekFrom {
19 Start,
21 Current,
23 End,
25}
26
27impl SeekFrom {
28 fn to_std(self, offset: i64) -> io::SeekFrom {
30 match self {
31 Self::Start => io::SeekFrom::Start(offset as u64),
32 Self::Current => io::SeekFrom::Current(offset),
33 Self::End => io::SeekFrom::End(offset),
34 }
35 }
36}
37
38pub trait OutfileBase {
44 fn write(&mut self, data: &[u8]) -> Result<usize>;
46
47 fn tell(&self) -> i64 {
49 0
50 }
51
52 fn seek(&mut self, _offset: i64, _whence: SeekFrom) -> Result<()> {
54 Err(OjphError::Unsupported("seek not supported".into()))
55 }
56
57 fn flush(&mut self) -> Result<()> {
59 Ok(())
60 }
61}
62
63pub trait InfileBase {
69 fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
71
72 fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()>;
74
75 fn tell(&self) -> i64;
77
78 fn eof(&self) -> bool;
80}
81
82pub struct J2cOutfile {
88 file: File,
89 pos: i64,
90}
91
92impl J2cOutfile {
93 pub fn open(path: &str) -> Result<Self> {
95 let file = File::create(path)?;
96 Ok(Self { file, pos: 0 })
97 }
98}
99
100impl OutfileBase for J2cOutfile {
101 fn write(&mut self, data: &[u8]) -> Result<usize> {
102 let n = self.file.write(data)?;
103 self.pos += n as i64;
104 Ok(n)
105 }
106
107 fn tell(&self) -> i64 {
108 self.pos
109 }
110
111 fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
112 use std::io::Seek;
113 let new_pos = self.file.seek(whence.to_std(offset))?;
114 self.pos = new_pos as i64;
115 Ok(())
116 }
117
118 fn flush(&mut self) -> Result<()> {
119 self.file.flush()?;
120 Ok(())
121 }
122}
123
124pub struct J2cInfile {
130 file: File,
131 pos: i64,
132 at_eof: bool,
133}
134
135impl J2cInfile {
136 pub fn open(path: &str) -> Result<Self> {
138 let file = File::open(path)?;
139 Ok(Self {
140 file,
141 pos: 0,
142 at_eof: false,
143 })
144 }
145}
146
147impl InfileBase for J2cInfile {
148 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
149 let n = self.file.read(buf)?;
150 self.pos += n as i64;
151 if n == 0 && !buf.is_empty() {
152 self.at_eof = true;
153 }
154 Ok(n)
155 }
156
157 fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
158 use std::io::Seek;
159 let new_pos = self.file.seek(whence.to_std(offset))?;
160 self.pos = new_pos as i64;
161 self.at_eof = false;
162 Ok(())
163 }
164
165 fn tell(&self) -> i64 {
166 self.pos
167 }
168
169 fn eof(&self) -> bool {
170 self.at_eof
171 }
172}
173
174pub struct MemOutfile {
181 buf: Vec<u8>,
182 pos: usize,
183}
184
185impl MemOutfile {
186 pub fn new() -> Self {
188 Self {
189 buf: Vec::new(),
190 pos: 0,
191 }
192 }
193
194 pub fn with_capacity(cap: usize) -> Self {
196 Self {
197 buf: Vec::with_capacity(cap),
198 pos: 0,
199 }
200 }
201
202 pub fn get_data(&self) -> &[u8] {
204 &self.buf
205 }
206
207 pub fn len(&self) -> usize {
209 self.buf.len()
210 }
211
212 pub fn is_empty(&self) -> bool {
214 self.buf.is_empty()
215 }
216}
217
218impl Default for MemOutfile {
219 fn default() -> Self {
220 Self::new()
221 }
222}
223
224impl OutfileBase for MemOutfile {
225 fn write(&mut self, data: &[u8]) -> Result<usize> {
226 if self.pos == self.buf.len() {
227 self.buf.extend_from_slice(data);
228 } else {
229 let end = self.pos + data.len();
231 if end > self.buf.len() {
232 self.buf.resize(end, 0);
233 }
234 self.buf[self.pos..end].copy_from_slice(data);
235 }
236 self.pos += data.len();
237 Ok(data.len())
238 }
239
240 fn tell(&self) -> i64 {
241 self.pos as i64
242 }
243
244 fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
245 let new_pos = match whence {
246 SeekFrom::Start => offset,
247 SeekFrom::Current => self.pos as i64 + offset,
248 SeekFrom::End => self.buf.len() as i64 + offset,
249 };
250 if new_pos < 0 {
251 return Err(OjphError::InvalidParam("seek before start".into()));
252 }
253 self.pos = new_pos as usize;
254 Ok(())
255 }
256
257 fn flush(&mut self) -> Result<()> {
258 Ok(())
259 }
260}
261
262pub struct MemInfile<'a> {
268 data: &'a [u8],
269 pos: usize,
270}
271
272impl<'a> MemInfile<'a> {
273 pub fn new(data: &'a [u8]) -> Self {
275 Self { data, pos: 0 }
276 }
277}
278
279impl InfileBase for MemInfile<'_> {
280 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
281 let remaining = self.data.len().saturating_sub(self.pos);
282 let n = buf.len().min(remaining);
283 buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
284 self.pos += n;
285 Ok(n)
286 }
287
288 fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
289 let new_pos = match whence {
290 SeekFrom::Start => offset,
291 SeekFrom::Current => self.pos as i64 + offset,
292 SeekFrom::End => self.data.len() as i64 + offset,
293 };
294 if new_pos < 0 || new_pos as usize > self.data.len() {
295 return Err(OjphError::InvalidParam("seek out of range".into()));
296 }
297 self.pos = new_pos as usize;
298 Ok(())
299 }
300
301 fn tell(&self) -> i64 {
302 self.pos as i64
303 }
304
305 fn eof(&self) -> bool {
306 self.pos >= self.data.len()
307 }
308}