use crate::ffi::boolean;
use crate::ffi::jpegli_decompress_struct;
use crate::ffi::JERR_BAD_LENGTH;
use crate::ffi::{jpegli_common_struct, jpegli_resync_to_restart, jpegli_source_mgr};
use crate::ffi::{JERR_FILE_READ, JERR_VIRTUAL_BUG};
use crate::ffi::{JPOOL_IMAGE, JPOOL_PERMANENT, JWRN_JPEG_EOF};
use crate::{fail, warn};
use std::io::{self, BufRead, BufReader, Read};
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_long, c_uint};
use std::ptr;
use std::ptr::NonNull;
#[repr(C)]
pub(crate) struct SourceMgr<R> {
pub(crate) iface: jpegli_source_mgr,
to_consume: usize,
reader: R,
}
impl<R: BufRead> SourceMgr<R> {
#[inline]
pub(crate) fn new(reader: R) -> io::Result<Self> {
let mut this = Self {
iface: jpegli_source_mgr {
next_input_byte: ptr::null_mut(),
bytes_in_buffer: 0,
init_source: Some(Self::init_source),
fill_input_buffer: Some(Self::fill_input_buffer),
skip_input_data: Some(Self::skip_input_data),
resync_to_restart: Some(jpegli_resync_to_restart),
term_source: Some(Self::term_source),
},
to_consume: 0,
reader,
};
this.fill_input_buffer_impl()?;
Ok(this)
}
#[inline]
unsafe fn cast(cinfo: &mut jpegli_decompress_struct) -> &mut Self {
let this: &mut Self = &mut *cinfo.src.cast();
type FnPtr<'a> = unsafe extern "C-unwind" fn(cinfo: &'a mut jpegli_decompress_struct);
if Some::<FnPtr>(Self::init_source) != this.iface.init_source {
fail(&mut cinfo.common, JERR_VIRTUAL_BUG);
}
this
}
unsafe extern "C-unwind" fn init_source(cinfo: &mut jpegli_decompress_struct) {
let _s = Self::cast(cinfo);
debug_assert!(!_s.iface.next_input_byte.is_null());
debug_assert!(_s.iface.bytes_in_buffer > 0);
}
#[cold]
fn set_buffer_to_eoi(&mut self) {
debug_assert_eq!(self.to_consume, 0);
self.iface.next_input_byte = [0xFF, 0xD9, 0xFF, 0xD9].as_ptr();
self.iface.bytes_in_buffer = 4;
}
#[inline(never)]
fn fill_input_buffer_impl(&mut self) -> io::Result<()> {
self.reader.consume(self.to_consume);
self.to_consume = 0;
let buf = self.reader.fill_buf()?;
self.to_consume = buf.len();
self.iface.next_input_byte = buf.as_ptr();
self.iface.bytes_in_buffer = buf.len();
if buf.is_empty() {
return Err(io::ErrorKind::UnexpectedEof.into());
}
Ok(())
}
unsafe extern "C-unwind" fn fill_input_buffer(cinfo: &mut jpegli_decompress_struct) -> boolean {
let this = Self::cast(cinfo);
match this.fill_input_buffer_impl() {
Ok(()) => 1,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
this.set_buffer_to_eoi();
warn(&mut cinfo.common, JWRN_JPEG_EOF);
1
}
Err(_) => {
fail(&mut cinfo.common, JERR_FILE_READ);
}
}
}
unsafe extern "C-unwind" fn skip_input_data(
cinfo: &mut jpegli_decompress_struct,
num_bytes: c_long,
) {
if num_bytes <= 0 {
return;
}
let this = Self::cast(cinfo);
let mut num_bytes = num_bytes as usize;
loop {
if this.iface.bytes_in_buffer > 0 {
let skip_from_buffer = this.iface.bytes_in_buffer.min(num_bytes);
this.iface.bytes_in_buffer -= skip_from_buffer;
this.iface.next_input_byte = this.iface.next_input_byte.add(skip_from_buffer);
num_bytes -= skip_from_buffer;
}
if num_bytes == 0 {
break;
}
if let Err(_) = this.fill_input_buffer_impl() {
fail(&mut cinfo.common, JERR_FILE_READ);
}
}
}
fn return_unconsumed_data(&mut self) {
let unconsumed = self.to_consume.saturating_sub(self.iface.bytes_in_buffer);
self.to_consume = 0;
self.reader.consume(unconsumed);
}
unsafe extern "C-unwind" fn term_source(cinfo: &mut jpegli_decompress_struct) {
let this = Self::cast(cinfo);
this.return_unconsumed_data();
}
pub fn into_inner(mut self) -> R {
self.return_unconsumed_data();
self.reader
}
}