-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding gexiv2_metadata_save_stream #26
Comments
@felixc would this work? #[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum WrapperSeekOrigin {
Begin = 0,
Current = 1,
End = 2,
}
type StreamCanSeek = extern "C" fn(handle: *mut c_void) -> bool;
type StreamCanRead = extern "C" fn(handle: *mut c_void) -> bool;
type StreamCanWrite = extern "C" fn(handle: *mut c_void) -> bool;
type StreamLength = extern "C" fn(handle: *mut c_void) -> c_longlong;
type StreamPosition = extern "C" fn(handle: *mut c_void) -> c_longlong;
type StreamRead = extern "C" fn(handle: *mut c_void, buffer: *mut c_void, offset: c_int, count: c_int) -> c_int;
type StreamWrite = extern "C" fn(handle: *mut c_void, buffer: *const c_void, offset: c_int, count: c_int);
type StreamSeek = extern "C" fn(handle: *mut c_void, offset: c_longlong, origin: WrapperSeekOrigin);
type StreamFlush = extern "C" fn(handle: *mut c_void);
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ManagedStreamCallbacks {
handle: *mut c_void,
can_seek: StreamCanSeek,
can_read: StreamCanRead,
can_write: StreamCanWrite,
length: StreamLength,
position: StreamPosition,
read: StreamRead,
write: StreamWrite,
seek: StreamSeek,
flush: StreamFlush,
}
// ... other code ...
extern {
// ... other public functions ...
pub fn gexiv2_metadata_save_stream(this: *mut GExiv2Metadata, callbacks: *mut ManagedStreamCallbacks, error: *mut *mut GError) -> c_int;
pub fn gexiv2_metadata_open_stream(this: *mut GExiv2Metadata, callbacks: *mut ManagedStreamCallbacks, error: *mut *mut GError) -> c_int; |
Possibly adding this to the test file as well? extern "C" fn mock_can_seek(_handle: *mut c_void) -> bool {
true
}
extern "C" fn mock_can_read(_handle: *mut c_void) -> bool {
true
}
extern "C" fn mock_can_write(_handle: *mut c_void) -> bool {
true
}
extern "C" fn mock_length(handle: *mut c_void) -> c_longlong {
assert!(!handle.is_null(), "Handle is null");
let vec: &Vec<u8> = unsafe { &*(handle as *mut Vec<u8>) };
vec.len() as c_longlong
}
extern "C" fn mock_position(handle: *mut c_void) -> c_longlong {
let cursor: &Cursor<Vec<u8>> = unsafe { &*(handle as *mut Cursor<Vec<u8>>) };
cursor.position() as c_longlong
}
extern "C" fn mock_read(handle: *mut c_void, buffer: *mut c_void, _offset: c_int, count: c_int) -> c_int {
let cursor: &mut Cursor<Vec<u8>> = unsafe { &mut *(handle as *mut Cursor<Vec<u8>>) };
let buffer_slice = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, count as usize) };
match cursor.read(buffer_slice) {
Ok(bytes_read) => bytes_read as c_int,
Err(e) => {
panic!("Error reading: {}", e);
},
}
}
extern "C" fn mock_seek(handle: *mut c_void, offset: c_longlong, origin: WrapperSeekOrigin) {
let cursor: &mut Cursor<Vec<u8>> = unsafe { &mut *(handle as *mut Cursor<Vec<u8>>) };
let pos = match origin {
WrapperSeekOrigin::Begin => SeekFrom::Start(offset as u64),
WrapperSeekOrigin::Current => SeekFrom::Current(offset),
WrapperSeekOrigin::End => SeekFrom::End(offset),
};
match cursor.seek(pos) {
Ok(_) => (),
Err(e) => panic!("Error seeking: {}", e),
}
}
extern "C" fn mock_flush(handle: *mut c_void) {
let cursor: &mut Cursor<Vec<u8>> = unsafe { &mut *(handle as *mut Cursor<Vec<u8>>) };
match cursor.flush() {
Ok(_) => (),
Err(e) => panic!("Error flushing: {}", e),
}
}
extern "C" fn mock_stream_write(handle: *mut c_void, buffer: *const c_void, offset: c_int, count: c_int) {
let cursor: &mut Cursor<Vec<u8>> = unsafe { &mut *(handle as *mut Cursor<Vec<u8>>) };
let buffer_slice = unsafe { std::slice::from_raw_parts(buffer as *const u8, count as usize) };
match cursor.write_all(buffer_slice) {
Ok(_) => {
println!("Wrote {} bytes", count);
},
Err(e) => panic!("Error writing: {}", e),
}
}
#[test]
fn test_metadata_save_to_stream() {
unsafe {
let meta: *mut GExiv2Metadata = make_new_metadata();
let mut output_data: Vec<u8> = Vec::from(MINI_JPEG);
let mut cursor = Cursor::new(output_data.clone());
let mut callbacks = ManagedStreamCallbacks {
handle: &mut cursor as *mut Cursor<Vec<u8>> as *mut c_void,
can_seek: mock_can_seek,
can_read: mock_can_read,
can_write: mock_can_write,
length: mock_length,
position: mock_position,
read: mock_read,
write: mock_stream_write,
seek: mock_seek,
flush: mock_flush,
};
let mut err: *mut GError = ptr::null_mut();
let result = gexiv2_metadata_save_stream(meta, &mut callbacks as *mut ManagedStreamCallbacks, &mut err);
if !err.is_null() {
let error_msg = ffi::CStr::from_ptr((*err).message).to_str().unwrap();
println!("Error: {}", error_msg);
}
assert_eq!(result, 1, "Failed to save metadata to stream.");
// verify the streamed data
let stream_length = mock_length(callbacks.handle);
assert_eq!(stream_length, MINI_JPEG.len() as c_longlong, "Stream length does not match original data length");
// Clean up
gexiv2_metadata_free(meta);
}
}
#[test]
fn test_metadata_open_and_save_to_stream() {
unsafe {
let meta: *mut GExiv2Metadata = gexiv2_metadata_new();
let mut output_data: Vec<u8> = Vec::from(MINI_JPEG);
let mut cursor = Cursor::new(output_data.clone());
let mut callbacks = ManagedStreamCallbacks {
handle: &mut cursor as *mut Cursor<Vec<u8>> as *mut c_void,
can_seek: mock_can_seek,
can_read: mock_can_read,
can_write: mock_can_write,
length: mock_length,
position: mock_position,
read: mock_read,
write: mock_stream_write,
seek: mock_seek,
flush: mock_flush,
};
let mut err: *mut GError = ptr::null_mut();
let _ = gexiv2_metadata_open_stream(meta, &mut callbacks as *mut ManagedStreamCallbacks, &mut err);
if !err.is_null() {
let error_msg = ffi::CStr::from_ptr((*err).message).to_str().unwrap();
println!("Error: {}", error_msg);
}
let mut err: *mut GError = ptr::null_mut();
let result = gexiv2_metadata_save_stream(meta, &mut callbacks as *mut ManagedStreamCallbacks, &mut err);
if !err.is_null() {
let error_msg = ffi::CStr::from_ptr((*err).message).to_str().unwrap();
println!("Error: {}", error_msg);
}
assert_eq!(result, 1, "Failed to save metadata to stream.");
// verify the streamed data
let stream_length = mock_length(callbacks.handle);
assert_eq!(stream_length, MINI_JPEG.len() as c_longlong, "Stream length does not match original data length");
// Clean up
gexiv2_metadata_free(meta);
}
} |
I'd love to get rid of that API in gexiv2, it is cursed and IMHO broken and full of weird assumptions - already from the exiv2 side. I'd prefer to give you an _as_bytes() function instead. |
That would be really amazing! I was able to get a version of that code working locally all in an attempt to avoid writing to disk, but having a supported/public as_bytes() method would be killer. Hopefully, it's not too much of a lift. |
@phako what would it take to get a _as_bytes() function? Anything I could help with? |
Me not being sick or overworked else-wise for a change, mostly. So if you have any code or idea to share, go for it |
Btw, which information are you looking for? the whole image or XMP sidecar data? |
Adding this support would be great
The text was updated successfully, but these errors were encountered: