1use crate::{
4 bindings::{self, gint64},
5 connection::VipsConnection,
6 error::Error,
7 utils::{self, new_c_string, path_to_cstring, result_cond, safe_result_cond},
8 Result,
9};
10use std::{
11 ffi::{c_int, c_void},
12 io::{Read, Seek, SeekFrom},
13 path::Path,
14};
15
16#[derive(Debug, Clone)]
18pub struct VipsSource {
19 pub(crate) ctx: *mut bindings::VipsSource,
20}
21
22pub trait Reader: Read + Seek + Send + 'static {}
23impl<T: Read + Seek + Send + 'static> Reader for T {}
24
25struct SourceContext {
26 reader: Box<dyn Reader>,
27}
28
29impl VipsSource {
30 pub fn new_from_reader<R: Reader>(input: R) -> Result<VipsSource> {
32 unsafe {
33 let source = bindings::vips_source_custom_new();
34
35 if source.is_null() {
36 return Err(
37 Error::InitializationError("Cannot create VipsSourceCustom".to_string()),
38 );
39 }
40
41 let context = Box::new(
42 SourceContext {
43 reader: Box::new(input),
44 },
45 );
46
47 let user_data = Box::into_raw(context) as *mut c_void;
48
49 bindings::g_signal_connect_data(
50 source as *mut c_void,
51 c"read".as_ptr(),
52 Some(
53 std::mem::transmute::<
54 unsafe extern "C" fn(
55 source: *mut bindings::VipsSourceCustom,
56 buffer: *mut c_void,
57 length: gint64,
58 user_data: *mut c_void,
59 ) -> gint64,
60 unsafe extern "C" fn(),
61 >(on_read),
62 ),
63 user_data,
64 Some(source_destroy),
65 0,
66 );
67
68 bindings::g_signal_connect_data(
69 source as *mut c_void,
70 c"seek".as_ptr(),
71 Some(
72 std::mem::transmute::<
73 unsafe extern "C" fn(
74 source: *mut bindings::VipsSourceCustom,
75 offset: gint64,
76 whence: c_int,
77 user_data: *mut c_void,
78 ) -> gint64,
79 unsafe extern "C" fn(),
80 >(on_seek),
81 ),
82 user_data,
83 None,
84 0,
85 );
86
87 Ok(
88 VipsSource {
89 ctx: source as *mut bindings::VipsSource,
90 },
91 )
92 }
93 }
94
95 pub fn new_from_descriptor(descriptor: i32) -> Result<VipsSource> {
97 unsafe {
98 let res = bindings::vips_source_new_from_descriptor(descriptor);
99 result_cond(
100 !res.is_null(),
101 VipsSource {
102 ctx: res,
103 },
104 Error::InitializationError(
105 "Could not initialise VipsSource from descriptor".to_string(),
106 ),
107 )
108 }
109 }
110
111 pub fn new_from_file<P: AsRef<Path>>(filename: P) -> Result<VipsSource> {
113 unsafe {
114 let filename_c_str = path_to_cstring(filename)?;
115 let res = bindings::vips_source_new_from_file(filename_c_str.as_ptr());
116 result_cond(
117 !res.is_null(),
118 VipsSource {
119 ctx: res,
120 },
121 Error::InitializationError("Could not initialise VipsSource from file".to_string()),
122 )
123 }
124 }
125
126 pub fn new_from_memory(buffer: &[u8]) -> Result<VipsSource> {
128 unsafe {
129 let res = bindings::vips_source_new_from_memory(
130 buffer.as_ptr() as *const c_void,
131 buffer.len(),
132 );
133 result_cond(
134 !res.is_null(),
135 VipsSource {
136 ctx: res,
137 },
138 Error::InitializationError(
139 "Could not initialise VipsSource from memory".to_string(),
140 ),
141 )
142 }
143 }
144
145 pub fn new_from_options(option_str: &str) -> Result<VipsSource> {
147 unsafe {
148 let options = new_c_string(option_str)?;
149 let res = bindings::vips_source_new_from_options(options.as_ptr());
150 result_cond(
151 !res.is_null(),
152 VipsSource {
153 ctx: res,
154 },
155 Error::InitializationError(
156 "Could not initialise VipsSource from options".to_string(),
157 ),
158 )
159 }
160 }
161
162 pub fn minimise(&mut self) {
164 unsafe {
165 bindings::vips_source_minimise(self.ctx);
166 }
167 }
168
169 pub fn unminimise(&mut self) -> Result<()> {
171 unsafe {
172 let result = bindings::vips_source_unminimise(self.ctx);
173 utils::result(
174 result,
175 (),
176 Error::OperationError("Error on vips unminimise".to_string()),
177 )
178 }
179 }
180
181 pub fn decode(&mut self) -> Result<()> {
183 unsafe {
184 let result = bindings::vips_source_decode(self.ctx);
185 utils::result(
186 result,
187 (),
188 Error::OperationError("Error on vips decode".to_string()),
189 )
190 }
191 }
192
193 pub fn read(&mut self, length: u64) -> Result<Vec<u8>> {
195 unsafe {
196 let mut bytes = vec![0u8; length as usize];
197 let result = bindings::vips_source_read(
198 self.ctx,
199 bytes.as_mut_ptr() as *mut c_void,
200 length as usize,
201 );
202 result_cond(
203 result != -1,
204 bytes,
205 Error::OperationError("Error on vips_source_read".to_string()),
206 )
207 }
208 }
209
210 pub fn is_mappable(&self) -> bool {
212 unsafe { bindings::vips_source_is_mappable(self.ctx) == 1 }
213 }
214
215 pub fn map(&self) -> Result<Vec<u8>> {
217 unsafe {
218 let mut length = 0;
219 let result = bindings::vips_source_map(
220 self.ctx,
221 &mut length,
222 );
223 safe_result_cond(
224 !result.is_null(),
225 || {
226 std::slice::from_raw_parts(
227 result as *const u8,
228 length,
229 )
230 .to_vec()
231 },
232 Error::OperationError("Error on vips map".to_string()),
233 )
234 }
235 }
236
237 pub fn seek(&mut self, offset: i64, whence: i32) -> Result<i64> {
239 unsafe {
240 let result = bindings::vips_source_seek(
241 self.ctx,
242 offset,
243 whence,
244 );
245 result_cond(
246 result != -1,
247 result,
248 Error::OperationError("Error on vips seek".to_string()),
249 )
250 }
251 }
252
253 pub fn rewind(&mut self) -> Result<()> {
255 unsafe {
256 let result = bindings::vips_source_rewind(self.ctx);
257 result_cond(
258 result != -1,
259 (),
260 Error::OperationError("Error on vips rewind".to_string()),
261 )
262 }
263 }
264
265 pub fn length(&self) -> Result<i64> {
267 unsafe {
268 let result = bindings::vips_source_length(self.ctx);
269 result_cond(
270 result != -1,
271 result,
272 Error::OperationError("Error on vips length".to_string()),
273 )
274 }
275 }
276
277 pub fn filename(&self) -> Option<String> {
279 unsafe { VipsConnection::filename(&mut (*self.ctx).parent_object) }
280 }
281
282 pub fn nick(&self) -> Option<String> {
284 unsafe { VipsConnection::nick(&mut (*self.ctx).parent_object) }
285 }
286}
287
288unsafe extern "C" fn on_read(
289 _: *mut bindings::VipsSourceCustom,
290 buffer: *mut c_void,
291 length: gint64,
292 user_data: *mut c_void,
293) -> gint64 {
294 let ctx = &mut *(user_data as *mut SourceContext);
295
296 let slice = std::slice::from_raw_parts_mut(
297 buffer as *mut u8,
298 length as usize,
299 );
300
301 match ctx
302 .reader
303 .read(slice)
304 {
305 Ok(n) => n as gint64,
306 Err(_) => -1,
307 }
308}
309
310unsafe extern "C" fn on_seek(
311 _: *mut bindings::VipsSourceCustom,
312 offset: gint64,
313 whence: c_int,
314 user_data: *mut c_void,
315) -> gint64 {
316 let ctx = &mut *(user_data as *mut SourceContext);
317
318 let pos = match whence {
319 0 => SeekFrom::Start(offset as u64),
320 1 => SeekFrom::Current(offset),
321 2 => SeekFrom::End(offset),
322 _ => return -1,
323 };
324
325 match ctx
326 .reader
327 .seek(pos)
328 {
329 Ok(n) => n as gint64,
330 Err(_) => -1,
331 }
332}
333
334unsafe extern "C" fn source_destroy(data: *mut c_void, _: *mut bindings::_GClosure) {
335 let _ = Box::from_raw(data as *mut SourceContext);
336}
337
338impl Drop for VipsSource {
339 fn drop(&mut self) {
340 unsafe {
341 if !self
342 .ctx
343 .is_null()
344 {
345 bindings::g_object_unref(self.ctx as *mut c_void);
346 }
347 }
348 }
349}