use std::ffi::c_void;
use std::io::{Seek, Write};
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;
use crate::ffi;
use crate::ffi::mux::{WriterGetPosFn, WriterSetPosFn};
unsafe impl Send for OwnedWriterPtr {}
struct OwnedWriterPtr {
writer: ffi::mux::WriterNonNullPtr,
}
impl OwnedWriterPtr {
const unsafe fn new(writer: ffi::mux::WriterNonNullPtr) -> Self {
Self { writer }
}
const fn as_ptr(&self) -> ffi::mux::WriterMutPtr {
self.writer.as_ptr()
}
}
impl Drop for OwnedWriterPtr {
fn drop(&mut self) {
unsafe {
ffi::mux::delete_writer(self.writer.as_ptr());
}
}
}
pub struct Writer<T>
where
T: Write,
{
writer_data: Pin<Box<MuxWriterData<T>>>,
mkv_writer: OwnedWriterPtr,
}
struct MuxWriterData<T> {
dest: T,
bytes_written: u64,
_marker: PhantomPinned,
}
impl<T> Writer<T>
where
T: Write,
{
#[inline]
pub fn new_non_seek(dest: T) -> Self {
extern "C" fn get_pos_fn<T>(data: *mut c_void) -> u64 {
let data = unsafe { data.cast::<MuxWriterData<T>>().as_mut().unwrap() };
data.bytes_written
}
Self::make_writer(dest, get_pos_fn::<T>, None)
}
#[must_use]
#[inline]
pub fn into_inner(self) -> T {
let Self { writer_data, .. } = self;
unsafe { Pin::into_inner_unchecked(writer_data).dest }
}
pub(crate) const fn mkv_writer(&self) -> ffi::mux::WriterMutPtr {
self.mkv_writer.as_ptr()
}
fn make_writer(
dest: T,
get_pos_fn: WriterGetPosFn,
set_pos_fn: Option<WriterSetPosFn>,
) -> Self {
extern "C" fn write_fn<T>(data: *mut c_void, buf: *const c_void, len: usize) -> bool
where
T: Write,
{
if buf.is_null() {
return false;
}
let data = unsafe { data.cast::<MuxWriterData<T>>().as_mut().unwrap() };
let buf = unsafe { std::slice::from_raw_parts(buf.cast::<u8>(), len) };
let result = data.dest.write(buf);
if let Ok(num_bytes) = result {
let num_bytes_u64: u64 = num_bytes.try_into().unwrap();
data.bytes_written += num_bytes_u64;
num_bytes == len
} else {
false
}
}
let mut writer_data = Box::pin(MuxWriterData {
dest,
bytes_written: 0,
_marker: PhantomPinned,
});
let mkv_writer = unsafe {
ffi::mux::new_writer(
Some(write_fn::<T>),
Some(get_pos_fn),
set_pos_fn,
None,
std::ptr::from_mut(writer_data.as_mut().get_unchecked_mut()).cast(),
)
};
assert!(!mkv_writer.is_null());
Self {
writer_data,
mkv_writer: unsafe { OwnedWriterPtr::new(NonNull::new(mkv_writer).unwrap()) },
}
}
}
impl<T> Writer<T>
where
T: Write + Seek,
{
#[inline]
pub fn new(dest: T) -> Self {
use std::io::SeekFrom;
extern "C" fn get_pos_fn<T>(data: *mut c_void) -> u64
where
T: Write + Seek,
{
let data = unsafe { data.cast::<MuxWriterData<T>>().as_mut().unwrap() };
data.dest.stream_position().unwrap()
}
extern "C" fn set_pos_fn<T>(data: *mut c_void, pos: u64) -> bool
where
T: Write + Seek,
{
let data = unsafe { data.cast::<MuxWriterData<T>>().as_mut().unwrap() };
data.dest.seek(SeekFrom::Start(pos)).is_ok()
}
Self::make_writer(dest, get_pos_fn::<T>, Some(set_pos_fn::<T>))
}
}
#[test]
fn sendable() {
fn is_send<T: Send>(_: &T) {}
let w = Writer::new(std::io::Cursor::new(vec![1, 2, 3]));
is_send(&w);
assert_eq!([1, 2, 3], *w.into_inner().into_inner());
let w = Writer::new_non_seek(vec![3, 4, 5]);
is_send(&w);
assert_eq!([3, 4, 5], *w.into_inner());
}