use std::fs::File;
use std::io::{self, Read, Write};
use crate::error::{OjphError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SeekFrom {
Start,
Current,
End,
}
impl SeekFrom {
fn to_std(self, offset: i64) -> io::SeekFrom {
match self {
Self::Start => io::SeekFrom::Start(offset as u64),
Self::Current => io::SeekFrom::Current(offset),
Self::End => io::SeekFrom::End(offset),
}
}
}
pub trait OutfileBase {
fn write(&mut self, data: &[u8]) -> Result<usize>;
fn tell(&self) -> i64 {
0
}
fn seek(&mut self, _offset: i64, _whence: SeekFrom) -> Result<()> {
Err(OjphError::Unsupported("seek not supported".into()))
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
pub trait InfileBase {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()>;
fn tell(&self) -> i64;
fn eof(&self) -> bool;
}
pub struct J2cOutfile {
file: File,
pos: i64,
}
impl J2cOutfile {
pub fn open(path: &str) -> Result<Self> {
let file = File::create(path)?;
Ok(Self { file, pos: 0 })
}
}
impl OutfileBase for J2cOutfile {
fn write(&mut self, data: &[u8]) -> Result<usize> {
let n = self.file.write(data)?;
self.pos += n as i64;
Ok(n)
}
fn tell(&self) -> i64 {
self.pos
}
fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
use std::io::Seek;
let new_pos = self.file.seek(whence.to_std(offset))?;
self.pos = new_pos as i64;
Ok(())
}
fn flush(&mut self) -> Result<()> {
self.file.flush()?;
Ok(())
}
}
pub struct J2cInfile {
file: File,
pos: i64,
at_eof: bool,
}
impl J2cInfile {
pub fn open(path: &str) -> Result<Self> {
let file = File::open(path)?;
Ok(Self {
file,
pos: 0,
at_eof: false,
})
}
}
impl InfileBase for J2cInfile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let n = self.file.read(buf)?;
self.pos += n as i64;
if n == 0 && !buf.is_empty() {
self.at_eof = true;
}
Ok(n)
}
fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
use std::io::Seek;
let new_pos = self.file.seek(whence.to_std(offset))?;
self.pos = new_pos as i64;
self.at_eof = false;
Ok(())
}
fn tell(&self) -> i64 {
self.pos
}
fn eof(&self) -> bool {
self.at_eof
}
}
pub struct MemOutfile {
buf: Vec<u8>,
pos: usize,
}
impl MemOutfile {
pub fn new() -> Self {
Self {
buf: Vec::new(),
pos: 0,
}
}
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: Vec::with_capacity(cap),
pos: 0,
}
}
pub fn get_data(&self) -> &[u8] {
&self.buf
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
}
impl Default for MemOutfile {
fn default() -> Self {
Self::new()
}
}
impl OutfileBase for MemOutfile {
fn write(&mut self, data: &[u8]) -> Result<usize> {
if self.pos == self.buf.len() {
self.buf.extend_from_slice(data);
} else {
let end = self.pos + data.len();
if end > self.buf.len() {
self.buf.resize(end, 0);
}
self.buf[self.pos..end].copy_from_slice(data);
}
self.pos += data.len();
Ok(data.len())
}
fn tell(&self) -> i64 {
self.pos as i64
}
fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
let new_pos = match whence {
SeekFrom::Start => offset,
SeekFrom::Current => self.pos as i64 + offset,
SeekFrom::End => self.buf.len() as i64 + offset,
};
if new_pos < 0 {
return Err(OjphError::InvalidParam("seek before start".into()));
}
self.pos = new_pos as usize;
Ok(())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
pub struct MemInfile<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> MemInfile<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
}
impl InfileBase for MemInfile<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let remaining = self.data.len().saturating_sub(self.pos);
let n = buf.len().min(remaining);
buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
self.pos += n;
Ok(n)
}
fn seek(&mut self, offset: i64, whence: SeekFrom) -> Result<()> {
let new_pos = match whence {
SeekFrom::Start => offset,
SeekFrom::Current => self.pos as i64 + offset,
SeekFrom::End => self.data.len() as i64 + offset,
};
if new_pos < 0 || new_pos as usize > self.data.len() {
return Err(OjphError::InvalidParam("seek out of range".into()));
}
self.pos = new_pos as usize;
Ok(())
}
fn tell(&self) -> i64 {
self.pos as i64
}
fn eof(&self) -> bool {
self.pos >= self.data.len()
}
}