Commit c45cafd0 authored by Simon Feltman's avatar Simon Feltman
Browse files

cache refactoring: Move GArray arg setup and marshaling into new file

Move GArray argument caching and marshaling fragments into an
isolated file: pygi-array.c.

https://bugzilla.gnome.org/show_bug.cgi?id=709700
parent 4697a379
......@@ -107,6 +107,8 @@ _gi_la_SOURCES = \
pygi-basictype.h \
pygi-list.c \
pygi-list.h \
pygi-array.c \
pygi-array.h \
pygi-hashtable.c \
pygi-hashtable.h
_gi_la_CFLAGS = \
......
This diff is collapsed.
/* -*- Mode: C; c-basic-offset: 4 -*-
* vim: tabstop=4 shiftwidth=4 expandtab
*
* Copyright (C) 2014 Simon Feltman <sfeltman@gnome.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __PYGI_ARRAY_H__
#define __PYGI_ARRAY_H__
#include <girepository.h>
#include "pygi-cache.h"
G_BEGIN_DECLS
PyGIArgCache *pygi_arg_garray_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction);
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);
G_END_DECLS
#endif /*__PYGI_ARRAY_H__*/
......@@ -30,6 +30,7 @@
#include "pygi-hashtable.h"
#include "pygi-basictype.h"
#include "pygi-list.h"
#include "pygi-array.h"
PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info,
......@@ -160,15 +161,6 @@ _sequence_cache_free_func (PyGISequenceCache *cache)
}
}
static void
_array_cache_free_func (PyGIArgGArray *cache)
{
if (cache != NULL) {
_pygi_arg_cache_free (((PyGISequenceCache *)cache)->item_cache);
g_slice_free (PyGIArgGArray, cache);
}
}
static void
_callback_cache_free_func (PyGICallbackCache *cache)
{
......@@ -255,80 +247,6 @@ pygi_arg_sequence_setup (PyGISequenceCache *sc,
return TRUE;
}
static gboolean
pygi_arg_garray_setup (PyGIArgGArray *sc,
GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be NULL for return arguments */
GITransfer transfer,
PyGIDirection direction,
gssize child_offset)
{
GITypeInfo *item_type_info;
if (!pygi_arg_sequence_setup ((PyGISequenceCache *)sc,
type_info,
arg_info,
transfer,
direction)) {
return FALSE;
}
((PyGIArgCache *)sc)->destroy_notify = (GDestroyNotify)_array_cache_free_func;
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 = g_type_info_get_array_length (type_info);
if (sc->len_arg_index >= 0)
sc->len_arg_index += child_offset;
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);
return TRUE;
}
static PyGISequenceCache *
pygi_arg_sequence_new (GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction)
{
PyGISequenceCache *sc = g_slice_new0 (PyGISequenceCache);
if (sc == NULL)
return NULL;
if (!pygi_arg_sequence_setup (sc, type_info, arg_info, transfer, direction)) {
_pygi_arg_cache_free ( (PyGIArgCache *)sc);
return NULL;
}
return sc;
}
static PyGIArgGArray *
_arg_array_cache_new (GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction,
gssize child_offset)
{
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,
child_offset)) {
_pygi_arg_cache_free ( (PyGIArgCache *)array_cache);
return NULL;
}
return array_cache;
}
static PyGICallbackCache *
_callback_cache_new (GIArgInfo *arg_info,
GIInterfaceInfo *iface_info,
......@@ -357,109 +275,6 @@ _arg_cache_alloc (void)
return g_slice_new0 (PyGIArgCache);
}
static PyGIArgCache*
_arg_cache_array_len_arg_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache,
PyGIDirection direction,
gssize arg_index,
gssize *py_arg_index)
{
PyGIArgGArray *seq_cache = (PyGIArgGArray *)arg_cache;
if (seq_cache->len_arg_index >= 0) {
PyGIArgCache *child_cache = NULL;
child_cache = _pygi_callable_cache_get_arg (callable_cache,
seq_cache->len_arg_index);
if (child_cache == NULL) {
child_cache = _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 = NULL;
child_cache->from_py_marshaller = NULL;
/* 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) {
gssize i;
(*py_arg_index) -= 1;
callable_cache->n_py_args -= 1;
for (i = seq_cache->len_arg_index + 1;
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, seq_cache->len_arg_index, child_cache);
return child_cache;
}
return NULL;
}
static gboolean
_arg_cache_from_py_array_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache,
GITypeInfo *type_info,
GITransfer transfer,
PyGIDirection direction,
gssize arg_index)
{
PyGIArgGArray *seq_cache = (PyGIArgGArray *)arg_cache;
seq_cache->array_type = g_type_info_get_array_type (type_info);
arg_cache->from_py_marshaller = _pygi_marshal_from_py_array;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_array;
return TRUE;
}
static gboolean
_arg_cache_to_py_array_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache,
GITypeInfo *type_info,
GITransfer transfer,
PyGIDirection direction,
gssize arg_index)
{
PyGIArgGArray *seq_cache = (PyGIArgGArray *)arg_cache;
seq_cache->array_type = g_type_info_get_array_type (type_info);
arg_cache->to_py_marshaller = _pygi_marshal_to_py_array;
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_array;
return TRUE;
}
static void
_arg_cache_from_py_gerror_setup (PyGIArgCache *arg_cache)
{
......@@ -717,16 +532,10 @@ _arg_cache_new (GITypeInfo *type_info,
PyGICallableCache *callable_cache)
{
PyGIArgCache *arg_cache = NULL;
gssize child_offset = 0;
GITypeTag type_tag;
type_tag = g_type_info_get_tag (type_info);
if (callable_cache != NULL)
child_offset =
(callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
switch (type_tag) {
case GI_TYPE_TAG_VOID:
case GI_TYPE_TAG_BOOLEAN:
......@@ -755,34 +564,15 @@ _arg_cache_new (GITypeInfo *type_info,
case GI_TYPE_TAG_ARRAY:
{
PyGIArgGArray *seq_cache =
_arg_array_cache_new (type_info,
arg_cache = pygi_arg_garray_new_from_info (type_info,
arg_info,
transfer,
direction,
child_offset);
arg_cache = (PyGIArgCache *)seq_cache;
direction);
if (arg_cache == NULL)
break;
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_array_setup (arg_cache,
callable_cache,
type_info,
transfer,
direction,
c_arg_index);
return NULL;
if (direction & PYGI_DIRECTION_TO_PYTHON)
_arg_cache_to_py_array_setup (arg_cache,
callable_cache,
pygi_arg_garray_len_arg_setup (arg_cache,
type_info,
transfer,
direction,
c_arg_index);
_arg_cache_array_len_arg_setup (arg_cache,
callable_cache,
direction,
c_arg_index,
......
......@@ -294,153 +294,3 @@ _pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *state,
( (PyGIInterfaceCache *)arg_cache)->interface_info,
data);
}
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) {
len = g_strv_length ((gchar **)data);
} else if (array_cache->len_arg_index >= 0) {
GIArgument *len_arg = state->args[array_cache->len_arg_index];
len = len_arg->v_long;
}
array_ = g_array_new (FALSE,
FALSE,
array_cache->item_size);
if (array_ == NULL)
return NULL;
g_free (array_->data);
array_->data = data;
array_->len = len;
return array_;
}
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 = (array_ != NULL) ? array_->len : ptr_array_->len;
PyGIMarshalCleanupFunc cleanup_func =
sequence_cache->item_cache->from_py_cleanup;
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_marshal_cleanup_from_py_interface_struct_gvalue) {
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_);
}
}
}
void
_pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *dummy,
gpointer data,
gboolean was_processed)
{
if (arg_cache->transfer == GI_TRANSFER_EVERYTHING ||
arg_cache->transfer == GI_TRANSFER_CONTAINER) {
GArray *array_ = NULL;
GPtrArray *ptr_array_ = NULL;
PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache;
PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache;
/* 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;
} 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) {
gsize i;
guint len = (array_ != NULL) ? array_->len : ptr_array_->len;
PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->to_py_cleanup;
for (i = 0; i < len; i++) {
cleanup_func (state,
sequence_cache->item_cache,
NULL,
(array_ != NULL) ? g_array_index (array_, gpointer, i) : g_ptr_array_index (ptr_array_, i),
was_processed);
}
}
if (array_ != NULL)
g_array_free (array_, TRUE);
else
g_ptr_array_free (ptr_array_, TRUE);
}
}
......@@ -70,16 +70,6 @@ void _pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *stat
PyObject *py_arg,
gpointer data,
gboolean was_processed);
void _pygi_marshal_cleanup_from_py_array (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed);
void _pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *dummy,
gpointer data,
gboolean was_processed);
G_END_DECLS
#endif /* __PYGI_MARSHAL_CLEANUP_H__ */
......@@ -48,108 +48,6 @@ static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};
#endif
#endif
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 = size_in;
return TRUE;
} else {
goto overflow;
}
case GI_TYPE_TAG_UINT8:
if (size_in >= 0 && size_in <= G_MAXUINT8) {
arg_out->v_uint8 = 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 = size_in;
return TRUE;
} else {
goto overflow;
}
case GI_TYPE_TAG_UINT16:
if (size_in >= 0 && size_in <= G_MAXUINT16) {
arg_out->v_uint16 = 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 = size_in;
return TRUE;
} else {
goto overflow;
}
case GI_TYPE_TAG_UINT32:
if (size_in >= 0 && size_in <= G_MAXUINT32) {
arg_out->v_uint32 = 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_from_c_long (GIArgument *arg_out,
long c_long_in,
......@@ -242,258 +140,6 @@ _is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) {
return is_member;
}
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;
int i = 0;
int success_count = 0;
Py_ssize_t length;
gssize item_size;
gboolean is_ptr_array;
GArray *array_ = NULL;
PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache;
PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache;