Commit 4a6bf3be authored by Simon Feltman's avatar Simon Feltman

cache refactoring: Move PyGIHashCache and related marshaling into new file

Re-organize hash table arg cache and its marshaling by moving all
related code fragments into an isolated file where most of it is made
static. pygi-hashtable.h exposes a single function:
pygi_arg_hash_table_new_from_info. This is all the caching system needs to
produce the proper bits for handling hash table marshaling.

https://bugzilla.gnome.org/show_bug.cgi?id=709700
parent 983d0c22
......@@ -102,7 +102,9 @@ _gi_la_SOURCES = \
pygi-marshal-to-py.c \
pygi-marshal-to-py.h \
pygi-marshal-cleanup.c \
pygi-marshal-cleanup.h
pygi-marshal-cleanup.h \
pygi-hashtable.c \
pygi-hashtable.h
_gi_la_CFLAGS = \
$(extension_cppflags) \
$(GLIB_CFLAGS) \
......
......@@ -19,22 +19,16 @@
* USA
*/
#include <girepository.h>
#include "pygi-info.h"
#include "pygi-cache.h"
#include "pygi-marshal-to-py.h"
#include "pygi-marshal-from-py.h"
#include "pygi-marshal-cleanup.h"
#include "pygi-type.h"
#include <girepository.h>
#include "pygi-hashtable.h"
PyGIArgCache * _arg_cache_new (GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction,
/* will be removed */
gssize c_arg_index,
gssize py_arg_index,
PyGICallableCache *callable_cache);
PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info,
GITypeInfo *type_info,
......@@ -127,7 +121,7 @@ pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache,
/* cleanup */
static void
void
_pygi_arg_cache_free (PyGIArgCache *cache)
{
if (cache == NULL)
......@@ -154,15 +148,6 @@ _interface_cache_free_func (PyGIInterfaceCache *cache)
}
}
static void
_hash_cache_free_func (PyGIHashCache *cache)
{
if (cache != NULL) {
_pygi_arg_cache_free (cache->key_cache);
_pygi_arg_cache_free (cache->value_cache);
g_slice_free (PyGIHashCache, cache);
}
}
static void
_sequence_cache_free_func (PyGISequenceCache *cache)
......@@ -261,53 +246,6 @@ _sequence_cache_new (GITypeInfo *type_info,
return sc;
}
static PyGIHashCache *
_hash_cache_new (GITypeInfo *type_info,
GIDirection direction,
GITransfer transfer)
{
PyGIHashCache *hc;
GITypeInfo *key_type_info;
GITypeInfo *value_type_info;
GITransfer item_transfer;
hc = g_slice_new0 (PyGIHashCache);
( (PyGIArgCache *)hc)->destroy_notify = (GDestroyNotify)_hash_cache_free_func;
key_type_info = g_type_info_get_param_type (type_info, 0);
value_type_info = g_type_info_get_param_type (type_info, 1);
item_transfer =
transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
hc->key_cache = _arg_cache_new (key_type_info,
NULL,
item_transfer,
direction,
0, 0,
NULL);
if (hc->key_cache == NULL) {
_pygi_arg_cache_free ( (PyGIArgCache *)hc);
return NULL;
}
hc->value_cache = _arg_cache_new (value_type_info,
NULL,
item_transfer,
direction,
0, 0,
NULL);
if (hc->value_cache == NULL) {
_pygi_arg_cache_free ( (PyGIArgCache *)hc);
return NULL;
}
g_base_info_unref( (GIBaseInfo *)key_type_info);
g_base_info_unref( (GIBaseInfo *)value_type_info);
return hc;
}
static PyGICallbackCache *
_callback_cache_new (GIArgInfo *arg_info,
......@@ -511,20 +449,6 @@ _arg_cache_to_py_gslist_setup (PyGIArgCache *arg_cache,
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_glist;
}
static void
_arg_cache_from_py_ghash_setup (PyGIArgCache *arg_cache)
{
arg_cache->from_py_marshaller = _pygi_marshal_from_py_ghash;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_ghash;
}
static void
_arg_cache_to_py_ghash_setup (PyGIArgCache *arg_cache)
{
arg_cache->to_py_marshaller = _pygi_marshal_to_py_ghash;
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_ghash;
}
static void
_arg_cache_from_py_gerror_setup (PyGIArgCache *arg_cache)
{
......@@ -920,22 +844,11 @@ _arg_cache_new (GITypeInfo *type_info,
break;
}
case GI_TYPE_TAG_GHASH:
arg_cache =
(PyGIArgCache *)_hash_cache_new (type_info,
direction,
transfer);
if (arg_cache == NULL)
break;
arg_cache = pygi_arg_hash_table_new_from_info (type_info, arg_info, transfer, direction);
arg_cache->py_arg_index = py_arg_index;
arg_cache->c_arg_index = c_arg_index;
return arg_cache;
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_ghash_setup (arg_cache);
if (direction & PYGI_DIRECTION_TO_PYTHON) {
_arg_cache_to_py_ghash_setup (arg_cache);
}
break;
case GI_TYPE_TAG_INTERFACE:
{
GIInterfaceInfo *interface_info = g_type_info_get_interface (type_info);
......
......@@ -150,13 +150,6 @@ typedef struct _PyGIInterfaceCache
gchar *type_name;
} PyGIInterfaceCache;
typedef struct _PyGIHashCache
{
PyGIArgCache arg_cache;
PyGIArgCache *key_cache;
PyGIArgCache *value_cache;
} PyGIHashCache;
typedef struct _PyGICallbackCache
{
PyGIArgCache arg_cache;
......@@ -218,8 +211,18 @@ pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache,
PyGIDirection direction,
GIInterfaceInfo *iface_info);
void _pygi_arg_cache_clear (PyGIArgCache *cache);
void _pygi_callable_cache_free (PyGICallableCache *cache);
PyGIArgCache * _arg_cache_new (GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction,
/* will be removed */
gssize c_arg_index,
gssize py_arg_index,
PyGICallableCache *callable_cache);
void _pygi_arg_cache_free (PyGIArgCache *cache);
void _pygi_arg_cache_clear (PyGIArgCache *cache);
void _pygi_callable_cache_free (PyGICallableCache *cache);
PyGICallableCache *_pygi_callable_cache_new (GICallableInfo *callable_info,
gboolean is_ccallback);
......
/* -*- Mode: C; c-basic-offset: 4 -*-
* vim: tabstop=4 shiftwidth=4 expandtab
*
* Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>
* 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/>.
*/
#include "pygi-hashtable.h"
#include "pygi-argument.h"
#include "pygi-private.h"
typedef struct _PyGIHashCache
{
PyGIArgCache arg_cache;
PyGIArgCache *key_cache;
PyGIArgCache *value_cache;
} PyGIHashCache;
static void
_hash_cache_free_func (PyGIHashCache *cache)
{
if (cache != NULL) {
_pygi_arg_cache_free (cache->key_cache);
_pygi_arg_cache_free (cache->value_cache);
g_slice_free (PyGIHashCache, cache);
}
}
static gboolean
_pygi_marshal_from_py_ghash (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg,
gpointer *cleanup_data)
{
PyGIMarshalFromPyFunc key_from_py_marshaller;
PyGIMarshalFromPyFunc value_from_py_marshaller;
int i;
Py_ssize_t length;
PyObject *py_keys, *py_values;
GHashFunc hash_func;
GEqualFunc equal_func;
GHashTable *hash_ = NULL;
PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache;
if (py_arg == Py_None) {
arg->v_pointer = NULL;
return TRUE;
}
py_keys = PyMapping_Keys (py_arg);
if (py_keys == NULL) {
PyErr_Format (PyExc_TypeError, "Must be mapping, not %s",
py_arg->ob_type->tp_name);
return FALSE;
}
length = PyMapping_Length (py_arg);
if (length < 0) {
Py_DECREF (py_keys);
return FALSE;
}
py_values = PyMapping_Values (py_arg);
if (py_values == NULL) {
Py_DECREF (py_keys);
return FALSE;
}
key_from_py_marshaller = hash_cache->key_cache->from_py_marshaller;
value_from_py_marshaller = hash_cache->value_cache->from_py_marshaller;
switch (hash_cache->key_cache->type_tag) {
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
hash_func = g_str_hash;
equal_func = g_str_equal;
break;
default:
hash_func = NULL;
equal_func = NULL;
}
hash_ = g_hash_table_new (hash_func, equal_func);
if (hash_ == NULL) {
PyErr_NoMemory ();
Py_DECREF (py_keys);
Py_DECREF (py_values);
return FALSE;
}
for (i = 0; i < length; i++) {
GIArgument key, value;
gpointer key_cleanup_data = NULL;
gpointer value_cleanup_data = NULL;
PyObject *py_key = PyList_GET_ITEM (py_keys, i);
PyObject *py_value = PyList_GET_ITEM (py_values, i);
if (py_key == NULL || py_value == NULL)
goto err;
if (!key_from_py_marshaller ( state,
callable_cache,
hash_cache->key_cache,
py_key,
&key,
&key_cleanup_data))
goto err;
if (!value_from_py_marshaller ( state,
callable_cache,
hash_cache->value_cache,
py_value,
&value,
&value_cleanup_data))
goto err;
g_hash_table_insert (hash_,
_pygi_arg_to_hash_pointer (&key, hash_cache->key_cache->type_tag),
_pygi_arg_to_hash_pointer (&value, hash_cache->value_cache->type_tag));
continue;
err:
/* FIXME: cleanup hash keys and values */
Py_XDECREF (py_key);
Py_XDECREF (py_value);
Py_DECREF (py_keys);
Py_DECREF (py_values);
g_hash_table_unref (hash_);
_PyGI_ERROR_PREFIX ("Item %i: ", i);
return FALSE;
}
arg->v_pointer = hash_;
if (arg_cache->transfer == GI_TRANSFER_NOTHING) {
/* Free everything in cleanup. */
*cleanup_data = arg->v_pointer;
} else if (arg_cache->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 = g_hash_table_ref (arg->v_pointer);
} else { /* GI_TRANSFER_EVERYTHING */
/* No cleanup, everything is given to the callee.
* Note that the keys and values will leak for transfer everything because
* we do not use g_hash_table_new_full and set key/value_destroy_func. */
*cleanup_data = NULL;
}
return TRUE;
}
static void
_pygi_marshal_cleanup_from_py_ghash (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed)
{
if (data == NULL)
return;
if (was_processed) {
GHashTable *hash_;
PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache;
hash_ = (GHashTable *)data;
/* clean up keys and values first */
if (hash_cache->key_cache->from_py_cleanup != NULL ||
hash_cache->value_cache->from_py_cleanup != NULL) {
GHashTableIter hiter;
gpointer key;
gpointer value;
PyGIMarshalCleanupFunc key_cleanup_func =
hash_cache->key_cache->from_py_cleanup;
PyGIMarshalCleanupFunc value_cleanup_func =
hash_cache->value_cache->from_py_cleanup;
g_hash_table_iter_init (&hiter, hash_);
while (g_hash_table_iter_next (&hiter, &key, &value)) {
if (key != NULL && key_cleanup_func != NULL)
key_cleanup_func (state,
hash_cache->key_cache,
NULL,
key,
TRUE);
if (value != NULL && value_cleanup_func != NULL)
value_cleanup_func (state,
hash_cache->value_cache,
NULL,
value,
TRUE);
}
}
g_hash_table_unref (hash_);
}
}
static PyObject *
_pygi_marshal_to_py_ghash (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
GIArgument *arg)
{
GHashTable *hash_;
GHashTableIter hash_table_iter;
PyGIMarshalToPyFunc key_to_py_marshaller;
PyGIMarshalToPyFunc value_to_py_marshaller;
PyGIArgCache *key_arg_cache;
PyGIArgCache *value_arg_cache;
PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache;
GIArgument key_arg;
GIArgument value_arg;
PyObject *py_obj = NULL;
hash_ = arg->v_pointer;
if (hash_ == NULL) {
py_obj = Py_None;
Py_INCREF (py_obj);
return py_obj;
}
py_obj = PyDict_New ();
if (py_obj == NULL)
return NULL;
key_arg_cache = hash_cache->key_cache;
key_to_py_marshaller = key_arg_cache->to_py_marshaller;
value_arg_cache = hash_cache->value_cache;
value_to_py_marshaller = value_arg_cache->to_py_marshaller;
g_hash_table_iter_init (&hash_table_iter, hash_);
while (g_hash_table_iter_next (&hash_table_iter,
&key_arg.v_pointer,
&value_arg.v_pointer)) {
PyObject *py_key;
PyObject *py_value;
int retval;
_pygi_hash_pointer_to_arg (&key_arg, hash_cache->key_cache->type_tag);
py_key = key_to_py_marshaller ( state,
callable_cache,
key_arg_cache,
&key_arg);
if (py_key == NULL) {
Py_CLEAR (py_obj);
return NULL;
}
_pygi_hash_pointer_to_arg (&value_arg, hash_cache->value_cache->type_tag);
py_value = value_to_py_marshaller ( state,
callable_cache,
value_arg_cache,
&value_arg);
if (py_value == NULL) {
Py_CLEAR (py_obj);
Py_DECREF(py_key);
return NULL;
}
retval = PyDict_SetItem (py_obj, py_key, py_value);
Py_DECREF (py_key);
Py_DECREF (py_value);
if (retval < 0) {
Py_CLEAR (py_obj);
return NULL;
}
}
return py_obj;
}
static void
_pygi_marshal_cleanup_to_py_ghash (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *dummy,
gpointer data,
gboolean was_processed)
{
if (data == NULL)
return;
/* assume hashtable has boxed key and value */
if (arg_cache->transfer == GI_TRANSFER_EVERYTHING || arg_cache->transfer == GI_TRANSFER_CONTAINER)
g_hash_table_unref ( (GHashTable *)data);
}
static void
_arg_cache_from_py_ghash_setup (PyGIArgCache *arg_cache)
{
arg_cache->from_py_marshaller = _pygi_marshal_from_py_ghash;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_ghash;
}
static void
_arg_cache_to_py_ghash_setup (PyGIArgCache *arg_cache)
{
arg_cache->to_py_marshaller = _pygi_marshal_to_py_ghash;
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_ghash;
}
static gboolean
pygi_arg_hash_table_setup_from_info (PyGIHashCache *hc,
GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction)
{
GITypeInfo *key_type_info;
GITypeInfo *value_type_info;
GITransfer item_transfer;
if (!pygi_arg_base_setup ((PyGIArgCache *)hc, type_info, arg_info, transfer, direction))
return FALSE;
( (PyGIArgCache *)hc)->destroy_notify = (GDestroyNotify)_hash_cache_free_func;
key_type_info = g_type_info_get_param_type (type_info, 0);
value_type_info = g_type_info_get_param_type (type_info, 1);
item_transfer =
transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
hc->key_cache = _arg_cache_new (key_type_info,
NULL,
item_transfer,
direction,
0, 0,
NULL);
if (hc->key_cache == NULL) {
return FALSE;
}
hc->value_cache = _arg_cache_new (value_type_info,
NULL,
item_transfer,
direction,
0, 0,
NULL);
if (hc->value_cache == NULL) {
return FALSE;
}
g_base_info_unref( (GIBaseInfo *)key_type_info);
g_base_info_unref( (GIBaseInfo *)value_type_info);
if (direction & PYGI_DIRECTION_FROM_PYTHON) {
_arg_cache_from_py_ghash_setup ((PyGIArgCache *)hc);
}
if (direction & PYGI_DIRECTION_TO_PYTHON) {
_arg_cache_to_py_ghash_setup ((PyGIArgCache *)hc);
}
return TRUE;
}
PyGIArgCache *
pygi_arg_hash_table_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info,
GITransfer transfer,
PyGIDirection direction)
{
gboolean res = FALSE;
PyGIHashCache *hc = NULL;
hc = g_slice_new0 (PyGIHashCache);
if (hc == NULL)
return NULL;
res = pygi_arg_hash_table_setup_from_info (hc,
type_info,
arg_info,
transfer,
direction);
if (res) {
return (PyGIArgCache *)hc;
} else {
_pygi_arg_cache_free ((PyGIArgCache *)hc);
return NULL;
}
}
/* -*- Mode: C; c-basic-offset: 4 -*-
* vim: tabstop=4 shiftwidth=4 expandtab
*
* Copyright (C) 2013 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_HASHTABLE_H__