/* -*- Mode: C; c-basic-offset: 4 -*- * vim: tabstop=4 shiftwidth=4 expandtab * * Copyright (C) 2011 John (J5) Palmieri * Copyright (C) 2014 Simon Feltman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include #include #include "pygi-array.h" #include "pygi-info.h" #include "pygi-marshal-cleanup.h" #include "pygi-basictype.h" #include "pygi-util.h" /* Needed for _pygi_marshal_cleanup_from_py_interface_struct_gvalue hack */ #include "pygi-struct-marshal.h" /* * GArray to Python */ static gboolean gi_argument_from_py_ssize_t (GIArgument *arg_out, Py_ssize_t size_in, GITypeTag type_tag) { switch (type_tag) { case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_BOOLEAN: goto unhandled_type; case GI_TYPE_TAG_INT8: if (size_in >= G_MININT8 && size_in <= G_MAXINT8) { arg_out->v_int8 = (gint8)size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_UINT8: if (size_in >= 0 && size_in <= G_MAXUINT8) { arg_out->v_uint8 = (guint8)size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_INT16: if (size_in >= G_MININT16 && size_in <= G_MAXINT16) { arg_out->v_int16 = (gint16)size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_UINT16: if (size_in >= 0 && size_in <= G_MAXUINT16) { arg_out->v_uint16 = (guint16)size_in; return TRUE; } else { goto overflow; } /* Ranges assume two's complement */ case GI_TYPE_TAG_INT32: if (size_in >= G_MININT32 && size_in <= G_MAXINT32) { arg_out->v_int32 = (gint32)size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_UINT32: if (size_in >= 0 && (gsize)size_in <= G_MAXUINT32) { arg_out->v_uint32 = (guint32)size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_INT64: arg_out->v_int64 = size_in; return TRUE; case GI_TYPE_TAG_UINT64: if (size_in >= 0) { arg_out->v_uint64 = size_in; return TRUE; } else { goto overflow; } case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_UNICHAR: default: goto unhandled_type; } overflow: PyErr_Format (PyExc_OverflowError, "Unable to marshal C Py_ssize_t %zd to %s", size_in, g_type_tag_to_string (type_tag)); return FALSE; unhandled_type: PyErr_Format (PyExc_TypeError, "Unable to marshal C Py_ssize_t %zd to %s", size_in, g_type_tag_to_string (type_tag)); return FALSE; } static gboolean gi_argument_to_gsize (GIArgument *arg_in, gsize *gsize_out, GITypeTag type_tag) { switch (type_tag) { case GI_TYPE_TAG_INT8: *gsize_out = arg_in->v_int8; return TRUE; case GI_TYPE_TAG_UINT8: *gsize_out = arg_in->v_uint8; return TRUE; case GI_TYPE_TAG_INT16: *gsize_out = arg_in->v_int16; return TRUE; case GI_TYPE_TAG_UINT16: *gsize_out = arg_in->v_uint16; return TRUE; case GI_TYPE_TAG_INT32: *gsize_out = arg_in->v_int32; return TRUE; case GI_TYPE_TAG_UINT32: *gsize_out = arg_in->v_uint32; return TRUE; case GI_TYPE_TAG_INT64: if (arg_in->v_uint64 > G_MAXSIZE) { PyErr_Format (PyExc_TypeError, "Unable to marshal %s to gsize", g_type_tag_to_string (type_tag)); return FALSE; } *gsize_out = (gsize)arg_in->v_int64; return TRUE; case GI_TYPE_TAG_UINT64: if (arg_in->v_uint64 > G_MAXSIZE) { PyErr_Format (PyExc_TypeError, "Unable to marshal %s to gsize", g_type_tag_to_string (type_tag)); return FALSE; } *gsize_out = (gsize)arg_in->v_uint64; return TRUE; default: PyErr_Format (PyExc_TypeError, "Unable to marshal %s to gsize", g_type_tag_to_string (type_tag)); return FALSE; } } static gboolean _pygi_marshal_from_py_array (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, PyObject *py_arg, GIArgument *arg, gpointer *cleanup_data) { PyGIMarshalFromPyFunc from_py_marshaller; guint i = 0; gsize success_count = 0; Py_ssize_t py_length; guint length; guint item_size; gboolean is_ptr_array; GArray *array_ = NULL; PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; GITransfer cleanup_transfer = arg_cache->transfer; if (py_arg == Py_None) { arg->v_pointer = NULL; return TRUE; } if (!PySequence_Check (py_arg)) { PyErr_Format (PyExc_TypeError, "Must be sequence, not %s", Py_TYPE (py_arg)->tp_name); return FALSE; } py_length = PySequence_Length (py_arg); if (py_length < 0) return FALSE; if (!pygi_guint_from_pyssize (py_length, &length)) return FALSE; if (array_cache->fixed_size >= 0 && (guint)array_cache->fixed_size != length) { PyErr_Format (PyExc_ValueError, "Must contain %zd items, not %u", array_cache->fixed_size, length); return FALSE; } item_size = (guint)array_cache->item_size; is_ptr_array = (array_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY); if (is_ptr_array) { array_ = (GArray *)g_ptr_array_sized_new (length); } else { array_ = g_array_sized_new (array_cache->is_zero_terminated, TRUE, item_size, length); } if (array_ == NULL) { PyErr_NoMemory (); return FALSE; } if (sequence_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8 && PyBytes_Check (py_arg)) { gchar *data = PyBytes_AsString (py_arg); /* Avoid making a copy if the data * is not transferred to the C function * and cannot not be modified by it. */ if (array_cache->array_type == GI_ARRAY_TYPE_C && arg_cache->transfer == GI_TRANSFER_NOTHING && !array_cache->is_zero_terminated) { g_free (array_->data); array_->data = data; cleanup_transfer = GI_TRANSFER_EVERYTHING; } else { memcpy (array_->data, data, length); } array_->len = length; if (array_cache->is_zero_terminated) { /* If array_ has been created with zero_termination, space for the * terminator is properly allocated, so we're not off-by-one here. */ array_->data[length] = '\0'; } goto array_success; } from_py_marshaller = sequence_cache->item_cache->from_py_marshaller; for (i = 0, success_count = 0; i < length; i++) { GIArgument item = {0}; gpointer item_cleanup_data = NULL; PyObject *py_item = PySequence_GetItem (py_arg, i); if (py_item == NULL) goto err; if (!from_py_marshaller ( state, callable_cache, sequence_cache->item_cache, py_item, &item, &item_cleanup_data)) { Py_DECREF (py_item); goto err; } Py_DECREF (py_item); if (item_cleanup_data != NULL && item_cleanup_data != item.v_pointer) { /* We only support one level of data discrepancy between an items * data and its cleanup data. This is because we only track a single * extra cleanup data pointer per-argument and cannot track the entire * array of items differing data and cleanup_data. * For example, this would fail if trying to marshal an array of * callback closures marked with SCOPE call type where the cleanup data * is different from the items v_pointer, likewise an array of arrays. */ PyErr_SetString(PyExc_RuntimeError, "Cannot cleanup item data for array due to " "the items data its cleanup data being different."); goto err; } /* FIXME: it is much more efficent to have seperate marshaller * for ptr arrays than doing the evaluation * and casting each loop iteration */ if (is_ptr_array) { g_ptr_array_add((GPtrArray *)array_, item.v_pointer); } else if (sequence_cache->item_cache->is_pointer) { /* if the item is a pointer, simply copy the pointer */ g_assert (item_size == sizeof (item.v_pointer)); g_array_insert_val (array_, i, item); } else if (sequence_cache->item_cache->type_tag == GI_TYPE_TAG_INTERFACE) { /* Special case handling of flat arrays of gvalue/boxed/struct */ PyGIInterfaceCache *item_iface_cache = (PyGIInterfaceCache *) sequence_cache->item_cache; GIBaseInfo *base_info = (GIBaseInfo *) item_iface_cache->interface_info; GIInfoType info_type = g_base_info_get_type (base_info); switch (info_type) { case GI_INFO_TYPE_UNION: case GI_INFO_TYPE_STRUCT: { PyGIArgCache *item_arg_cache = (PyGIArgCache *)item_iface_cache; PyGIMarshalCleanupFunc from_py_cleanup = item_arg_cache->from_py_cleanup; if (g_type_is_a (item_iface_cache->g_type, G_TYPE_VALUE)) { /* Special case GValue flat arrays to properly init and copy the contents. */ GValue* dest = (GValue*)(void*)(array_->data + (i * item_size)); if (item.v_pointer != NULL) { memset (dest, 0, item_size); g_value_init (dest, G_VALUE_TYPE ((GValue*) item.v_pointer)); g_value_copy ((GValue*) item.v_pointer, dest); } /* Manually increment the length because we are manually setting the memory. */ array_->len++; } else { /* Handles flat arrays of boxed or struct types. */ g_array_insert_vals (array_, i, item.v_pointer, 1); } /* Cleanup any memory left by the per-item marshaler because * _pygi_marshal_cleanup_from_py_array will not know about this * due to "item" being a temporarily marshaled value done on the stack. */ if (from_py_cleanup) from_py_cleanup (state, item_arg_cache, py_item, item_cleanup_data, TRUE); break; } default: g_array_insert_val (array_, i, item); } } else { /* default value copy of a simple type */ g_array_insert_val (array_, i, item); } success_count++; } goto array_success; err: if (sequence_cache->item_cache->from_py_cleanup != NULL) { gsize j; PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->from_py_cleanup; /* Only attempt per item cleanup on pointer items */ if (sequence_cache->item_cache->is_pointer) { for(j = 0; j < success_count; j++) { PyObject *py_seq_item = PySequence_GetItem (py_arg, j); cleanup_func (state, sequence_cache->item_cache, py_seq_item, is_ptr_array ? g_ptr_array_index ((GPtrArray *)array_, j) : g_array_index (array_, gpointer, j), TRUE); Py_DECREF (py_seq_item); } } } if (is_ptr_array) g_ptr_array_free ( ( GPtrArray *)array_, TRUE); else g_array_free (array_, TRUE); _PyGI_ERROR_PREFIX ("Item %u: ", i); return FALSE; array_success: if (array_cache->len_arg_index >= 0) { /* we have an child arg to handle */ PyGIArgCache *child_cache = _pygi_callable_cache_get_arg (callable_cache, (guint)array_cache->len_arg_index); if (!gi_argument_from_py_ssize_t (&state->args[child_cache->c_arg_index].arg_value, length, child_cache->type_tag)) { goto err; } } if (array_cache->array_type == GI_ARRAY_TYPE_C) { /* In the case of GI_ARRAY_C, we give the data directly as the argument * but keep the array_ wrapper as cleanup data so we don't have to find * it's length again. */ arg->v_pointer = array_->data; if (cleanup_transfer == GI_TRANSFER_EVERYTHING) { g_array_free (array_, FALSE); *cleanup_data = NULL; } else { *cleanup_data = array_; } } else { arg->v_pointer = array_; if (cleanup_transfer == GI_TRANSFER_NOTHING) { /* Free everything in cleanup. */ *cleanup_data = array_; } else if (cleanup_transfer == GI_TRANSFER_CONTAINER) { /* Make a shallow copy so we can free the elements later in cleanup * because it is possible invoke will free the list before our cleanup. */ *cleanup_data = is_ptr_array ? (gpointer)g_ptr_array_ref ((GPtrArray *)array_) : (gpointer)g_array_ref (array_); } else { /* GI_TRANSFER_EVERYTHING */ /* No cleanup, everything is given to the callee. */ *cleanup_data = NULL; } } return TRUE; } static void _pygi_marshal_cleanup_from_py_array (PyGIInvokeState *state, PyGIArgCache *arg_cache, PyObject *py_arg, gpointer data, gboolean was_processed) { if (was_processed) { GArray *array_ = NULL; GPtrArray *ptr_array_ = NULL; PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; if (array_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY) { ptr_array_ = (GPtrArray *) data; } else { array_ = (GArray *) data; } /* clean up items first */ if (sequence_cache->item_cache->from_py_cleanup != NULL) { gsize i; guint len; PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->from_py_cleanup; g_assert (array_ || ptr_array_); len = (array_ != NULL) ? array_->len : ptr_array_->len; for (i = 0; i < len; i++) { gpointer item; PyObject *py_item = NULL; /* case 1: GPtrArray */ if (ptr_array_ != NULL) item = g_ptr_array_index (ptr_array_, i); /* case 2: C array or GArray with object pointers */ else if (sequence_cache->item_cache->is_pointer) item = g_array_index (array_, gpointer, i); /* case 3: C array or GArray with simple types or structs */ else { item = array_->data + i * array_cache->item_size; /* special-case hack: GValue array items do not get slice * allocated in _pygi_marshal_from_py_array(), so we must * not try to deallocate it as a slice and thus * short-circuit cleanup_func. */ if (cleanup_func == pygi_arg_gvalue_from_py_cleanup) { g_value_unset ((GValue*) item); continue; } } py_item = PySequence_GetItem (py_arg, i); cleanup_func (state, sequence_cache->item_cache, py_item, item, TRUE); Py_XDECREF (py_item); } } /* Only free the array when we didn't transfer ownership */ if (array_cache->array_type == GI_ARRAY_TYPE_C) { /* always free the GArray wrapper created in from_py marshaling and * passed back as cleanup_data */ g_array_free (array_, arg_cache->transfer == GI_TRANSFER_NOTHING); } else { if (array_ != NULL) g_array_unref (array_); else g_ptr_array_unref (ptr_array_); } } } /* * GArray from Python */ static PyObject * _pygi_marshal_to_py_array (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, GIArgument *arg, gpointer *cleanup_data) { GArray *array_; PyObject *py_obj = NULL; PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; guint processed_items = 0; /* GArrays make it easier to iterate over arrays * with different element sizes but requires that * we allocate a GArray if the argument was a C array */ if (array_cache->array_type == GI_ARRAY_TYPE_C) { gsize len; if (array_cache->fixed_size >= 0) { g_assert(arg->v_pointer != NULL); len = array_cache->fixed_size; } else if (array_cache->is_zero_terminated) { if (arg->v_pointer == NULL) { len = 0; } else if (array_cache->item_size == 1) { len = strlen (arg->v_pointer); } else if (array_cache->item_size == sizeof(gpointer)) { len = g_strv_length ((gchar **)arg->v_pointer); } else if (array_cache->item_size == sizeof(int)) { for (len = 0; *(((int*)arg->v_pointer) + len); len++); } else if (array_cache->item_size == sizeof(short)) { for (len = 0; *(((short*)arg->v_pointer) + len); len++); } else { g_assert_not_reached (); } } else { GIArgument *len_arg = &state->args[array_cache->len_arg_index].arg_value; PyGIArgCache *sub_cache = _pygi_callable_cache_get_arg (callable_cache, (guint)array_cache->len_arg_index); if (!gi_argument_to_gsize (len_arg, &len, sub_cache->type_tag)) { return NULL; } } array_ = g_array_new (FALSE, FALSE, (guint)array_cache->item_size); if (array_ == NULL) { PyErr_NoMemory (); if (arg_cache->transfer == GI_TRANSFER_EVERYTHING && arg->v_pointer != NULL) g_free (arg->v_pointer); return NULL; } if (array_->data != NULL) g_free (array_->data); array_->data = arg->v_pointer; array_->len = (guint)len; } else { array_ = arg->v_pointer; } if (seq_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8) { if (arg->v_pointer == NULL) { py_obj = PyBytes_FromString (""); } else { py_obj = PyBytes_FromStringAndSize (array_->data, array_->len); } } else { if (arg->v_pointer == NULL) { py_obj = PyList_New (0); } else { guint i; gsize item_size; PyGIMarshalToPyFunc item_to_py_marshaller; PyGIArgCache *item_arg_cache; GPtrArray *item_cleanups; py_obj = PyList_New (array_->len); if (py_obj == NULL) goto err; item_cleanups = g_ptr_array_sized_new (array_->len); *cleanup_data = item_cleanups; item_arg_cache = seq_cache->item_cache; item_to_py_marshaller = item_arg_cache->to_py_marshaller; item_size = g_array_get_element_size (array_); for (i = 0; i < array_->len; i++) { GIArgument item_arg = {0}; PyObject *py_item; gpointer item_cleanup_data = NULL; /* If we are receiving an array of pointers, simply assign the pointer * and move on, letting the per-item marshaler deal with the * various transfer modes and ref counts (e.g. g_variant_ref_sink). */ if (array_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY) { item_arg.v_pointer = g_ptr_array_index ( ( GPtrArray *)array_, i); } else if (item_arg_cache->is_pointer) { item_arg.v_pointer = g_array_index (array_, gpointer, i); } else if (item_arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) { PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *) item_arg_cache; /* FIXME: This probably doesn't work with boxed types or gvalues. * See fx. _pygi_marshal_from_py_array() */ switch (g_base_info_get_type (iface_cache->interface_info)) { case GI_INFO_TYPE_STRUCT: if (arg_cache->transfer == GI_TRANSFER_EVERYTHING && !g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) { /* array elements are structs */ gpointer *_struct = g_malloc (item_size); memcpy (_struct, array_->data + i * item_size, item_size); item_arg.v_pointer = _struct; } else { item_arg.v_pointer = array_->data + i * item_size; } break; case GI_INFO_TYPE_ENUM: memcpy (&item_arg, array_->data + i * item_size, item_size); break; default: item_arg.v_pointer = g_array_index (array_, gpointer, i); break; } } else { memcpy (&item_arg, array_->data + i * item_size, item_size); } py_item = item_to_py_marshaller ( state, callable_cache, item_arg_cache, &item_arg, &item_cleanup_data); g_ptr_array_index (item_cleanups, i) = item_cleanup_data; if (py_item == NULL) { Py_CLEAR (py_obj); if (array_cache->array_type == GI_ARRAY_TYPE_C) g_array_unref (array_); g_ptr_array_unref (item_cleanups); goto err; } PyList_SET_ITEM (py_obj, i, py_item); processed_items++; } } } if (array_cache->array_type == GI_ARRAY_TYPE_C) g_array_free (array_, FALSE); return py_obj; err: if (array_cache->array_type == GI_ARRAY_TYPE_C) { g_array_free (array_, arg_cache->transfer == GI_TRANSFER_EVERYTHING); } else { /* clean up unprocessed items */ if (seq_cache->item_cache->to_py_cleanup != NULL) { guint j; PyGIMarshalToPyCleanupFunc cleanup_func = seq_cache->item_cache->to_py_cleanup; for (j = processed_items; j < array_->len; j++) { cleanup_func (state, seq_cache->item_cache, NULL, g_array_index (array_, gpointer, j), FALSE); } } if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) g_array_free (array_, TRUE); } return NULL; } static GArray* _wrap_c_array (PyGIInvokeState *state, PyGIArgGArray *array_cache, gpointer data) { GArray *array_; gsize len = 0; if (array_cache->fixed_size >= 0) { len = array_cache->fixed_size; } else if (array_cache->is_zero_terminated) { if (array_cache->item_size == sizeof(gpointer)) len = g_strv_length ((gchar **)data); else if (array_cache->item_size == 1) len = strlen ((gchar*)data); else if (array_cache->item_size == sizeof(int)) for (len = 0; *(((int*)data) + len); len++); else if (array_cache->item_size == sizeof(short)) for (len = 0; *(((short*)data) + len); len++); else g_assert_not_reached (); } else if (array_cache->len_arg_index >= 0) { GIArgument *len_arg = &state->args[array_cache->len_arg_index].arg_value; len = len_arg->v_long; } array_ = g_array_new (FALSE, FALSE, (guint)array_cache->item_size); if (array_ == NULL) return NULL; g_free (array_->data); array_->data = data; array_->len = (guint)len; return array_; } static void _pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state, PyGIArgCache *arg_cache, gpointer cleanup_data, gpointer data, gboolean was_processed) { GArray *array_ = NULL; GPtrArray *ptr_array_ = NULL; PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; gboolean free_array = FALSE; gboolean free_array_full = TRUE; if (arg_cache->transfer == GI_TRANSFER_EVERYTHING || arg_cache->transfer == GI_TRANSFER_CONTAINER) { free_array = TRUE; } /* If this isn't a garray create one to help process variable sized array elements */ if (array_cache->array_type == GI_ARRAY_TYPE_C) { array_ = _wrap_c_array (state, array_cache, data); if (array_ == NULL) return; free_array = TRUE; free_array_full = arg_cache->transfer != GI_TRANSFER_NOTHING; } else if (array_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY) { ptr_array_ = (GPtrArray *) data; } else { array_ = (GArray *) data; } if (sequence_cache->item_cache->to_py_cleanup != NULL) { GPtrArray *item_cleanups = (GPtrArray *) cleanup_data; gsize i; guint len; PyGIMarshalToPyCleanupFunc cleanup_func = sequence_cache->item_cache->to_py_cleanup; g_assert (array_ || ptr_array_); len = (array_ != NULL) ? array_->len : ptr_array_->len; for (i = 0; i < len; i++) { cleanup_func (state, sequence_cache->item_cache, g_ptr_array_index(item_cleanups, i), (array_ != NULL) ? g_array_index (array_, gpointer, i) : g_ptr_array_index (ptr_array_, i), was_processed); } } if (cleanup_data) g_ptr_array_unref ((GPtrArray *) cleanup_data); if (free_array) { if (array_ != NULL) g_array_free (array_, free_array_full); else g_ptr_array_free (ptr_array_, free_array_full); } } static void _array_cache_free_func (PyGIArgGArray *cache) { if (cache != NULL) { pygi_arg_cache_free (((PyGISequenceCache *)cache)->item_cache); g_slice_free (PyGIArgGArray, cache); } } PyGIArgCache* pygi_arg_garray_len_arg_setup (PyGIArgCache *arg_cache, GITypeInfo *type_info, PyGICallableCache *callable_cache, PyGIDirection direction, gssize arg_index, gssize *py_arg_index) { PyGIArgGArray *seq_cache = (PyGIArgGArray *)arg_cache; /* attempt len_arg_index setup for the first time */ if (seq_cache->len_arg_index < 0) { seq_cache->len_arg_index = g_type_info_get_array_length (type_info); /* offset by self arg for methods and vfuncs */ if (seq_cache->len_arg_index >= 0 && callable_cache != NULL) { seq_cache->len_arg_index += callable_cache->args_offset; } } if (seq_cache->len_arg_index >= 0) { PyGIArgCache *child_cache = NULL; child_cache = _pygi_callable_cache_get_arg (callable_cache, (guint)seq_cache->len_arg_index); if (child_cache == NULL) { child_cache = pygi_arg_cache_alloc (); } else { /* If the "length" arg cache already exists (the length comes before * the array in the argument list), remove it from the to_py_args list * because it does not belong in "to python" return tuple. The length * will implicitly be a part of the returned Python list. */ if (direction & PYGI_DIRECTION_TO_PYTHON) { callable_cache->to_py_args = g_slist_remove (callable_cache->to_py_args, child_cache); } /* This is a case where the arg cache already exists and has been * setup by another array argument sharing the same length argument. * See: gi_marshalling_tests_multi_array_key_value_in */ if (child_cache->meta_type == PYGI_META_ARG_TYPE_CHILD) return child_cache; } /* There is a length argument for this array, so increment the number * of "to python" child arguments when applicable. */ if (direction & PYGI_DIRECTION_TO_PYTHON) callable_cache->n_to_py_child_args++; child_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; child_cache->direction = direction; child_cache->to_py_marshaller = pygi_marshal_to_py_basic_type_cache_adapter; child_cache->from_py_marshaller = pygi_marshal_from_py_basic_type_cache_adapter; child_cache->py_arg_index = -1; /* ugly edge case code: * * When the length comes before the array parameter we need to update * indexes of arguments after the index argument. */ if (seq_cache->len_arg_index < arg_index && direction & PYGI_DIRECTION_FROM_PYTHON) { guint i; (*py_arg_index) -= 1; callable_cache->n_py_args -= 1; for (i = (guint)seq_cache->len_arg_index + 1; (gsize)i < _pygi_callable_cache_args_len (callable_cache); i++) { PyGIArgCache *update_cache = _pygi_callable_cache_get_arg (callable_cache, i); if (update_cache == NULL) break; update_cache->py_arg_index -= 1; } } _pygi_callable_cache_set_arg (callable_cache, (guint)seq_cache->len_arg_index, child_cache); return child_cache; } return NULL; } static gboolean pygi_arg_garray_setup (PyGIArgGArray *sc, GITypeInfo *type_info, GIArgInfo *arg_info, /* may be NULL for return arguments */ GITransfer transfer, PyGIDirection direction, PyGICallableCache *callable_cache) { GITypeInfo *item_type_info; PyGIArgCache *arg_cache = (PyGIArgCache *)sc; if (!pygi_arg_sequence_setup ((PyGISequenceCache *)sc, type_info, arg_info, transfer, direction, callable_cache)) { return FALSE; } ((PyGIArgCache *)sc)->destroy_notify = (GDestroyNotify)_array_cache_free_func; sc->array_type = g_type_info_get_array_type (type_info); sc->is_zero_terminated = g_type_info_is_zero_terminated (type_info); sc->fixed_size = g_type_info_get_array_fixed_size (type_info); sc->len_arg_index = -1; /* setup by pygi_arg_garray_len_arg_setup */ item_type_info = g_type_info_get_param_type (type_info, 0); sc->item_size = _pygi_g_type_info_size (item_type_info); g_base_info_unref ( (GIBaseInfo *)item_type_info); if (direction & PYGI_DIRECTION_FROM_PYTHON) { arg_cache->from_py_marshaller = _pygi_marshal_from_py_array; arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_array; } if (direction & PYGI_DIRECTION_TO_PYTHON) { arg_cache->to_py_marshaller = _pygi_marshal_to_py_array; arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_array; } return TRUE; } PyGIArgCache * pygi_arg_garray_new_from_info (GITypeInfo *type_info, GIArgInfo *arg_info, GITransfer transfer, PyGIDirection direction, PyGICallableCache *callable_cache) { PyGIArgGArray *array_cache = g_slice_new0 (PyGIArgGArray); if (array_cache == NULL) return NULL; if (!pygi_arg_garray_setup (array_cache, type_info, arg_info, transfer, direction, callable_cache)) { pygi_arg_cache_free ( (PyGIArgCache *)array_cache); return NULL; } return (PyGIArgCache *)array_cache; }