Skip to main content

xberg_libheif/
reader.rs

1use std::os::raw::{c_int, c_void};
2use std::{io, slice};
3
4use libheif_sys as lh;
5
6use crate::enums::ReaderGrowStatus;
7
8pub trait Reader {
9    /// Current position, in bytes, inside a source.
10    fn position(&mut self) -> u64;
11
12    /// Pull some bytes from a source into the specified buffer, returning
13    /// how many bytes were read.
14    #[deprecated(since = "2.4.0", note = "use 'read_exact' method instead.")]
15    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
16
17    /// Pull bytes from a source into the specified buffer.
18    fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> {
19        loop {
20            #[allow(deprecated)]
21            let size = self.read(buf)?;
22            if size == buf.len() {
23                return Ok(());
24            }
25            buf = &mut buf[size..];
26        }
27    }
28
29    /// Seek to a position, in bytes, from the start of a source.
30    fn seek(&mut self, position: u64) -> io::Result<u64>;
31
32    /// Wait until a source will be ready to read bytes to
33    /// the specified position.
34    ///
35    /// When calling this function, `libheif` wants to make sure that it can read the file
36    /// up to `target_size`.
37    /// This is useful when the file is currently downloaded and may
38    /// grow with time.
39    /// You may, for example, extract the image sizes even before the actual
40    /// compressed image data has been completely downloaded.
41    ///
42    /// Even if your input files do not grow, you will have to implement at least
43    /// detection whether the `target_size` is above the (fixed) file length
44    /// (in this case, return 'ReaderGrowStatus::SizeBeyondEof').
45    fn wait_for_file_size(&mut self, target_size: u64) -> ReaderGrowStatus;
46}
47
48#[derive(Debug)]
49pub struct StreamReader<T>
50where
51    T: io::Read + io::Seek,
52{
53    stream: T,
54    total_size: u64,
55}
56
57impl<T> StreamReader<T>
58where
59    T: io::Read + io::Seek,
60{
61    #[allow(unsafe_code)]
62    pub fn new(stream: T, total_size: u64) -> StreamReader<T> {
63        StreamReader { stream, total_size }
64    }
65}
66
67impl<T> Reader for StreamReader<T>
68where
69    T: io::Read + io::Seek,
70{
71    #[allow(unsafe_code)]
72    fn position(&mut self) -> u64 {
73        self.stream.stream_position().unwrap_or(self.total_size)
74    }
75
76    #[allow(unsafe_code)]
77    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
78        self.stream.read_exact(buf).map(|_| buf.len())
79    }
80
81    #[allow(unsafe_code)]
82    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
83        self.stream.read_exact(buf)
84    }
85
86    #[allow(unsafe_code)]
87    fn seek(&mut self, position: u64) -> io::Result<u64> {
88        self.stream.seek(io::SeekFrom::Start(position as _))
89    }
90
91    #[allow(unsafe_code)]
92    fn wait_for_file_size(&mut self, target_size: u64) -> ReaderGrowStatus {
93        if self.stream.stream_position().is_err() {
94            ReaderGrowStatus::Timeout
95        } else if target_size > self.total_size {
96            ReaderGrowStatus::SizeBeyondEof
97        } else {
98            ReaderGrowStatus::SizeReached
99        }
100    }
101}
102
103#[allow(unsafe_code)]
104unsafe extern "C" fn get_position(user_data: *mut c_void) -> i64 {
105    let reader = unsafe { &mut *(user_data as *mut Box<dyn Reader>) };
106    reader.position() as _
107}
108
109#[allow(unsafe_code)]
110unsafe extern "C" fn read(data: *mut c_void, size: usize, user_data: *mut c_void) -> c_int {
111    if data.is_null() || size == 0 {
112        return 0;
113    }
114    let reader = unsafe { &mut *(user_data as *mut Box<dyn Reader>) };
115    let buf = unsafe { slice::from_raw_parts_mut(data as *mut u8, size) };
116    if reader.read_exact(buf).is_ok() { 0 } else { 1 }
117}
118
119#[allow(unsafe_code)]
120unsafe extern "C" fn seek(position: i64, user_data: *mut c_void) -> c_int {
121    let reader = unsafe { &mut *(user_data as *mut Box<dyn Reader>) };
122    match reader.seek(position as _) {
123        Ok(_) => 0,
124        Err(_) => 1,
125    }
126}
127
128#[allow(unsafe_code)]
129unsafe extern "C" fn wait_for_file_size(target_size: i64, user_data: *mut c_void) -> lh::heif_reader_grow_status {
130    let reader = unsafe { &mut *(user_data as *mut Box<dyn Reader>) };
131    let target_size = target_size as u64;
132    reader.wait_for_file_size(target_size) as _
133}
134
135#[cfg(not(feature = "v1_19"))]
136pub(crate) static HEIF_READER: lh::heif_reader = lh::heif_reader {
137    reader_api_version: 1,
138    get_position: Some(get_position),
139    read: Some(read),
140    seek: Some(seek),
141    wait_for_file_size: Some(wait_for_file_size),
142};
143
144#[cfg(feature = "v1_19")]
145pub(crate) static HEIF_READER: lh::heif_reader = lh::heif_reader {
146    reader_api_version: 1,
147    get_position: Some(get_position),
148    read: Some(read),
149    seek: Some(seek),
150    wait_for_file_size: Some(wait_for_file_size),
151    request_range: None,
152    preload_range_hint: None,
153    release_file_range: None,
154    release_error_msg: None,
155};