From 58579b2dcd2e23c139a5649ba2eefd1f2844f1e1 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Tue, 5 Jan 2016 14:16:17 -0600 Subject: [PATCH] FFI Interface methods and tests Added FFI interface methods to the library Added a set of tests for the FFI. currently only the python one is working as intended. the c one isn't complete yet. Need to add example tests for java, ruby, go?... Woot! FFI!!! --- .gitignore | 4 + Cargo.toml | 1 + FFI-tests/_ffi_test_py.c | 638 +++++++++++++++++++++++++++++++ FFI-tests/ffi_test.c | 28 ++ FFI-tests/ffi_test.py | 34 ++ FFI-tests/ffi_test_build.py | 26 ++ FFI-tests/run_ffi_test_python.sh | 3 + FFI-tests/test_images | 1 + src/lib.rs | 101 +++-- 9 files changed, 805 insertions(+), 31 deletions(-) create mode 100644 FFI-tests/_ffi_test_py.c create mode 100644 FFI-tests/ffi_test.c create mode 100755 FFI-tests/ffi_test.py create mode 100755 FFI-tests/ffi_test_build.py create mode 100755 FFI-tests/run_ffi_test_python.sh create mode 120000 FFI-tests/test_images diff --git a/.gitignore b/.gitignore index fe4d3be..492b3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ Cargo.lock #Rustfmt backup files *.rs.bk + +# FFI test binaries +FFI-tests/*.so +FFI-tests/*.o diff --git a/Cargo.toml b/Cargo.toml index d792e6b..1232267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ image = "0.6.0" complex = "0.8.0" dft = "0.4.1" sha1 = "0.1.1" +libc = "0.2.4" diff --git a/FFI-tests/_ffi_test_py.c b/FFI-tests/_ffi_test_py.c new file mode 100644 index 0000000..890c322 --- /dev/null +++ b/FFI-tests/_ffi_test_py.c @@ -0,0 +1,638 @@ +#define _CFFI_ +#include +#ifdef __cplusplus +extern "C" { +#endif +#include + +/* This part is from file 'cffi/parse_c_type.h'. It is copied at the + beginning of C sources generated by CFFI's ffi.set_source(). */ + +typedef void *_cffi_opcode_t; + +#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) +#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) + +#define _CFFI_OP_PRIMITIVE 1 +#define _CFFI_OP_POINTER 3 +#define _CFFI_OP_ARRAY 5 +#define _CFFI_OP_OPEN_ARRAY 7 +#define _CFFI_OP_STRUCT_UNION 9 +#define _CFFI_OP_ENUM 11 +#define _CFFI_OP_FUNCTION 13 +#define _CFFI_OP_FUNCTION_END 15 +#define _CFFI_OP_NOOP 17 +#define _CFFI_OP_BITFIELD 19 +#define _CFFI_OP_TYPENAME 21 +#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs +#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs +#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) +#define _CFFI_OP_CONSTANT 29 +#define _CFFI_OP_CONSTANT_INT 31 +#define _CFFI_OP_GLOBAL_VAR 33 +#define _CFFI_OP_DLOPEN_FUNC 35 +#define _CFFI_OP_DLOPEN_CONST 37 +#define _CFFI_OP_GLOBAL_VAR_F 39 +#define _CFFI_OP_EXTERN_PYTHON 41 + +#define _CFFI_PRIM_VOID 0 +#define _CFFI_PRIM_BOOL 1 +#define _CFFI_PRIM_CHAR 2 +#define _CFFI_PRIM_SCHAR 3 +#define _CFFI_PRIM_UCHAR 4 +#define _CFFI_PRIM_SHORT 5 +#define _CFFI_PRIM_USHORT 6 +#define _CFFI_PRIM_INT 7 +#define _CFFI_PRIM_UINT 8 +#define _CFFI_PRIM_LONG 9 +#define _CFFI_PRIM_ULONG 10 +#define _CFFI_PRIM_LONGLONG 11 +#define _CFFI_PRIM_ULONGLONG 12 +#define _CFFI_PRIM_FLOAT 13 +#define _CFFI_PRIM_DOUBLE 14 +#define _CFFI_PRIM_LONGDOUBLE 15 + +#define _CFFI_PRIM_WCHAR 16 +#define _CFFI_PRIM_INT8 17 +#define _CFFI_PRIM_UINT8 18 +#define _CFFI_PRIM_INT16 19 +#define _CFFI_PRIM_UINT16 20 +#define _CFFI_PRIM_INT32 21 +#define _CFFI_PRIM_UINT32 22 +#define _CFFI_PRIM_INT64 23 +#define _CFFI_PRIM_UINT64 24 +#define _CFFI_PRIM_INTPTR 25 +#define _CFFI_PRIM_UINTPTR 26 +#define _CFFI_PRIM_PTRDIFF 27 +#define _CFFI_PRIM_SIZE 28 +#define _CFFI_PRIM_SSIZE 29 +#define _CFFI_PRIM_INT_LEAST8 30 +#define _CFFI_PRIM_UINT_LEAST8 31 +#define _CFFI_PRIM_INT_LEAST16 32 +#define _CFFI_PRIM_UINT_LEAST16 33 +#define _CFFI_PRIM_INT_LEAST32 34 +#define _CFFI_PRIM_UINT_LEAST32 35 +#define _CFFI_PRIM_INT_LEAST64 36 +#define _CFFI_PRIM_UINT_LEAST64 37 +#define _CFFI_PRIM_INT_FAST8 38 +#define _CFFI_PRIM_UINT_FAST8 39 +#define _CFFI_PRIM_INT_FAST16 40 +#define _CFFI_PRIM_UINT_FAST16 41 +#define _CFFI_PRIM_INT_FAST32 42 +#define _CFFI_PRIM_UINT_FAST32 43 +#define _CFFI_PRIM_INT_FAST64 44 +#define _CFFI_PRIM_UINT_FAST64 45 +#define _CFFI_PRIM_INTMAX 46 +#define _CFFI_PRIM_UINTMAX 47 + +#define _CFFI__NUM_PRIM 48 +#define _CFFI__UNKNOWN_PRIM (-1) +#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) +#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) + +#define _CFFI__IO_FILE_STRUCT (-1) + + +struct _cffi_global_s { + const char *name; + void *address; + _cffi_opcode_t type_op; + void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown + // OP_CPYTHON_BLTN_*: addr of direct function +}; + +struct _cffi_getconst_s { + unsigned long long value; + const struct _cffi_type_context_s *ctx; + int gindex; +}; + +struct _cffi_struct_union_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_STRUCT_UNION + int flags; // _CFFI_F_* flags below + size_t size; + int alignment; + int first_field_index; // -> _cffi_fields array + int num_fields; +}; +#define _CFFI_F_UNION 0x01 // is a union, not a struct +#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the + // "standard layout" or if some are missing +#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct +#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() +#define _CFFI_F_OPAQUE 0x10 // opaque + +struct _cffi_field_s { + const char *name; + size_t field_offset; + size_t field_size; + _cffi_opcode_t field_type_op; +}; + +struct _cffi_enum_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_ENUM + int type_prim; // _CFFI_PRIM_xxx + const char *enumerators; // comma-delimited string +}; + +struct _cffi_typename_s { + const char *name; + int type_index; /* if opaque, points to a possibly artificial + OP_STRUCT which is itself opaque */ +}; + +struct _cffi_type_context_s { + _cffi_opcode_t *types; + const struct _cffi_global_s *globals; + const struct _cffi_field_s *fields; + const struct _cffi_struct_union_s *struct_unions; + const struct _cffi_enum_s *enums; + const struct _cffi_typename_s *typenames; + int num_globals; + int num_struct_unions; + int num_enums; + int num_typenames; + const char *const *includes; + int num_types; + int flags; /* future extension */ +}; + +struct _cffi_parse_info_s { + const struct _cffi_type_context_s *ctx; + _cffi_opcode_t *output; + unsigned int output_size; + size_t error_location; + const char *error_message; +}; + +struct _cffi_externpy_s { + const char *name; + size_t size_of_result; + void *reserved1, *reserved2; +}; + +#ifdef _CFFI_INTERNAL +static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); +static int search_in_globals(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +#endif + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ + typedef unsigned char _Bool; +# endif +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +#endif + +#ifdef __GNUC__ +# define _CFFI_UNUSED_FN __attribute__((unused)) +#else +# define _CFFI_UNUSED_FN /* nothing */ +#endif + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyInt_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + not used any more +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) +#define _cffi_call_python \ + ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[25]) +#define _CFFI_NUM_EXPORTS 26 + +typedef struct _ctypedescr CTypeDescrObject; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; + +#define _cffi_type(index) ( \ + assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ + (CTypeDescrObject *)_cffi_types[index]) + +static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, + const struct _cffi_type_context_s *ctx) +{ + PyObject *module, *o_arg, *new_module; + void *raw[] = { + (void *)module_name, + (void *)version, + (void *)_cffi_exports, + (void *)ctx, + }; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + o_arg = PyLong_FromVoidPtr((void *)raw); + if (o_arg == NULL) + goto failure; + + new_module = PyObject_CallMethod( + module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); + + Py_DECREF(o_arg); + Py_DECREF(module); + return new_module; + + failure: + Py_XDECREF(module); + return NULL; +} + +_CFFI_UNUSED_FN +static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected, + const char *fnname) +{ + if (PyTuple_GET_SIZE(args_tuple) != expected) { + PyErr_Format(PyExc_TypeError, + "%.150s() takes exactly %zd arguments (%zd given)", + fnname, expected, PyTuple_GET_SIZE(args_tuple)); + return NULL; + } + return &PyTuple_GET_ITEM(args_tuple, 0); /* pointer to the first item, + the others follow */ +} + +/********** end CPython-specific section **********/ +#else +_CFFI_UNUSED_FN +static void (*_cffi_call_python)(struct _cffi_externpy_s *, char *); +#endif + + +#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) + +#define _cffi_prim_int(size, sign) \ + ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ + (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ + (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ + (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ + _CFFI__UNKNOWN_PRIM) + +#define _cffi_prim_float(size) \ + ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ + (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ + (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ + _CFFI__UNKNOWN_FLOAT_PRIM) + +#define _cffi_check_int(got, got_nonpos, expected) \ + ((got_nonpos) == (expected <= 0) && \ + (got) == (unsigned long long)expected) + +#ifdef __cplusplus +} +#endif + +/************************************************************/ + + + #include + + +/************************************************************/ + +static void *_cffi_types[] = { +/* 0 */ _CFFI_OP(_CFFI_OP_FUNCTION, 6), // uint64_t()(char const *) +/* 1 */ _CFFI_OP(_CFFI_OP_POINTER, 5), // char const * +/* 2 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), +/* 3 */ _CFFI_OP(_CFFI_OP_FUNCTION, 7), // void()(void) +/* 4 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), +/* 5 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 2), // char +/* 6 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 24), // uint64_t +/* 7 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 0), // void +}; + +static uint64_t _cffi_d_ext_get_ahash(char const * x0) +{ + return ext_get_ahash(x0); +} +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_ext_get_ahash(PyObject *self, PyObject *arg0) +{ + char const * x0; + Py_ssize_t datasize; + uint64_t result; + + datasize = _cffi_prepare_pointer_call_argument( + _cffi_type(1), arg0, (char **)&x0); + if (datasize != 0) { + if (datasize < 0) + return NULL; + x0 = (char const *)alloca((size_t)datasize); + memset((void *)x0, 0, (size_t)datasize); + if (_cffi_convert_array_from_object((char *)x0, _cffi_type(1), arg0) < 0) + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { result = ext_get_ahash(x0); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + (void)self; /* unused */ + return _cffi_from_c_int(result, uint64_t); +} +#else +# define _cffi_f_ext_get_ahash _cffi_d_ext_get_ahash +#endif + +static uint64_t _cffi_d_ext_get_dhash(char const * x0) +{ + return ext_get_dhash(x0); +} +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_ext_get_dhash(PyObject *self, PyObject *arg0) +{ + char const * x0; + Py_ssize_t datasize; + uint64_t result; + + datasize = _cffi_prepare_pointer_call_argument( + _cffi_type(1), arg0, (char **)&x0); + if (datasize != 0) { + if (datasize < 0) + return NULL; + x0 = (char const *)alloca((size_t)datasize); + memset((void *)x0, 0, (size_t)datasize); + if (_cffi_convert_array_from_object((char *)x0, _cffi_type(1), arg0) < 0) + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { result = ext_get_dhash(x0); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + (void)self; /* unused */ + return _cffi_from_c_int(result, uint64_t); +} +#else +# define _cffi_f_ext_get_dhash _cffi_d_ext_get_dhash +#endif + +static uint64_t _cffi_d_ext_get_phash(char const * x0) +{ + return ext_get_phash(x0); +} +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_ext_get_phash(PyObject *self, PyObject *arg0) +{ + char const * x0; + Py_ssize_t datasize; + uint64_t result; + + datasize = _cffi_prepare_pointer_call_argument( + _cffi_type(1), arg0, (char **)&x0); + if (datasize != 0) { + if (datasize < 0) + return NULL; + x0 = (char const *)alloca((size_t)datasize); + memset((void *)x0, 0, (size_t)datasize); + if (_cffi_convert_array_from_object((char *)x0, _cffi_type(1), arg0) < 0) + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { result = ext_get_phash(x0); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + (void)self; /* unused */ + return _cffi_from_c_int(result, uint64_t); +} +#else +# define _cffi_f_ext_get_phash _cffi_d_ext_get_phash +#endif + +static void _cffi_d_init(void) +{ + init(); +} +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_init(PyObject *self, PyObject *noarg) +{ + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { init(); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + (void)self; /* unused */ + (void)noarg; /* unused */ + Py_INCREF(Py_None); + return Py_None; +} +#else +# define _cffi_f_init _cffi_d_init +#endif + +static void _cffi_d_teardown(void) +{ + teardown(); +} +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_teardown(PyObject *self, PyObject *noarg) +{ + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { teardown(); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + (void)self; /* unused */ + (void)noarg; /* unused */ + Py_INCREF(Py_None); + return Py_None; +} +#else +# define _cffi_f_teardown _cffi_d_teardown +#endif + +static const struct _cffi_global_s _cffi_globals[] = { + { "ext_get_ahash", (void *)_cffi_f_ext_get_ahash, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 0), (void *)_cffi_d_ext_get_ahash }, + { "ext_get_dhash", (void *)_cffi_f_ext_get_dhash, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 0), (void *)_cffi_d_ext_get_dhash }, + { "ext_get_phash", (void *)_cffi_f_ext_get_phash, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 0), (void *)_cffi_d_ext_get_phash }, + { "init", (void *)_cffi_f_init, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_N, 3), (void *)_cffi_d_init }, + { "teardown", (void *)_cffi_f_teardown, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_N, 3), (void *)_cffi_d_teardown }, +}; + +static const struct _cffi_type_context_s _cffi_type_context = { + _cffi_types, + _cffi_globals, + NULL, /* no fields */ + NULL, /* no struct_unions */ + NULL, /* no enums */ + NULL, /* no typenames */ + 5, /* num_globals */ + 0, /* num_struct_unions */ + 0, /* num_enums */ + 0, /* num_typenames */ + NULL, /* no includes */ + 8, /* num_types */ + 0, /* flags */ +}; + +#ifdef PYPY_VERSION +PyMODINIT_FUNC +_cffi_pypyinit__ffi_test_py(const void *p[]) +{ + p[0] = (const void *)0x2601; + p[1] = &_cffi_type_context; +} +# ifdef _MSC_VER + PyMODINIT_FUNC +# if PY_MAJOR_VERSION >= 3 + PyInit__ffi_test_py(void) { return NULL; } +# else + init_ffi_test_py(void) { } +# endif +# endif +#elif PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit__ffi_test_py(void) +{ + return _cffi_init("_ffi_test_py", 0x2601, &_cffi_type_context); +} +#else +PyMODINIT_FUNC +init_ffi_test_py(void) +{ + _cffi_init("_ffi_test_py", 0x2601, &_cffi_type_context); +} +#endif diff --git a/FFI-tests/ffi_test.c b/FFI-tests/ffi_test.c new file mode 100644 index 0000000..2ef8edb --- /dev/null +++ b/FFI-tests/ffi_test.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +int main() { + uint64_t (*ext_get_ahash)(const char *); + uint64_t (*ext_get_dhash)(const char *); + uint64_t (*ext_get_phash)(const char *); + + imglib = dlopen("./libpihash.so", RTLD_LAZY); + if ( imglib != NULL ) { + + } + + + + + printf("Large_Test_AHash: ", largeA); + printf("Large_Test_DHash: ", largeD); + printf("Large_Test_PHash: ", largeP); + printf("Medium_Test_AHash: ", mediumA); + printf("Medium_Test_DHash: ", mediumD); + printf("Medium_Test_PHash: ", mediumP); + printf("Small_Test_AHash: ", smallA); + printf("Small_Test_DHash: ", smallD); + printf("Small_Test_PHash: ", smallP); +} diff --git a/FFI-tests/ffi_test.py b/FFI-tests/ffi_test.py new file mode 100755 index 0000000..eafa4bb --- /dev/null +++ b/FFI-tests/ffi_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +from _ffi_test_py import ffi, lib + +large_image_path = "test_images/sample_01_large.jpg".encode(encoding="utf-8") +medium_image_path = "test_images/sample_01_medium.jpg".encode(encoding="utf-8") +small_image_path = "test_images/sample_01_small.jpg".encode(encoding="utf-8") + +print("starting test") + +#initialize the library +lib.init() + +# Print the path and the bytes as hex for debugging +#print(large_image_path) +#print('\\x'+'\\x'.join('{:02x}'.format(x) for x in large_image_path)) + +print("Get hashes for {}", large_image_path) +print("AHash: {}",lib.ext_get_ahash(large_image_path)) +print("DHash: {}",lib.ext_get_dhash(large_image_path)) +print("PHash: {}",lib.ext_get_phash(large_image_path)) + +print("Get hashes for {}", medium_image_path) +print("AHash: {}",lib.ext_get_ahash(medium_image_path)) +print("DHash: {}",lib.ext_get_dhash(medium_image_path)) +print("PHash: {}",lib.ext_get_phash(medium_image_path)) + +print("Get hashes for {}", small_image_path) +print("AHash: {}",lib.ext_get_ahash(small_image_path)) +print("DHash: {}",lib.ext_get_dhash(small_image_path)) +print("PHash: {}",lib.ext_get_phash(small_image_path)) + +# Do cleanup +#lib.teardown() diff --git a/FFI-tests/ffi_test_build.py b/FFI-tests/ffi_test_build.py new file mode 100755 index 0000000..f027cfd --- /dev/null +++ b/FFI-tests/ffi_test_build.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# A simple test script to confirm the C FFI for the rust library is working + +from cffi import FFI +ffi = FFI() + +ffi.set_source("_ffi_test_py", + """ + #include + """, + libraries=["pihash"], + library_dirs=["."] + ) + +ffi.cdef(""" + + void init(); + void teardown(); + uint64_t ext_get_ahash(const char *); + uint64_t ext_get_dhash(const char *); + uint64_t ext_get_phash(const char *); +""") + +if __name__ == "__main__": + ffi.compile() diff --git a/FFI-tests/run_ffi_test_python.sh b/FFI-tests/run_ffi_test_python.sh new file mode 100755 index 0000000..f88036b --- /dev/null +++ b/FFI-tests/run_ffi_test_python.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +LD_LIBRARY_PATH="$PWD/:$LD_LIBRARY_PATH" python3 ./ffi_test.py diff --git a/FFI-tests/test_images b/FFI-tests/test_images new file mode 120000 index 0000000..5d6c29c --- /dev/null +++ b/FFI-tests/test_images @@ -0,0 +1 @@ +../test_images \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index bd2fdb0..673251c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ mod hash; mod cache; +extern crate libc; + use std::path::Path; use hash::PerceptualHash; use std::ffi::CStr; @@ -16,7 +18,7 @@ use std::ffi::CStr; * Not performing this step may cause parts to fail. */ #[no_mangle] -pub extern fn init() { +pub extern "C" fn init() { match cache::prep_cache() { Ok(_) => {} Err(e) => println!("Error: {}", e), @@ -27,7 +29,7 @@ pub extern fn init() { * Teardown for the library */ #[no_mangle] -pub extern fn teardown() { +pub extern "C" fn teardown() { match cache::clear_cache() { Ok(_) => {} Err(e) => println!("Error: {}", e), @@ -38,44 +40,15 @@ pub fn get_phashes(path: &Path) -> hash::PerceptualHashes { hash::get_perceptual_hashes(path, &hash::Precision::Medium) } -#[no_mangle] -pub extern fn ext_get_ahash(path_str: &CStr) -> u64 { - let image_path = match path_str.to_str() { - Ok(result) => result, - Err(e) => "", - }; - let path = Path::new(&image_path); - get_ahash(&path) -} pub fn get_ahash(path: &Path) -> u64 { hash::AHash::new(&path, &hash::Precision::Medium).get_hash() } -#[no_mangle] -pub extern fn ext_get_dhash(path_str: &CStr) -> u64 { - let image_path = match path_str.to_str() { - Ok(result) => result, - Err(e) => "", - }; - let path = Path::new(&image_path); - get_dhash(&path) -} - pub fn get_dhash(path: &Path) -> u64 { hash::DHash::new(&path, &hash::Precision::Medium).get_hash() } -#[no_mangle] -pub extern fn ext_get_phash(path_str: &CStr) -> u64 { - let image_path = match path_str.to_str() { - Ok(result) => result, - Err(e) => "", - }; - let path = Path::new(&image_path); - get_phash(&path) -} - pub fn get_phash(path: &Path) -> u64 { hash::PHash::new(&path, &hash::Precision::Medium).get_hash() } @@ -84,6 +57,72 @@ pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 { hash::calculate_hamming_distance(hash1, hash2) } +// External proxies for the get_*hash methods + +#[no_mangle] +pub extern "C" fn ext_get_ahash(path_char: *const libc::c_char) -> u64 { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + get_ahash(&path) + } +} + +#[no_mangle] +pub extern "C" fn ext_get_dhash(path_char: *const libc::c_char) -> u64 { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + get_dhash(&path) + } +} + +#[no_mangle] +pub extern "C" fn ext_get_phash(path_char: *const libc::c_char) -> u64 { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + get_phash(&path) + } +} + +fn to_hex_string(bytes: &[u8]) -> String { + println!("length: {}", bytes.len()); + let mut strs: Vec = Vec::new(); + for byte in bytes { + // println!("{:02x}", byte); + strs.push(format!("{:02x}", byte)); + } + strs.join("\\x") +} + // Module for the tests // #[cfg(test)]