summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Regier <garrett.regier@riftio.com>2015-01-27 10:39:53 -0800
committerGarrett Regier <garrett.regier@riftio.com>2015-02-16 11:37:48 -0800
commit60de632153a693fb2b0f2ce26208c6ff668bdf4d (patch)
tree5c26aa02171e283deda066383b5e525069fd7129
parenta4160892dd28ab1d656cef4d4059f3b3f95caf4a (diff)
downloadpygobject-60de632153a693fb2b0f2ce26208c6ff668bdf4d.tar.gz
Avoid copying bytearrays from Python to C when transfer nothing
https://bugzilla.gnome.org/show_bug.cgi?id=743278
-rw-r--r--gi/pygi-array.c23
-rw-r--r--tests/test_gi.py14
2 files changed, 32 insertions, 5 deletions
diff --git a/gi/pygi-array.c b/gi/pygi-array.c
index e2598cd3..121992b6 100644
--- a/gi/pygi-array.c
+++ b/gi/pygi-array.c
@@ -191,6 +191,7 @@ _pygi_marshal_from_py_array (PyGIInvokeState *state,
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) {
@@ -234,7 +235,21 @@ _pygi_marshal_from_py_array (PyGIInvokeState *state,
if (sequence_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8 &&
PYGLIB_PyBytes_Check (py_arg)) {
- memcpy(array_->data, PYGLIB_PyBytes_AsString (py_arg), length);
+ gchar *data = PYGLIB_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
@@ -385,7 +400,7 @@ array_success:
*/
arg->v_pointer = array_->data;
- if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) {
+ if (cleanup_transfer == GI_TRANSFER_EVERYTHING) {
g_array_free (array_, FALSE);
*cleanup_data = NULL;
} else {
@@ -394,10 +409,10 @@ array_success:
} else {
arg->v_pointer = array_;
- if (arg_cache->transfer == GI_TRANSFER_NOTHING) {
+ if (cleanup_transfer == GI_TRANSFER_NOTHING) {
/* Free everything in cleanup. */
*cleanup_data = array_;
- } else if (arg_cache->transfer == GI_TRANSFER_CONTAINER) {
+ } 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 ?
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 31b31f58..f85d60fb 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1089,7 +1089,19 @@ class TestGByteArray(unittest.TestCase):
self.assertEqual(b'\x001\xFF3', GIMarshallingTests.bytearray_full_return())
def test_bytearray_none_in(self):
- GIMarshallingTests.bytearray_none_in(b'\x00\x31\xFF\x33')
+ b = b'\x00\x31\xFF\x33'
+ ba = GLib.ByteArray.new_take(b)
+
+ # b should always have the same value even
+ # though the generated GByteArray is being modified
+ GIMarshallingTests.bytearray_none_in(b)
+ GIMarshallingTests.bytearray_none_in(b)
+
+ # The GByteArray is just a bytes
+ # thus it will not reflect any changes
+ GIMarshallingTests.bytearray_none_in(ba)
+ GIMarshallingTests.bytearray_none_in(ba)
+ #self.assertEqual(ba, b'\x00\x31\x00\x33')
class TestGList(unittest.TestCase):