use crate::boolean;
use crate::fail;
use crate::ffi::jpegli_common_struct;
use crate::ffi::jpegli_compress_struct;
use crate::ffi::jpegli_destination_mgr;
use crate::ffi::JERR_BUFFER_SIZE;
use crate::ffi::JERR_FILE_WRITE;
use crate::ffi::JERR_INPUT_EOF;
use crate::ffi::JWRN_JPEG_EOF;
use crate::ffi::J_MESSAGE_CODE;
use crate::ffi::{JPOOL_IMAGE, JPOOL_PERMANENT};
use crate::warn;
use std::io;
use std::io::Write;
use std::mem::MaybeUninit;
use std::os::raw::c_int;
use std::os::raw::c_long;
use std::os::raw::c_uint;
use std::ptr;
use std::ptr::NonNull;
#[repr(C)]
pub(crate) struct DestinationMgr<W> {
pub(crate) iface: jpegli_destination_mgr,
buf: Vec<u8>,
writer: W,
}
impl<W: Write> DestinationMgr<W> {
#[inline]
pub fn new(writer: W, capacity: usize) -> Self {
Self {
iface: jpegli_destination_mgr {
next_output_byte: ptr::null_mut(),
free_in_buffer: 0,
init_destination: Some(Self::init_destination),
empty_output_buffer: Some(Self::empty_output_buffer),
term_destination: Some(Self::term_destination),
},
buf: Vec::with_capacity(if capacity > 0 {
capacity.min(i32::MAX as usize)
} else {
4096
}),
writer,
}
}
pub fn into_inner(self) -> W {
self.writer
}
fn reset_buffer(&mut self) {
self.buf.clear();
let spare_capacity = self.buf.spare_capacity_mut();
self.iface.next_output_byte = spare_capacity.as_mut_ptr().cast();
self.iface.free_in_buffer = spare_capacity.len();
}
unsafe fn write_buffer(&mut self, full: bool) -> Result<(), J_MESSAGE_CODE> {
let buf = self.buf.spare_capacity_mut();
let used_capacity = if full {
buf.len()
} else {
buf.len()
.checked_sub(self.iface.free_in_buffer)
.ok_or(JERR_BUFFER_SIZE)?
};
if used_capacity > 0 {
unsafe {
self.buf.set_len(used_capacity);
}
self.writer
.write_all(&self.buf)
.map_err(|_| JERR_FILE_WRITE)?;
}
self.reset_buffer();
Ok(())
}
unsafe fn cast(cinfo: &mut jpegli_compress_struct) -> &mut Self {
let this: &mut Self = &mut *cinfo.dest.cast();
type FnPtr<'a> = unsafe extern "C-unwind" fn(cinfo: &'a mut jpegli_compress_struct);
if Some::<FnPtr>(Self::init_destination) != this.iface.init_destination {
fail(&mut cinfo.common, JERR_BUFFER_SIZE);
}
this
}
unsafe extern "C-unwind" fn empty_output_buffer(cinfo: &mut jpegli_compress_struct) -> boolean {
let this = Self::cast(cinfo);
if let Err(code) = this.write_buffer(true) {
fail(&mut cinfo.common, code);
}
1
}
unsafe extern "C-unwind" fn init_destination(cinfo: &mut jpegli_compress_struct) {
let this = Self::cast(cinfo);
this.reset_buffer();
}
unsafe extern "C-unwind" fn term_destination(cinfo: &mut jpegli_compress_struct) {
let this = Self::cast(cinfo);
if let Err(code) = this.write_buffer(false) {
fail(&mut cinfo.common, code);
}
if let Err(_) = this.writer.flush() {
fail(&mut cinfo.common, JERR_FILE_WRITE);
}
this.iface.free_in_buffer = 0;
}
}
#[test]
fn w() {
for any_write_first in [true, false] {
for capacity in [0, 1, 2, 3, 5, 10, 255, 256, 4096] {
let mut w = DestinationMgr::new(Vec::new(), capacity);
let mut expected = Vec::new();
unsafe {
let mut j: jpegli_compress_struct = std::mem::zeroed();
j.dest = &mut w.iface;
(w.iface.init_destination.unwrap())(&mut j);
assert!(w.iface.free_in_buffer > 0);
if any_write_first {
while w.iface.free_in_buffer > 0 {
expected.push(123);
*w.iface.next_output_byte = 123;
w.iface.next_output_byte = w.iface.next_output_byte.add(1);
w.iface.free_in_buffer -= 1;
}
(w.iface.empty_output_buffer.unwrap())(&mut j);
assert!(w.iface.free_in_buffer > 0);
let slice = std::slice::from_raw_parts_mut(
w.iface.next_output_byte,
w.iface.free_in_buffer,
);
slice.iter_mut().enumerate().for_each(|(i, s)| *s = i as u8);
expected.extend_from_slice(slice);
w.iface.next_output_byte = w.iface.next_output_byte.add(1); w.iface.free_in_buffer = 999; (w.iface.empty_output_buffer.unwrap())(&mut j);
assert!(w.iface.free_in_buffer > 0);
}
let slice = std::slice::from_raw_parts_mut(
w.iface.next_output_byte,
w.iface.free_in_buffer - 1,
);
slice
.iter_mut()
.enumerate()
.for_each(|(i, s)| *s = (i * 17) as u8);
expected.extend_from_slice(slice);
w.iface.next_output_byte = w.iface.next_output_byte.add(slice.len());
w.iface.free_in_buffer -= slice.len(); (w.iface.term_destination.unwrap())(&mut j);
assert_eq!(expected, w.into_inner());
}
}
}
}