feat(mobile): free native core buffers through a dedicated export

This commit is contained in:
Santo Shakil
2026-07-23 03:41:34 +06:00
parent 3da42ca655
commit d6b077a59c
9 changed files with 45 additions and 12 deletions
@@ -86,7 +86,7 @@ void main() {
expect(pixels[i], 255, reason: 'alpha at $i');
}
expect(pixels.toSet().length, greaterThan(2));
malloc.free(ptr);
immich_core_free(ptr);
expect(immich_core_thumbhash_decode(hashPtr, 4, info), equals(nullptr));
} finally {
+1 -1
View File
@@ -35,7 +35,7 @@ Uint8List? decodeDriftThumbHash(String? thumbHash) {
try {
return _rgbaToBmp(rgba, info[0], info[1], info[2]);
} finally {
malloc.free(rgba);
immich_core_free(rgba);
}
} finally {
malloc.free(hashPtr);
@@ -20,6 +20,15 @@ char *immich_core_version(void);
*/
void immich_core_free_string(char *ptr);
/**
* Release a buffer returned by this library, such as a decoded ThumbHash.
*
* # Safety
* `ptr` must be a buffer previously returned by this library, or null, and
* must not be released twice.
*/
void immich_core_free(uint8_t *ptr);
/**
* Returns whether an EXIF orientation swaps width and height.
*/
@@ -55,8 +64,8 @@ bool immich_core_rgba1010102_to_rgba8888(const uint8_t *src,
uintptr_t dst_len);
/**
* Decodes a ThumbHash into a libc buffer and writes width, height, and row bytes to `out_info`.
* Free the buffer with `free`; malformed hashes return null.
* Decodes a ThumbHash into a new buffer and writes width, height, and row bytes to `out_info`.
* Free the buffer with `immich_core_free`; malformed hashes return null.
*
* # Safety
* `hash` must be valid for `hash_len` bytes and `out_info` for three u32 writes.
@@ -28,6 +28,17 @@ pub unsafe extern "C" fn immich_core_free_string(ptr: *mut c_char) {
});
}
/// Release a buffer returned by this library, such as a decoded ThumbHash.
///
/// # Safety
/// `ptr` must be a buffer previously returned by this library, or null, and
/// must not be released twice.
#[no_mangle]
pub unsafe extern "C" fn immich_core_free(ptr: *mut u8) {
// SAFETY: the buffer came from this library's libc allocation; free accepts null.
unsafe { libc::free(ptr as *mut core::ffi::c_void) };
}
fn guard<T>(sentinel: T, f: impl FnOnce() -> T + std::panic::UnwindSafe) -> T {
crate::log::ensure_panic_hook();
std::panic::catch_unwind(f).unwrap_or(sentinel)
@@ -22,8 +22,8 @@ pub(crate) fn decode_malloc(hash: &[u8]) -> Option<(*mut u8, u32, u32)> {
Some((dst, w, h))
}
/// Decodes a ThumbHash into a libc buffer and writes width, height, and row bytes to `out_info`.
/// Free the buffer with `free`; malformed hashes return null.
/// Decodes a ThumbHash into a new buffer and writes width, height, and row bytes to `out_info`.
/// Free the buffer with `immich_core_free`; malformed hashes return null.
///
/// # Safety
/// `hash` must be valid for `hash_len` bytes and `out_info` for three u32 writes.
+1 -1
View File
@@ -18,4 +18,4 @@ pub use capi::image::{
immich_core_rotate_rgba8888,
};
pub use capi::thumbhash::immich_core_thumbhash_decode;
pub use capi::{immich_core_free_string, immich_core_version};
pub use capi::{immich_core_free, immich_core_free_string, immich_core_version};
+7 -2
View File
@@ -4,7 +4,7 @@ use std::ffi::CStr;
use std::ptr;
use immich_core_ffi::{
immich_core_free_string, immich_core_orientation_swaps_dims,
immich_core_free, immich_core_free_string, immich_core_orientation_swaps_dims,
immich_core_rgba1010102_to_rgba8888, immich_core_rotate_rgba8888, immich_core_thumbhash_decode,
immich_core_version,
};
@@ -186,7 +186,12 @@ fn thumbhash_decodes_into_a_malloc_buffer() {
let pixels = unsafe { std::slice::from_raw_parts(ptr, len) };
assert!(pixels.chunks_exact(4).all(|px| px[3] == 255));
assert!(pixels.chunks_exact(4).any(|px| px[0] != pixels[0]));
unsafe { libc::free(ptr as *mut libc::c_void) };
unsafe { immich_core_free(ptr) };
}
#[test]
fn free_accepts_null() {
unsafe { immich_core_free(ptr::null_mut()) };
}
#[test]
@@ -18,6 +18,14 @@ external ffi.Pointer<ffi.Char> immich_core_version();
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Char>)>()
external void immich_core_free_string(ffi.Pointer<ffi.Char> ptr);
/// Release a buffer returned by this library, such as a decoded ThumbHash.
///
/// # Safety
/// `ptr` must be a buffer previously returned by this library, or null, and
/// must not be released twice.
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Uint8>)>()
external void immich_core_free(ffi.Pointer<ffi.Uint8> ptr);
/// Returns whether an EXIF orientation swaps width and height.
@ffi.Native<ffi.Bool Function(ffi.Int32)>()
external bool immich_core_orientation_swaps_dims(int orientation);
@@ -74,8 +82,8 @@ external bool immich_core_rgba1010102_to_rgba8888(
int dst_len,
);
/// Decodes a ThumbHash into a libc buffer and writes width, height, and row bytes to `out_info`.
/// Free the buffer with `free`; malformed hashes return null.
/// Decodes a ThumbHash into a new buffer and writes width, height, and row bytes to `out_info`.
/// Free the buffer with `immich_core_free`; malformed hashes return null.
///
/// # Safety
/// `hash` must be valid for `hash_len` bytes and `out_info` for three u32 writes.
@@ -125,7 +125,7 @@ void main() {
expect(pixels[i], 255, reason: 'alpha at $i');
}
expect(pixels.toSet().length, greaterThan(2));
malloc.free(ptr);
immich_core_free(ptr);
expect(immich_core_thumbhash_decode(hashPtr, 4, info), equals(nullptr));
} finally {