libheif_rs/
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    pub fn new(stream: T, total_size: u64) -> StreamReader<T> {
62        StreamReader { stream, total_size }
63    }
64}
65
66impl<T> Reader for StreamReader<T>
67where
68    T: io::Read + io::Seek,
69{
70    fn position(&mut self) -> u64 {
71        self.stream.stream_position().unwrap_or(self.total_size)
72    }
73
74    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
75        self.stream.read_exact(buf).map(|_| buf.len())
76    }
77
78    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
79        self.stream.read_exact(buf)
80    }
81
82    fn seek(&mut self, position: u64) -> io::Result<u64> {
83        self.stream.seek(io::SeekFrom::Start(position as _))
84    }
85
86    fn wait_for_file_size(&mut self, target_size: u64) -> ReaderGrowStatus {
87        if self.stream.stream_position().is_err() {
88            ReaderGrowStatus::Timeout
89        } else if target_size > self.total_size {
90            ReaderGrowStatus::SizeBeyondEof
91        } else {
92            ReaderGrowStatus::SizeReached
93        }
94    }
95}
96
97unsafe extern "C" fn get_position(user_data: *mut c_void) -> i64 {
98    let reader = &mut *(user_data as *mut Box<dyn Reader>);
99    reader.position() as _
100}
101
102unsafe extern "C" fn read(data: *mut c_void, size: usize, user_data: *mut c_void) -> c_int {
103    if data.is_null() || size == 0 {
104        return 0;
105    }
106    let reader = &mut *(user_data as *mut Box<dyn Reader>);
107    let buf = slice::from_raw_parts_mut(data as *mut u8, size);
108    if reader.read_exact(buf).is_ok() {
109        0
110    } else {
111        1
112    }
113}
114
115unsafe extern "C" fn seek(position: i64, user_data: *mut c_void) -> c_int {
116    let reader = &mut *(user_data as *mut Box<dyn Reader>);
117    match reader.seek(position as _) {
118        Ok(_) => 0,
119        Err(_) => 1,
120    }
121}
122
123unsafe extern "C" fn wait_for_file_size(
124    target_size: i64,
125    user_data: *mut c_void,
126) -> lh::heif_reader_grow_status {
127    let reader = &mut *(user_data as *mut Box<dyn Reader>);
128    let target_size = target_size as u64;
129    reader.wait_for_file_size(target_size) as _
130}
131
132#[cfg(not(feature = "v1_19"))]
133pub(crate) static HEIF_READER: lh::heif_reader = lh::heif_reader {
134    reader_api_version: 1,
135    get_position: Some(get_position),
136    read: Some(read),
137    seek: Some(seek),
138    wait_for_file_size: Some(wait_for_file_size),
139};
140
141#[cfg(feature = "v1_19")]
142pub(crate) static HEIF_READER: lh::heif_reader = lh::heif_reader {
143    reader_api_version: 1,
144    get_position: Some(get_position),
145    read: Some(read),
146    seek: Some(seek),
147    wait_for_file_size: Some(wait_for_file_size),
148    request_range: None,
149    preload_range_hint: None,
150    release_file_range: None,
151    release_error_msg: None,
152};