[pygobject] Avoid copying bytearrays from Python to C when transfer nothing



commit 60de632153a693fb2b0f2ce26208c6ff668bdf4d
Author: Garrett Regier <garrett regier riftio com>
Date:   Tue Jan 27 10:39:53 2015 -0800

    Avoid copying bytearrays from Python to C when transfer nothing
    
    https://bugzilla.gnome.org/show_bug.cgi?id=743278

 gi/pygi-array.c  |   23 +++++++++++++++++++----
 tests/test_gi.py |   14 +++++++++++++-
 2 files changed, 32 insertions(+), 5 deletions(-)
---
diff --git a/gi/pygi-array.c b/gi/pygi-array.c
index e2598cd..121992b 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 31b31f5..f85d60f 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):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]