diff --git a/pyvips/gvalue.py b/pyvips/gvalue.py index 1631844..88f2360 100644 --- a/pyvips/gvalue.py +++ b/pyvips/gvalue.py @@ -220,36 +220,17 @@ def set(self, value): memory = glib_lib.g_malloc(len(value)) ffi.memmove(memory, value, len(value)) - # this is horrible! - # - # * in API mode, we must have 8.6+ and use set_blob_free to - # attach the metadata to avoid leaks - # * pre-8.6, we just pass a NULL free pointer and live with the - # leak - # - # this is because in API mode you can't pass a builtin (what - # vips_lib.g_free() becomes) as a parameter to ffi.callback(), and - # vips_value_set_blob() needs a callback for arg 2 - # - # additionally, you can't make a py def which calls g_free() and - # then use the py def in the callback, since libvips will trigger - # these functions during cleanup, and py will have shut down by - # then and you'll get a segv - - if at_least_libvips(8, 6): + # In API mode, we use set_blob_free in a backwards compatible way. + # For pre-8.6 libvipses, in ABI mode, we declare the type of the + # free func in set_blob incorrectly so that we can pass g_free + # at runtime without triggering an exception. + if pyvips.API_mode or at_least_libvips(8, 6): vips_lib.vips_value_set_blob_free(self.gvalue, memory, len(value)) else: - # we declare the type of the free func in set_blob incorrectly - # so that we can pass g_free at runtime without triggering an - # exception - if pyvips.API_mode: - vips_lib.vips_value_set_blob(self.gvalue, - ffi.NULL, memory, len(value)) - else: - vips_lib.vips_value_set_blob(self.gvalue, - glib_lib.g_free, - memory, len(value)) + vips_lib.vips_value_set_blob(self.gvalue, + glib_lib.g_free, + memory, len(value)) else: raise Error('unsupported gtype for set {0}, fundamental {1}'. format(type_name(gtype), type_name(fundamental))) diff --git a/pyvips/pyvips_build.py b/pyvips/pyvips_build.py index e03fee1..e262483 100644 --- a/pyvips/pyvips_build.py +++ b/pyvips/pyvips_build.py @@ -9,15 +9,35 @@ if pkgconfig.installed('vips', '< 8.2'): raise Exception('pkg-config "vips" is too old -- need libvips 8.2 or later') +major, minor, micro = [int(s) for s in pkgconfig.modversion('vips').split('.')] + ffibuilder = FFI() +# vips_value_set_blob_free and vips_area_free_cb compat for libvips < 8.6 +compat = ''' +int +vips_area_free_cb(void *mem, VipsArea *area) +{ + g_free(mem); + + return 0; +} + +void +vips_value_set_blob_free(GValue* value, void* data, size_t length) +{ + vips_value_set_blob(value, (VipsCallbackFn) vips_area_free_cb, + data, length); +} +''' if major == 8 and minor < 6 else '' + ffibuilder.set_source("_libvips", - r""" + f""" #include + {compat} """, **pkgconfig.parse('vips')) -major, minor, micro = [int(s) for s in pkgconfig.modversion('vips').split('.')] features = { 'major': major, diff --git a/pyvips/vdecls.py b/pyvips/vdecls.py index 2dbc2a8..d3658af 100644 --- a/pyvips/vdecls.py +++ b/pyvips/vdecls.py @@ -110,9 +110,6 @@ def cdefs(features): void vips_value_set_array_int (GValue* value, const int* array, int n ); void vips_value_set_array_image (GValue *value, int n); - typedef void (*FreeFn)(void* a); - void vips_value_set_blob (GValue* value, - FreeFn free_fn, void* data, size_t length); int g_value_get_boolean (const GValue* value); int g_value_get_int (GValue* value); @@ -405,6 +402,15 @@ def cdefs(features): int vips_cache_get_max_files(); ''' + # we must only define this in ABI mode ... in API mode we use + # vips_value_set_blob_free in a backwards compatible way + if not features['api']: + code += ''' + typedef void (*FreeFn)(void* a); + void vips_value_set_blob (GValue* value, + FreeFn free_fn, void* data, size_t length); + ''' + if _at_least(features, 8, 5): code += ''' char** vips_image_get_fields (VipsImage* image);