#include <Python.h>
#include <float.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
{% if module.uses_owned_buffer_returns() %}
typedef struct FfiBuf_u8 { uint8_t* ptr; size_t len; size_t cap; size_t align; } FfiBuf_u8;
typedef void (*boltffi_python_free_buf_fn)(FfiBuf_u8);
static boltffi_python_free_buf_fn boltffi_python_free_buf_symbol = NULL;
{% endif %}
{% if module.uses_records() %}
{% for record in module.records %}
static PyObject *{{ record.type_ref.type_object_name() }} = NULL;
{% endfor %}
{% endif %}
{% if module.uses_c_style_enums() %}
typedef struct boltffi_python_c_style_enum_registration {
PyObject *type_object;
Py_ssize_t member_count;
PyObject **members_by_wire_tag;
} boltffi_python_c_style_enum_registration;
{% for enumeration in module.enums %}
static PyObject *{{ enumeration.type_ref.member_cache_name() }}[{{ enumeration.variants.len() }}] = {NULL};
static const char *{{ enumeration.type_ref.member_name_table_name() }}[{{ enumeration.variants.len() }}] = {
{% for variant in enumeration.variants %}
"{{ variant.member_name }}"{% if !loop.last %}, {% endif %}
{% endfor %}
};
static const {{ enumeration.type_ref.tag_type.c_type_name() }} {{ enumeration.type_ref.member_native_value_table_name() }}[{{ enumeration.variants.len() }}] = {
{% for variant in enumeration.variants %}
{{ variant.native_c_literal }}{% if !loop.last %}, {% endif %}
{% endfor %}
};
static boltffi_python_c_style_enum_registration {{ enumeration.type_ref.registration_name() }} = {
NULL,
{{ enumeration.variants.len() }},
{{ enumeration.type_ref.member_cache_name() }},
};
{% endfor %}
{% endif %}
{% if module.uses_records() %}
{% for record in module.records %}
typedef struct {
{% for field in record.fields.iter() %}
{{ field.primitive.c_type_name() }} {{ field.native_name }};
{% endfor %}
} {{ record.type_ref.c_type_name }};
{% endfor %}
{% endif %}
{% for binding in module.native_callables() %}
typedef {{ binding.callable.return_type.c_type_name() }} (*{{ binding.callable.function_pointer_typedef_name() }})({% if binding.callable.takes_no_parameters() %}void{% else %}{% for ffi_argument in binding.callable.ffi_arguments() %}{{ ffi_argument.c_type_name }}{% if !loop.last %}, {% endif %}{% endfor %}{% endif %});
static {{ binding.callable.function_pointer_typedef_name() }} {{ binding.callable.function_pointer_name() }} = NULL;
{% endfor %}
{% if module.has_native_callables() %}
#ifdef _WIN32
static HMODULE boltffi_python_library_handle = NULL;
#else
static void *boltffi_python_library_handle = NULL;
#endif
static void boltffi_python_unload_library(void) {
{% for binding in module.native_callables() %}
{{ binding.callable.function_pointer_name() }} = NULL;
{% endfor %}
{% if module.uses_owned_buffer_returns() %}
boltffi_python_free_buf_symbol = NULL;
{% endif %}
if (boltffi_python_library_handle == NULL) {
return;
}
#ifdef _WIN32
FreeLibrary(boltffi_python_library_handle);
#else
dlclose(boltffi_python_library_handle);
#endif
boltffi_python_library_handle = NULL;
}
static int boltffi_python_load_library(PyObject *library_path) {
#ifdef _WIN32
wchar_t *wide_library_path = NULL;
#else
const char *utf8_library_path = NULL;
const char *loader_error = NULL;
#endif
if (!PyUnicode_Check(library_path)) {
PyErr_SetString(PyExc_TypeError, "expected str library path");
return 0;
}
#ifdef _WIN32
wide_library_path = PyUnicode_AsWideCharString(library_path, NULL);
if (wide_library_path == NULL) {
return 0;
}
boltffi_python_library_handle = LoadLibraryW(wide_library_path);
PyMem_Free(wide_library_path);
if (boltffi_python_library_handle == NULL) {
PyErr_Format(PyExc_ImportError, "failed to load native library from %S", library_path);
return 0;
}
#else
utf8_library_path = PyUnicode_AsUTF8(library_path);
if (utf8_library_path == NULL) {
return 0;
}
dlerror();
boltffi_python_library_handle = dlopen(utf8_library_path, RTLD_NOW | RTLD_LOCAL);
if (boltffi_python_library_handle == NULL) {
loader_error = dlerror();
if (loader_error == NULL) {
PyErr_Format(PyExc_ImportError, "failed to load native library from %S", library_path);
} else {
PyErr_Format(PyExc_ImportError, "failed to load native library from %S: %s", library_path, loader_error);
}
return 0;
}
#endif
return 1;
}
static int boltffi_python_bind_symbols(void) {
{% if module.uses_owned_buffer_returns() %}
#ifdef _WIN32
boltffi_python_free_buf_symbol = (boltffi_python_free_buf_fn)GetProcAddress(boltffi_python_library_handle, "{{ module.free_buffer_symbol }}");
#else
boltffi_python_free_buf_symbol = (boltffi_python_free_buf_fn)dlsym(boltffi_python_library_handle, "{{ module.free_buffer_symbol }}");
#endif
if (boltffi_python_free_buf_symbol == NULL) {
boltffi_python_unload_library();
PyErr_SetString(PyExc_ImportError, "failed to resolve native symbol {{ module.free_buffer_symbol }}");
return 0;
}
{% endif %}
{% for binding in module.native_callables() %}
#ifdef _WIN32
{{ binding.callable.function_pointer_name() }} = ({{ binding.callable.function_pointer_typedef_name() }})GetProcAddress(boltffi_python_library_handle, "{{ binding.callable.ffi_symbol }}");
#else
{{ binding.callable.function_pointer_name() }} = ({{ binding.callable.function_pointer_typedef_name() }})dlsym(boltffi_python_library_handle, "{{ binding.callable.ffi_symbol }}");
#endif
if ({{ binding.callable.function_pointer_name() }} == NULL) {
boltffi_python_unload_library();
PyErr_SetString(PyExc_ImportError, "failed to resolve native symbol {{ binding.callable.ffi_symbol }}");
return 0;
}
{% endfor %}
return 1;
}
static PyObject *boltffi_python_initialize_loader(PyObject *self, PyObject *library_path) {
(void)self;
if (boltffi_python_library_handle != NULL) {
Py_RETURN_NONE;
}
if (!boltffi_python_load_library(library_path)) {
return NULL;
}
if (!boltffi_python_bind_symbols()) {
return NULL;
}
Py_RETURN_NONE;
}
{% endif %}
{% if module.uses_string_parameters() %}
typedef struct boltffi_python_utf8_input {
const uint8_t *ptr;
uintptr_t len;
} boltffi_python_utf8_input;
static int boltffi_python_parse_string(PyObject *value, boltffi_python_utf8_input *out) {
const char *utf8_data = NULL;
Py_ssize_t utf8_length = 0;
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError, "expected str");
return 0;
}
utf8_data = PyUnicode_AsUTF8AndSize(value, &utf8_length);
if (utf8_data == NULL) {
return 0;
}
out->ptr = (const uint8_t *)utf8_data;
out->len = (uintptr_t)utf8_length;
return 1;
}
{% endif %}
{% if module.uses_buffer_input_parameters() %}
typedef struct boltffi_python_buffer_input {
const void *ptr;
uintptr_t len;
Py_buffer view;
void *owned_copy;
int has_view;
} boltffi_python_buffer_input;
static void boltffi_python_init_buffer_input(boltffi_python_buffer_input *input) {
input->ptr = NULL;
input->len = 0;
input->owned_copy = NULL;
input->has_view = 0;
memset(&input->view, 0, sizeof(Py_buffer));
}
static void boltffi_python_release_buffer_input(boltffi_python_buffer_input *input) {
if (input->has_view) {
PyBuffer_Release(&input->view);
}
if (input->owned_copy != NULL) {
PyMem_Free(input->owned_copy);
}
boltffi_python_init_buffer_input(input);
}
static const char *boltffi_python_skip_native_buffer_format_prefix(const char *format) {
if (format == NULL) {
return NULL;
}
while (*format == '@' || *format == '=') {
format += 1;
}
return format;
}
static int boltffi_python_try_parse_bytes_like_buffer(
PyObject *value,
boltffi_python_buffer_input *out
) {
if (PyObject_GetBuffer(value, &out->view, PyBUF_CONTIG_RO) != 0) {
return 0;
}
if (out->view.ndim != 1 || out->view.itemsize != 1) {
PyBuffer_Release(&out->view);
memset(&out->view, 0, sizeof(Py_buffer));
return 0;
}
out->ptr = out->view.buf;
out->len = (uintptr_t)out->view.len;
out->has_view = 1;
return 1;
}
static int boltffi_python_parse_bytes(PyObject *value, boltffi_python_buffer_input *out) {
boltffi_python_init_buffer_input(out);
if (!boltffi_python_try_parse_bytes_like_buffer(value, out)) {
PyErr_Clear();
PyErr_SetString(PyExc_TypeError, "expected bytes-like object");
return 0;
}
return 1;
}
{% endif %}
{% if !used_primitive_types.is_empty() %}
{% for primitive in used_primitive_types %}
static int {{ primitive.parser_name() }}(PyObject *value, {{ primitive.c_type_name() }} *out) {
{% if primitive.uses_bool_parser() %}
if (!PyBool_Check(value)) {
PyErr_SetString(PyExc_TypeError, "expected bool");
return 0;
}
*out = value == Py_True;
return 1;
{% elif primitive.uses_signed_long_long_parser() %}
long long parsed = PyLong_AsLongLong(value);
if (parsed == -1 && PyErr_Occurred()) {
return 0;
}
if (parsed < {{ primitive.signed_min_macro() }} || parsed > {{ primitive.signed_max_macro() }}) {
PyErr_SetString(PyExc_OverflowError, "{{ primitive.rust_name() }} out of range");
return 0;
}
*out = ({{ primitive.c_type_name() }})parsed;
return 1;
{% elif primitive.uses_unsigned_long_long_with_range_check() %}
unsigned long long parsed = PyLong_AsUnsignedLongLong(value);
if (PyErr_Occurred()) {
return 0;
}
if (parsed > {{ primitive.unsigned_max_macro() }}) {
PyErr_SetString(PyExc_OverflowError, "{{ primitive.rust_name() }} out of range");
return 0;
}
*out = ({{ primitive.c_type_name() }})parsed;
return 1;
{% elif primitive.uses_u64_parser() %}
unsigned long long parsed = PyLong_AsUnsignedLongLong(value);
if (PyErr_Occurred()) {
return 0;
}
*out = (uint64_t)parsed;
return 1;
{% elif primitive.uses_isize_parser() %}
Py_ssize_t parsed = PyLong_AsSsize_t(value);
if (parsed == -1 && PyErr_Occurred()) {
return 0;
}
*out = (intptr_t)parsed;
return 1;
{% elif primitive.uses_usize_parser() %}
size_t parsed = PyLong_AsSize_t(value);
if (parsed == (size_t)-1 && PyErr_Occurred()) {
return 0;
}
*out = (uintptr_t)parsed;
return 1;
{% elif primitive.uses_f32_parser() %}
double parsed = PyFloat_AsDouble(value);
if (parsed == -1.0 && PyErr_Occurred()) {
return 0;
}
if (parsed < -(double)FLT_MAX || parsed > (double)FLT_MAX) {
PyErr_SetString(PyExc_OverflowError, "f32 out of range");
return 0;
}
*out = (float)parsed;
return 1;
{% elif primitive.uses_f64_parser() %}
double parsed = PyFloat_AsDouble(value);
if (parsed == -1.0 && PyErr_Occurred()) {
return 0;
}
*out = parsed;
return 1;
{% endif %}
}
static PyObject *{{ primitive.boxer_name() }}({{ primitive.c_type_name() }} value) {
{% if primitive.boxes_as_bool() %}
return PyBool_FromLong(value ? 1 : 0);
{% elif primitive.boxes_as_signed_long() %}
return PyLong_FromLong((long)value);
{% elif primitive.boxes_as_unsigned_long() %}
return PyLong_FromUnsignedLong((unsigned long)value);
{% elif primitive.boxes_as_signed_long_long() %}
return PyLong_FromLongLong((long long)value);
{% elif primitive.boxes_as_unsigned_long_long() %}
return PyLong_FromUnsignedLongLong((unsigned long long)value);
{% elif primitive.boxes_as_ssize() %}
return PyLong_FromSsize_t((Py_ssize_t)value);
{% elif primitive.boxes_as_size() %}
return PyLong_FromSize_t((size_t)value);
{% elif primitive.boxes_as_double() %}
return PyFloat_FromDouble((double)value);
{% endif %}
}
{% endfor %}
{% endif %}
{% if !module.used_primitive_vector_parameter_types().is_empty() %}
typedef enum boltffi_python_buffer_kind {
BOLTFFI_PY_BUFFER_UNKNOWN = 0,
BOLTFFI_PY_BUFFER_BOOL,
BOLTFFI_PY_BUFFER_I8,
BOLTFFI_PY_BUFFER_U8,
BOLTFFI_PY_BUFFER_I16,
BOLTFFI_PY_BUFFER_U16,
BOLTFFI_PY_BUFFER_I32,
BOLTFFI_PY_BUFFER_U32,
BOLTFFI_PY_BUFFER_I64,
BOLTFFI_PY_BUFFER_U64,
BOLTFFI_PY_BUFFER_ISIZE,
BOLTFFI_PY_BUFFER_USIZE,
BOLTFFI_PY_BUFFER_F32,
BOLTFFI_PY_BUFFER_F64
} boltffi_python_buffer_kind;
static Py_ssize_t boltffi_python_buffer_kind_item_size(boltffi_python_buffer_kind kind) {
switch (kind) {
case BOLTFFI_PY_BUFFER_BOOL:
return (Py_ssize_t)sizeof(bool);
case BOLTFFI_PY_BUFFER_I8:
return (Py_ssize_t)sizeof(int8_t);
case BOLTFFI_PY_BUFFER_U8:
return (Py_ssize_t)sizeof(uint8_t);
case BOLTFFI_PY_BUFFER_I16:
return (Py_ssize_t)sizeof(int16_t);
case BOLTFFI_PY_BUFFER_U16:
return (Py_ssize_t)sizeof(uint16_t);
case BOLTFFI_PY_BUFFER_I32:
return (Py_ssize_t)sizeof(int32_t);
case BOLTFFI_PY_BUFFER_U32:
return (Py_ssize_t)sizeof(uint32_t);
case BOLTFFI_PY_BUFFER_I64:
return (Py_ssize_t)sizeof(int64_t);
case BOLTFFI_PY_BUFFER_U64:
return (Py_ssize_t)sizeof(uint64_t);
case BOLTFFI_PY_BUFFER_ISIZE:
return (Py_ssize_t)sizeof(intptr_t);
case BOLTFFI_PY_BUFFER_USIZE:
return (Py_ssize_t)sizeof(uintptr_t);
case BOLTFFI_PY_BUFFER_F32:
return (Py_ssize_t)sizeof(float);
case BOLTFFI_PY_BUFFER_F64:
return (Py_ssize_t)sizeof(double);
case BOLTFFI_PY_BUFFER_UNKNOWN:
default:
return -1;
}
}
static boltffi_python_buffer_kind boltffi_python_parse_buffer_kind(
const char *format,
Py_ssize_t item_size
) {
const char *core = boltffi_python_skip_native_buffer_format_prefix(format);
if (core == NULL) {
return BOLTFFI_PY_BUFFER_UNKNOWN;
}
if (strcmp(core, "?") == 0 && item_size == (Py_ssize_t)sizeof(bool)) {
return BOLTFFI_PY_BUFFER_BOOL;
}
if (strcmp(core, "b") == 0 && item_size == (Py_ssize_t)sizeof(int8_t)) {
return BOLTFFI_PY_BUFFER_I8;
}
if (strcmp(core, "B") == 0 && item_size == (Py_ssize_t)sizeof(uint8_t)) {
return BOLTFFI_PY_BUFFER_U8;
}
if (strcmp(core, "h") == 0 && item_size == (Py_ssize_t)sizeof(int16_t)) {
return BOLTFFI_PY_BUFFER_I16;
}
if (strcmp(core, "H") == 0 && item_size == (Py_ssize_t)sizeof(uint16_t)) {
return BOLTFFI_PY_BUFFER_U16;
}
if (strcmp(core, "i") == 0 && item_size == (Py_ssize_t)sizeof(int32_t)) {
return BOLTFFI_PY_BUFFER_I32;
}
if (strcmp(core, "I") == 0 && item_size == (Py_ssize_t)sizeof(uint32_t)) {
return BOLTFFI_PY_BUFFER_U32;
}
if (strcmp(core, "q") == 0 && item_size == (Py_ssize_t)sizeof(int64_t)) {
return BOLTFFI_PY_BUFFER_I64;
}
if (strcmp(core, "Q") == 0 && item_size == (Py_ssize_t)sizeof(uint64_t)) {
return BOLTFFI_PY_BUFFER_U64;
}
if (strcmp(core, "n") == 0 && item_size == (Py_ssize_t)sizeof(intptr_t)) {
return BOLTFFI_PY_BUFFER_ISIZE;
}
if (strcmp(core, "N") == 0 && item_size == (Py_ssize_t)sizeof(uintptr_t)) {
return BOLTFFI_PY_BUFFER_USIZE;
}
if (strcmp(core, "f") == 0 && item_size == (Py_ssize_t)sizeof(float)) {
return BOLTFFI_PY_BUFFER_F32;
}
if (strcmp(core, "d") == 0 && item_size == (Py_ssize_t)sizeof(double)) {
return BOLTFFI_PY_BUFFER_F64;
}
if (strcmp(core, "l") == 0) {
if (item_size == (Py_ssize_t)sizeof(int32_t)) {
return BOLTFFI_PY_BUFFER_I32;
}
if (item_size == (Py_ssize_t)sizeof(int64_t)) {
return BOLTFFI_PY_BUFFER_I64;
}
}
if (strcmp(core, "L") == 0) {
if (item_size == (Py_ssize_t)sizeof(uint32_t)) {
return BOLTFFI_PY_BUFFER_U32;
}
if (item_size == (Py_ssize_t)sizeof(uint64_t)) {
return BOLTFFI_PY_BUFFER_U64;
}
}
return BOLTFFI_PY_BUFFER_UNKNOWN;
}
static int boltffi_python_try_parse_typed_buffer(
PyObject *value,
boltffi_python_buffer_input *out,
boltffi_python_buffer_kind expected_kind,
int allow_untyped_buffer
) {
boltffi_python_buffer_kind actual_kind = BOLTFFI_PY_BUFFER_UNKNOWN;
Py_ssize_t expected_item_size = boltffi_python_buffer_kind_item_size(expected_kind);
if (PyObject_GetBuffer(value, &out->view, PyBUF_FORMAT | PyBUF_C_CONTIGUOUS) != 0) {
return 0;
}
if (out->view.ndim != 1 || out->view.itemsize != expected_item_size) {
PyBuffer_Release(&out->view);
memset(&out->view, 0, sizeof(Py_buffer));
return 0;
}
if (out->view.format != NULL) {
actual_kind = boltffi_python_parse_buffer_kind(out->view.format, out->view.itemsize);
if (actual_kind != expected_kind) {
PyBuffer_Release(&out->view);
memset(&out->view, 0, sizeof(Py_buffer));
return 0;
}
} else if (!allow_untyped_buffer) {
PyBuffer_Release(&out->view);
memset(&out->view, 0, sizeof(Py_buffer));
return 0;
}
out->ptr = out->view.buf;
out->len = (uintptr_t)((size_t)out->view.len / (size_t)out->view.itemsize);
out->has_view = 1;
return 1;
}
{% for primitive in module.used_primitive_vector_parameter_types() %}
static int {{ primitive.vector_parser_name() }}(PyObject *value, boltffi_python_buffer_input *out) {
PyObject *sequence = NULL;
Py_ssize_t sequence_length = 0;
Py_ssize_t index = 0;
{{ primitive.c_type_name() }} *owned_copy = NULL;
boltffi_python_init_buffer_input(out);
if (boltffi_python_try_parse_typed_buffer(
value,
out,
{{ primitive.buffer_kind_constant() }},
{% if primitive.allows_untyped_vector_buffer() %}1{% else %}0{% endif %}
)) {
return 1;
}
PyErr_Clear();
sequence = PySequence_Fast(value, "expected sequence");
if (sequence == NULL) {
return 0;
}
sequence_length = PySequence_Fast_GET_SIZE(sequence);
if ((size_t)sequence_length > (size_t)UINTPTR_MAX) {
Py_DECREF(sequence);
PyErr_SetString(PyExc_OverflowError, "sequence is too large for Rust");
return 0;
}
if (sequence_length > 0) {
owned_copy = PyMem_Malloc((size_t)sequence_length * sizeof({{ primitive.c_type_name() }}));
if (owned_copy == NULL) {
Py_DECREF(sequence);
PyErr_NoMemory();
return 0;
}
}
for (index = 0; index < sequence_length; index += 1) {
if (!{{ primitive.parser_name() }}(PySequence_Fast_GET_ITEM(sequence, index), &owned_copy[index])) {
PyMem_Free(owned_copy);
Py_DECREF(sequence);
return 0;
}
}
Py_DECREF(sequence);
out->ptr = owned_copy;
out->len = (uintptr_t)sequence_length;
out->owned_copy = owned_copy;
return 1;
}
{% endfor %}
{% endif %}
{% if module.uses_registered_types() %}
static int boltffi_python_validate_registered_type_object(PyObject *type_object, const char *type_name) {
if (!PyType_Check(type_object)) {
PyErr_Format(PyExc_TypeError, "expected type for %s", type_name);
return 0;
}
return 1;
}
static int boltffi_python_store_registered_type(
PyObject **type_slot,
PyObject *type_object,
const char *type_name
) {
if (!boltffi_python_validate_registered_type_object(type_object, type_name)) {
return 0;
}
Py_INCREF(type_object);
Py_XDECREF(*type_slot);
*type_slot = type_object;
return 1;
}
static int boltffi_python_expect_registered_type(PyObject *type_object, const char *type_name) {
if (type_object != NULL) {
return 1;
}
PyErr_Format(PyExc_ImportError, "native type %s is not registered", type_name);
return 0;
}
static int boltffi_python_expect_type_instance(
PyObject *value,
PyObject *type_object,
const char *type_name
) {
int is_instance = 0;
if (!boltffi_python_expect_registered_type(type_object, type_name)) {
return 0;
}
is_instance = PyObject_IsInstance(value, type_object);
if (is_instance < 0) {
return 0;
}
if (is_instance == 0) {
PyErr_Format(PyExc_TypeError, "expected %s", type_name);
return 0;
}
return 1;
}
{% if module.uses_c_style_enums() %}
typedef PyObject *(*boltffi_python_load_c_style_enum_member_fn)(PyObject *, Py_ssize_t);
static void boltffi_python_release_registered_enum_members(PyObject **members_by_wire_tag, Py_ssize_t member_count) {
Py_ssize_t member_index = 0;
for (member_index = 0; member_index < member_count; member_index += 1) {
Py_XDECREF(members_by_wire_tag[member_index]);
members_by_wire_tag[member_index] = NULL;
}
}
static void boltffi_python_clear_c_style_enum_registration(
boltffi_python_c_style_enum_registration *registration
) {
Py_XDECREF(registration->type_object);
registration->type_object = NULL;
boltffi_python_release_registered_enum_members(
registration->members_by_wire_tag,
registration->member_count
);
}
static PyObject *boltffi_python_load_c_style_enum_member(
PyObject *type_object,
const char *enum_name,
const char *member_name,
PyObject *native_value
) {
PyObject *named_member = NULL;
PyObject *resolved_member = NULL;
if (native_value == NULL) {
return NULL;
}
named_member = PyObject_GetAttrString(type_object, member_name);
if (named_member == NULL) {
return NULL;
}
resolved_member = PyObject_CallOneArg(type_object, native_value);
if (resolved_member == NULL) {
Py_DECREF(named_member);
return NULL;
}
if (named_member != resolved_member) {
PyErr_Format(PyExc_ValueError, "native enum %s member %s has the wrong value", enum_name, member_name);
Py_DECREF(named_member);
Py_DECREF(resolved_member);
return NULL;
}
Py_DECREF(resolved_member);
return named_member;
}
static int boltffi_python_store_c_style_enum_registration(
boltffi_python_c_style_enum_registration *registration,
PyObject *type_object,
const char *enum_name,
boltffi_python_load_c_style_enum_member_fn load_member
) {
PyObject **loaded_members = NULL;
Py_ssize_t member_index = 0;
if (!boltffi_python_validate_registered_type_object(type_object, enum_name)) {
return 0;
}
loaded_members = PyMem_Calloc((size_t)registration->member_count, sizeof(PyObject *));
if (loaded_members == NULL) {
PyErr_NoMemory();
return 0;
}
for (member_index = 0; member_index < registration->member_count; member_index += 1) {
loaded_members[member_index] = load_member(type_object, member_index);
if (loaded_members[member_index] == NULL) {
boltffi_python_release_registered_enum_members(loaded_members, registration->member_count);
PyMem_Free(loaded_members);
return 0;
}
}
boltffi_python_clear_c_style_enum_registration(registration);
if (!boltffi_python_store_registered_type(®istration->type_object, type_object, enum_name)) {
boltffi_python_release_registered_enum_members(loaded_members, registration->member_count);
PyMem_Free(loaded_members);
return 0;
}
for (member_index = 0; member_index < registration->member_count; member_index += 1) {
registration->members_by_wire_tag[member_index] = loaded_members[member_index];
}
PyMem_Free(loaded_members);
return 1;
}
static int boltffi_python_expect_enum_instance(
PyObject *value,
const boltffi_python_c_style_enum_registration *registration,
const char *enum_name
) {
return boltffi_python_expect_type_instance(value, registration->type_object, enum_name);
}
{% endif %}
{% endif %}
{% if module.uses_c_style_enums() %}
static PyObject *boltffi_python_box_registered_enum_member(
const boltffi_python_c_style_enum_registration *registration,
Py_ssize_t member_index,
const char *enum_name
) {
PyObject *member = NULL;
if (!boltffi_python_expect_registered_type(registration->type_object, enum_name)) {
return NULL;
}
if (member_index < 0 || member_index >= registration->member_count) {
PyErr_SetString(PyExc_RuntimeError, "native enum member index is invalid");
return NULL;
}
if (registration->members_by_wire_tag[member_index] == NULL) {
PyErr_Format(PyExc_ImportError, "native enum %s member cache is not initialized", enum_name);
return NULL;
}
member = registration->members_by_wire_tag[member_index];
Py_INCREF(member);
return member;
}
{% for enumeration in module.enums %}
static PyObject *{{ enumeration.type_ref.member_loader_name() }}(PyObject *type_object, Py_ssize_t member_index) {
PyObject *native_value = NULL;
PyObject *member = NULL;
native_value = {{ enumeration.type_ref.tag_type.boxer_name() }}({{ enumeration.type_ref.member_native_value_table_name() }}[member_index]);
member = boltffi_python_load_c_style_enum_member(
type_object,
"{{ enumeration.class_name() }}",
{{ enumeration.type_ref.member_name_table_name() }}[member_index],
native_value
);
Py_XDECREF(native_value);
return member;
}
static PyObject *{{ enumeration.type_ref.registration_wrapper_name() }}(PyObject *self, PyObject *const *args, Py_ssize_t nargs) {
(void)self;
if (nargs != 1) {
PyErr_Format(PyExc_TypeError, "{{ enumeration.type_ref.registration_function_name() }}() takes 1 positional argument but %zd were given", nargs);
return NULL;
}
if (!boltffi_python_store_c_style_enum_registration(
&{{ enumeration.type_ref.registration_name() }},
args[0],
"{{ enumeration.class_name() }}",
{{ enumeration.type_ref.member_loader_name() }}
)) {
return NULL;
}
Py_RETURN_NONE;
}
static int {{ enumeration.type_ref.parser_name() }}(PyObject *value, {{ enumeration.type_ref.tag_type.c_type_name() }} *out) {
if (!boltffi_python_expect_enum_instance(value, &{{ enumeration.type_ref.registration_name() }}, "{{ enumeration.class_name() }}")) {
return 0;
}
return {{ enumeration.type_ref.tag_type.parser_name() }}(value, out);
}
static int {{ enumeration.type_ref.native_to_wire_tag_name() }}({{ enumeration.type_ref.tag_type.c_type_name() }} value, int32_t *out) {
switch (value) {
{% for variant in enumeration.variants %}
case {{ variant.native_c_literal }}:
*out = {{ variant.wire_c_literal }};
return 1;
{% endfor %}
default:
PyErr_SetString(PyExc_ValueError, "invalid {{ enumeration.class_name() }} value");
return 0;
}
}
static PyObject *{{ enumeration.type_ref.box_from_wire_tag_name() }}(int32_t wire_tag) {
switch (wire_tag) {
{% for variant in enumeration.variants %}
case {{ variant.wire_c_literal }}:
return boltffi_python_box_registered_enum_member(
&{{ enumeration.type_ref.registration_name() }},
{{ loop.index0 }},
"{{ enumeration.class_name() }}"
);
{% endfor %}
default:
PyErr_SetString(PyExc_RuntimeError, "native enum wire tag is invalid");
return NULL;
}
}
static PyObject *{{ enumeration.type_ref.boxer_name() }}({{ enumeration.type_ref.tag_type.c_type_name() }} value) {
int32_t wire_tag = 0;
if (!{{ enumeration.type_ref.native_to_wire_tag_name() }}(value, &wire_tag)) {
return NULL;
}
return {{ enumeration.type_ref.box_from_wire_tag_name() }}(wire_tag);
}
{% endfor %}
{% endif %}
{% if module.uses_records() %}
static PyObject *boltffi_python_get_record_field(
PyObject *value,
const char *record_name,
const char *field_name
) {
PyObject *field_value = PyObject_GetAttrString(value, field_name);
if (field_value == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
PyErr_Format(PyExc_TypeError, "%s is missing field %s", record_name, field_name);
}
return field_value;
}
static PyObject *boltffi_python_box_registered_record(
PyObject *type_object,
PyObject *constructor_args,
const char *record_name
) {
PyObject *record_value = NULL;
if (constructor_args == NULL) {
return NULL;
}
if (!boltffi_python_expect_registered_type(type_object, record_name)) {
Py_DECREF(constructor_args);
return NULL;
}
record_value = PyObject_CallObject(type_object, constructor_args);
Py_DECREF(constructor_args);
return record_value;
}
{% for record in module.records %}
static PyObject *{{ record.type_ref.registration_wrapper_name() }}(PyObject *self, PyObject *const *args, Py_ssize_t nargs) {
(void)self;
if (nargs != 1) {
PyErr_Format(PyExc_TypeError, "{{ record.type_ref.registration_function_name() }}() takes 1 positional argument but %zd were given", nargs);
return NULL;
}
if (!boltffi_python_store_registered_type(
&{{ record.type_ref.type_object_name() }},
args[0],
"{{ record.class_name() }}"
)) {
return NULL;
}
Py_RETURN_NONE;
}
static int {{ record.type_ref.parser_name() }}(PyObject *value, {{ record.type_ref.c_type_name }} *out) {
{% for field in record.fields.iter() %}
PyObject *{{ field.python_name }}_value = NULL;
{% endfor %}
int parsed = 0;
if (!boltffi_python_expect_type_instance(value, {{ record.type_ref.type_object_name() }}, "{{ record.class_name() }}")) {
return 0;
}
{% for field in record.fields.iter() %}
{{ field.python_name }}_value = boltffi_python_get_record_field(value, "{{ record.class_name() }}", "{{ field.python_name }}");
if ({{ field.python_name }}_value == NULL) {
goto cleanup;
}
if (!{{ field.primitive.parser_name() }}({{ field.python_name }}_value, &out->{{ field.native_name }})) {
goto cleanup;
}
Py_DECREF({{ field.python_name }}_value);
{{ field.python_name }}_value = NULL;
{% endfor %}
parsed = 1;
cleanup:
{% for field in record.fields.iter() %}
Py_XDECREF({{ field.python_name }}_value);
{% endfor %}
return parsed;
}
static PyObject *{{ record.type_ref.boxer_name() }}({{ record.type_ref.c_type_name }} value) {
PyObject *constructor_args = NULL;
PyObject *field_value = NULL;
constructor_args = PyTuple_New({{ record.field_count() }});
if (constructor_args == NULL) {
return NULL;
}
{% for field in record.fields.iter() %}
field_value = {{ field.primitive.boxer_name() }}(value.{{ field.native_name }});
if (field_value == NULL) {
Py_DECREF(constructor_args);
return NULL;
}
PyTuple_SET_ITEM(constructor_args, {{ loop.index0 }}, field_value);
field_value = NULL;
{% endfor %}
return boltffi_python_box_registered_record(
{{ record.type_ref.type_object_name() }},
constructor_args,
"{{ record.class_name() }}"
);
}
{% endfor %}
{% endif %}
{% if !module.used_enum_vector_parameter_types().is_empty() %}
{% for enumeration in module.used_enum_vector_parameter_types() %}
static int {{ enumeration.vector_parser_name() }}(PyObject *value, boltffi_python_buffer_input *out) {
PyObject *sequence = NULL;
Py_ssize_t sequence_length = 0;
Py_ssize_t index = 0;
uint8_t *encoded_values = NULL;
{{ enumeration.tag_type.c_type_name() }} native_value = 0;
int32_t encoded_length = 0;
int32_t wire_tag = 0;
size_t encoded_size = 0;
boltffi_python_init_buffer_input(out);
sequence = PySequence_Fast(value, "expected sequence");
if (sequence == NULL) {
return 0;
}
sequence_length = PySequence_Fast_GET_SIZE(sequence);
if (sequence_length > (Py_ssize_t)INT32_MAX) {
Py_DECREF(sequence);
PyErr_SetString(PyExc_OverflowError, "sequence is too large for Rust");
return 0;
}
if ((size_t)sequence_length > ((size_t)UINTPTR_MAX - sizeof(int32_t)) / sizeof(int32_t)) {
Py_DECREF(sequence);
PyErr_SetString(PyExc_OverflowError, "sequence is too large for Rust");
return 0;
}
encoded_size = sizeof(int32_t) + ((size_t)sequence_length * sizeof(int32_t));
encoded_values = PyMem_Malloc(encoded_size);
if (encoded_values == NULL) {
Py_DECREF(sequence);
PyErr_NoMemory();
return 0;
}
encoded_length = (int32_t)sequence_length;
memcpy(encoded_values, &encoded_length, sizeof(int32_t));
for (index = 0; index < sequence_length; index += 1) {
if (!{{ enumeration.parser_name() }}(PySequence_Fast_GET_ITEM(sequence, index), &native_value)) {
PyMem_Free(encoded_values);
Py_DECREF(sequence);
return 0;
}
if (!{{ enumeration.native_to_wire_tag_name() }}(native_value, &wire_tag)) {
PyMem_Free(encoded_values);
Py_DECREF(sequence);
return 0;
}
memcpy(
encoded_values + sizeof(int32_t) + ((size_t)index * sizeof(int32_t)),
&wire_tag,
sizeof(int32_t)
);
}
Py_DECREF(sequence);
out->ptr = encoded_values;
/* Encoded enum vectors cross the ABI as a byte buffer, so len is the encoded byte count here. */
out->len = (uintptr_t)encoded_size;
out->owned_copy = encoded_values;
return 1;
}
{% endfor %}
{% endif %}
{% if module.uses_owned_buffer_returns() %}
static int boltffi_python_validate_owned_buffer(FfiBuf_u8 buffer) {
if (buffer.ptr == NULL && buffer.len != 0) {
PyErr_SetString(PyExc_RuntimeError, "native buffer is invalid");
return 0;
}
return 1;
}
{% endif %}
{% if module.uses_string_returns() %}
static PyObject *boltffi_python_decode_owned_utf8(FfiBuf_u8 buffer) {
PyObject *decoded = NULL;
uint32_t utf8_length = 0;
const char *utf8_data = "";
if (!boltffi_python_validate_owned_buffer(buffer)) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
if (buffer.len < sizeof(uint32_t)) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native string buffer is invalid");
return NULL;
}
memcpy(&utf8_length, buffer.ptr, sizeof(uint32_t));
if (buffer.len != sizeof(uint32_t) + (size_t)utf8_length) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native string buffer is invalid");
return NULL;
}
if ((size_t)utf8_length > (size_t)PY_SSIZE_T_MAX) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_OverflowError, "string result is too large for Python");
return NULL;
}
if (utf8_length != 0) {
utf8_data = (const char *)(buffer.ptr + sizeof(uint32_t));
}
decoded = PyUnicode_DecodeUTF8(utf8_data, (Py_ssize_t)utf8_length, NULL);
boltffi_python_free_buf_symbol(buffer);
return decoded;
}
{% endif %}
{% if module.uses_bytes_returns() %}
static PyObject *boltffi_python_decode_owned_bytes(FfiBuf_u8 buffer) {
PyObject *decoded = NULL;
const char *byte_data = "";
if (!boltffi_python_validate_owned_buffer(buffer)) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
if ((size_t)buffer.len > (size_t)PY_SSIZE_T_MAX) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_OverflowError, "byte result is too large for Python");
return NULL;
}
if (buffer.len != 0) {
byte_data = (const char *)buffer.ptr;
}
decoded = PyBytes_FromStringAndSize(byte_data, (Py_ssize_t)buffer.len);
boltffi_python_free_buf_symbol(buffer);
return decoded;
}
{% endif %}
{% for primitive in module.used_primitive_vector_return_types() %}
static PyObject *boltffi_python_decode_owned_vec_{{ primitive.rust_name() }}(FfiBuf_u8 buffer) {
PyObject *decoded = NULL;
PyObject *item = NULL;
Py_ssize_t item_count = 0;
Py_ssize_t index = 0;
const {{ primitive.c_type_name() }} *values = NULL;
if (!boltffi_python_validate_owned_buffer(buffer)) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
if (buffer.len % sizeof({{ primitive.c_type_name() }}) != 0) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native vector buffer is invalid");
return NULL;
}
if (buffer.len / sizeof({{ primitive.c_type_name() }}) > (size_t)PY_SSIZE_T_MAX) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_OverflowError, "vector result is too large for Python");
return NULL;
}
item_count = (Py_ssize_t)(buffer.len / sizeof({{ primitive.c_type_name() }}));
decoded = PyList_New(item_count);
if (decoded == NULL) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
values = (const {{ primitive.c_type_name() }} *)buffer.ptr;
for (index = 0; index < item_count; index += 1) {
item = {{ primitive.boxer_name() }}(values[index]);
if (item == NULL) {
Py_DECREF(decoded);
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
PyList_SET_ITEM(decoded, index, item);
}
boltffi_python_free_buf_symbol(buffer);
return decoded;
}
{% endfor %}
{% for enumeration in module.used_enum_vector_return_types() %}
static PyObject *{{ enumeration.vector_decoder_name() }}(FfiBuf_u8 buffer) {
PyObject *decoded = NULL;
PyObject *item = NULL;
Py_ssize_t item_count = 0;
Py_ssize_t index = 0;
const uint8_t *encoded_values = NULL;
int32_t encoded_length = 0;
int32_t wire_tag = 0;
size_t expected_size = 0;
if (!boltffi_python_validate_owned_buffer(buffer)) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
if (buffer.len < sizeof(int32_t)) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native vector buffer is invalid");
return NULL;
}
memcpy(&encoded_length, buffer.ptr, sizeof(int32_t));
if (encoded_length < 0) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native vector buffer is invalid");
return NULL;
}
if ((size_t)encoded_length > ((size_t)SIZE_MAX - sizeof(int32_t)) / sizeof(int32_t)) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native vector buffer is invalid");
return NULL;
}
expected_size = sizeof(int32_t) + ((size_t)encoded_length * sizeof(int32_t));
if (buffer.len != expected_size) {
boltffi_python_free_buf_symbol(buffer);
PyErr_SetString(PyExc_RuntimeError, "native vector buffer is invalid");
return NULL;
}
item_count = (Py_ssize_t)encoded_length;
decoded = PyList_New(item_count);
if (decoded == NULL) {
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
encoded_values = buffer.ptr;
for (index = 0; index < item_count; index += 1) {
memcpy(
&wire_tag,
encoded_values + sizeof(int32_t) + ((size_t)index * sizeof(int32_t)),
sizeof(int32_t)
);
item = {{ enumeration.box_from_wire_tag_name() }}(wire_tag);
if (item == NULL) {
Py_DECREF(decoded);
boltffi_python_free_buf_symbol(buffer);
return NULL;
}
PyList_SET_ITEM(decoded, index, item);
}
boltffi_python_free_buf_symbol(buffer);
return decoded;
}
{% endfor %}
{% for binding in module.native_callables() %}
static PyObject *{{ binding.callable.wrapper_name() }}(PyObject *self, PyObject *const *args, Py_ssize_t nargs) {
{% for parameter in binding.callable.parameters %}
{% for local_binding in parameter.local_bindings() %}
{{ local_binding.c_type_name }} {{ local_binding.name }};
{% endfor %}
{% endfor %}
{% if !binding.callable.returns_void() %}
{{ binding.callable.return_type.c_type_name() }} result;
{% endif %}
PyObject *python_result = NULL;
(void)self;
if (nargs != {{ binding.callable.parameter_count() }}) {
PyErr_Format(PyExc_TypeError, "{{ binding.module_attribute_name }}() takes {{ binding.callable.parameter_count() }} positional arguments but %zd were given", nargs);
return NULL;
}
if ({{ binding.callable.function_pointer_name() }} == NULL) {
PyErr_SetString(PyExc_ImportError, "native library is not initialized");
return NULL;
}
{% for parameter in binding.callable.parameters %}
{% if parameter.uses_buffer_input() %}
boltffi_python_init_buffer_input(&{{ parameter.parser_state_name() }});
{% endif %}
if (!{{ parameter.parser_name() }}(args[{{ loop.index0 }}], {% for parser_output in parameter.parser_output_arguments() %}{{ parser_output }}{% if !loop.last %}, {% endif %}{% endfor %})) {
goto cleanup;
}
{% endfor %}
{% if binding.callable.returns_void() %}
{{ binding.callable.function_pointer_name() }}({% for ffi_argument in binding.callable.call_argument_expressions() %}{{ ffi_argument }}{% if !loop.last %}, {% endif %}{% endfor %});
Py_INCREF(Py_None);
python_result = Py_None;
{% else %}
result = {{ binding.callable.function_pointer_name() }}({% for ffi_argument in binding.callable.call_argument_expressions() %}{{ ffi_argument }}{% if !loop.last %}, {% endif %}{% endfor %});
{% if binding.callable.returns_string() %}
python_result = boltffi_python_decode_owned_utf8(result);
{% elif binding.callable.returns_record() %}
{% let return_record = binding.callable.return_record().unwrap() %}
python_result = {{ return_record.boxer_name() }}(result);
{% elif binding.callable.returns_c_style_enum() %}
{% let return_enum = binding.callable.return_c_style_enum().unwrap() %}
python_result = {{ return_enum.boxer_name() }}(result);
{% elif binding.callable.returns_bytes() %}
python_result = boltffi_python_decode_owned_bytes(result);
{% elif binding.callable.returns_primitive_vector() %}
{% let return_primitive = binding.callable.return_vector_primitive().unwrap() %}
python_result = boltffi_python_decode_owned_vec_{{ return_primitive.rust_name() }}(result);
{% elif binding.callable.returns_c_style_enum_vector() %}
{% let return_enum = binding.callable.return_vector_c_style_enum().unwrap() %}
python_result = {{ return_enum.vector_decoder_name() }}(result);
{% else %}
{% let return_primitive = binding.callable.return_primitive().unwrap() %}
python_result = {{ return_primitive.boxer_name() }}(result);
{% endif %}
{% endif %}
cleanup:
{% for cleanup_statement in binding.callable.cleanup_statements() %}
{{ cleanup_statement }}
{% endfor %}
return python_result;
}
{% endfor %}
static PyMethodDef boltffi_python_methods[] = {
{% if module.has_native_callables() %}
{"_initialize_loader", (PyCFunction)boltffi_python_initialize_loader, METH_O, NULL},
{% endif %}
{% for record in module.records %}
{"{{ record.type_ref.registration_function_name() }}", (PyCFunction){{ record.type_ref.registration_wrapper_name() }}, METH_FASTCALL, NULL},
{% endfor %}
{% for enumeration in module.enums %}
{"{{ enumeration.type_ref.registration_function_name() }}", (PyCFunction){{ enumeration.type_ref.registration_wrapper_name() }}, METH_FASTCALL, NULL},
{% endfor %}
{% for binding in module.native_callables() %}
{"{{ binding.module_attribute_name }}", (PyCFunction){{ binding.callable.wrapper_name() }}, METH_FASTCALL, NULL},
{% endfor %}
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef boltffi_python_module = {
PyModuleDef_HEAD_INIT,
"{{ module.module_name }}._native",
NULL,
-1,
boltffi_python_methods,
};
PyMODINIT_FUNC PyInit__native(void) {
return PyModule_Create(&boltffi_python_module);
}