numcodecs_jpeg2000/ffi/
stream.rs

1//! Adapted from the MIT/Apache 2.0 licensed <https://github.com/Neopallium/jpeg2k>
2
3use std::ffi::c_void;
4
5pub struct DecodeStream<'a> {
6    stream: *mut openjpeg_sys::opj_stream_t,
7    _buf: &'a [u8],
8}
9
10impl Drop for DecodeStream<'_> {
11    fn drop(&mut self) {
12        unsafe {
13            openjpeg_sys::opj_stream_destroy(self.stream);
14        }
15    }
16}
17
18impl<'a> DecodeStream<'a> {
19    pub fn new(buf: &'a [u8]) -> Self {
20        let len = buf.len();
21        let data = Box::into_raw(Box::new(WrappedSlice::new(buf)));
22
23        let stream = unsafe {
24            let stream = openjpeg_sys::opj_stream_default_create(1);
25            openjpeg_sys::opj_stream_set_read_function(stream, Some(buf_read_stream_read_fn));
26            openjpeg_sys::opj_stream_set_skip_function(stream, Some(buf_read_stream_skip_fn));
27            openjpeg_sys::opj_stream_set_seek_function(stream, Some(buf_read_stream_seek_fn));
28            openjpeg_sys::opj_stream_set_user_data_length(stream, len as u64);
29            openjpeg_sys::opj_stream_set_user_data(
30                stream,
31                data.cast(),
32                Some(buf_read_stream_free_fn),
33            );
34            stream
35        };
36
37        Self { stream, _buf: buf }
38    }
39
40    #[allow(clippy::needless_pass_by_ref_mut)]
41    pub fn as_raw(&mut self) -> *mut openjpeg_sys::opj_stream_t {
42        self.stream
43    }
44}
45
46extern "C" fn buf_read_stream_free_fn(p_data: *mut c_void) {
47    let ptr: *mut WrappedSlice = p_data.cast();
48    drop(unsafe { Box::from_raw(ptr) });
49}
50
51extern "C" fn buf_read_stream_read_fn(
52    p_buffer: *mut c_void,
53    nb_bytes: usize,
54    p_data: *mut c_void,
55) -> usize {
56    if p_buffer.is_null() || nb_bytes == 0 {
57        return usize::MAX;
58    }
59
60    let slice: &mut WrappedSlice = unsafe { &mut *p_data.cast() };
61    slice
62        .read_into(p_buffer.cast(), nb_bytes)
63        .unwrap_or(usize::MAX)
64}
65
66extern "C" fn buf_read_stream_skip_fn(nb_bytes: i64, p_data: *mut c_void) -> i64 {
67    let slice: &mut WrappedSlice = unsafe { &mut *p_data.cast() };
68    #[allow(
69        clippy::cast_sign_loss,
70        clippy::cast_possible_truncation,
71        clippy::cast_possible_wrap
72    )]
73    {
74        slice.consume(nb_bytes as usize) as i64
75    }
76}
77
78extern "C" fn buf_read_stream_seek_fn(nb_bytes: i64, p_data: *mut c_void) -> i32 {
79    let slice: &mut WrappedSlice = unsafe { &mut *p_data.cast() };
80    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
81    let seek_offset = nb_bytes as usize;
82    let new_offset = slice.seek(seek_offset);
83
84    // Return true if the seek worked.
85    i32::from(seek_offset == new_offset)
86}
87
88struct WrappedSlice<'a> {
89    offset: usize,
90    buf: &'a [u8],
91}
92
93impl<'a> WrappedSlice<'a> {
94    const fn new(buf: &'a [u8]) -> Self {
95        Self { offset: 0, buf }
96    }
97
98    const fn remaining(&self) -> usize {
99        self.buf.len() - self.offset
100    }
101
102    fn seek(&mut self, new_offset: usize) -> usize {
103        // Make sure `new_offset <= buf.len()`
104        self.offset = std::cmp::min(self.buf.len(), new_offset);
105        self.offset
106    }
107
108    fn consume(&mut self, n_bytes: usize) -> usize {
109        let offset = self.offset.saturating_add(n_bytes);
110        // Make sure `offset <= buf.len()`
111        self.offset = self.buf.len().min(offset);
112        self.offset
113    }
114
115    fn read_into(&mut self, out: *mut u8, len: usize) -> Option<usize> {
116        // Get the number of remaining bytes
117        let remaining = self.remaining();
118        if remaining == 0 {
119            // No more bytes.
120            return None;
121        }
122
123        // Try to fill the output buffer
124        let n_read = std::cmp::min(remaining, len);
125        let offset = self.offset;
126        self.consume(n_read);
127
128        unsafe {
129            std::ptr::copy_nonoverlapping(self.buf.as_ptr().add(offset), out, n_read);
130        }
131
132        Some(n_read)
133    }
134}
135
136pub struct EncodeStream<'a> {
137    stream: *mut openjpeg_sys::opj_stream_t,
138    _buf: &'a mut Vec<u8>,
139}
140
141impl<'a> EncodeStream<'a> {
142    pub fn new(buf: &'a mut Vec<u8>) -> Self {
143        let stream = unsafe {
144            let stream = openjpeg_sys::opj_stream_default_create(0);
145            openjpeg_sys::opj_stream_set_write_function(stream, Some(vec_write_stream_write_fn));
146            openjpeg_sys::opj_stream_set_user_data(stream, std::ptr::from_mut(buf).cast(), None);
147            stream
148        };
149
150        Self { stream, _buf: buf }
151    }
152
153    #[allow(clippy::needless_pass_by_ref_mut)]
154    pub fn as_raw(&mut self) -> *mut openjpeg_sys::opj_stream_t {
155        self.stream
156    }
157}
158
159extern "C" fn vec_write_stream_write_fn(
160    p_buffer: *mut c_void,
161    nb_bytes: usize,
162    p_data: *mut c_void,
163) -> usize {
164    if p_buffer.is_null() {
165        return usize::MAX;
166    }
167
168    let vec: &mut Vec<u8> = unsafe { &mut *p_data.cast() };
169
170    let data = unsafe { std::slice::from_raw_parts(p_buffer.cast(), nb_bytes) };
171
172    vec.extend_from_slice(data);
173
174    nb_bytes
175}