python: Do not depend on access to internal data structures.
authorJustus Winter <justus@g10code.com>
Mon, 11 Jul 2016 14:34:15 +0000 (16:34 +0200)
committerJustus Winter <justus@g10code.com>
Mon, 11 Jul 2016 15:50:58 +0000 (17:50 +0200)
* lang/python/gpgme.i (gpgme_data_t): Rework so that it works without
access to the definition of 'struct gpgme_data'.
* lang/python/helpers.c (object_to_gpgme_data_t): Add assertion.

Signed-off-by: Justus Winter <justus@g10code.com>
lang/python/gpgme.i
lang/python/helpers.c

index 8dbb0c2..c1e0074 100644 (file)
 
 // Special handling for references to our objects.
 %typemap(in) gpgme_data_t DATAIN (gpgme_data_t wrapper = NULL,
-                                  PyObject *bytesio = NULL, Py_buffer view) {
+                                  PyObject *bytesio = NULL,
+                                  Py_buffer view, int have_view = 0) {
   /* If we create a temporary wrapper object, we will store it in
      wrapperN, where N is $argnum.  Here in this fragment, SWIG will
      automatically append $argnum.  */
                                        &bytesio, &view);
     if (pypointer == NULL)
       return NULL;
+    have_view = !! view.obj;
 
     /* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
 
   }
 }
 
+#if HAVE_DATA_H
+/* If we are doing an in-tree build, we can use the internal
+   representation of struct gpgme_data for an very efficient check if
+   the buffer has been modified.  */
+%{
+#include "src/data.h"  /* For struct gpgme_data.  */
+%}
+#endif
+
 %typemap(freearg) gpgme_data_t DATAIN {
   /* See whether we need to update the Python buffer.  */
-  if (resultobj && wrapper$argnum && view$argnum.buf
-      && wrapper$argnum->data.mem.buffer != NULL)
+  if (resultobj && wrapper$argnum && view$argnum.buf)
     {
-      /* The buffer is dirty.  */
-      if (view$argnum.readonly)
+      int dirty;
+      char *new_data = NULL;
+      size_t new_size;
+
+#if HAVE_DATA_H
+      new_data = wrapper$argnum->data.mem.buffer;
+      new_size = wrapper$argnum->data.mem.length;
+      dirty = new_data != NULL;
+#else
+      new_data = gpgme_data_release_and_get_mem (wrapper$argnum, &new_size);
+      wrapper$argnum = NULL;
+      dirty = new_size != view$argnum.len
+        || memcmp (new_data, view$argnum.buf, view$argnum.len);
+#endif
+
+      if (dirty)
         {
-          Py_XDECREF(resultobj);
-          resultobj = NULL;
-          PyErr_SetString(PyExc_ValueError, "cannot update read-only buffer");
-        }
-
-      /* See if we need to truncate the buffer.  */
-      if (resultobj && view$argnum.len != wrapper$argnum->data.mem.length)
-        {
-          if (bytesio$argnum == NULL)
+          /* The buffer is dirty.  */
+          if (view$argnum.readonly)
             {
               Py_XDECREF(resultobj);
               resultobj = NULL;
-              PyErr_SetString(PyExc_ValueError, "cannot resize buffer");
+              PyErr_SetString(PyExc_ValueError,
+                              "cannot update read-only buffer");
             }
-          else
+
+          /* See if we need to truncate the buffer.  */
+          if (resultobj && view$argnum.len != new_size)
             {
-              PyObject *retval;
-              PyBuffer_Release(&view$argnum);
-              retval = PyObject_CallMethod(bytesio$argnum, "truncate", "l",
-                                           (long)
-                                           wrapper$argnum->data.mem.length);
-              if (retval == NULL)
+              if (bytesio$argnum == NULL)
                 {
                   Py_XDECREF(resultobj);
                   resultobj = NULL;
+                  PyErr_SetString(PyExc_ValueError, "cannot resize buffer");
                 }
               else
                 {
-                  Py_DECREF(retval);
-
-                  retval = PyObject_CallMethod(bytesio$argnum, "getbuffer", NULL);
-                  if (retval == NULL
-                      || PyObject_GetBuffer(retval, &view$argnum,
-                                            PyBUF_SIMPLE|PyBUF_WRITABLE) < 0)
+                  PyObject *retval;
+                  PyBuffer_Release(&view$argnum);
+                  assert(view$argnum.obj == NULL);
+                  retval = PyObject_CallMethod(bytesio$argnum, "truncate",
+                                               "l", (long) new_size);
+                  if (retval == NULL)
                     {
                       Py_XDECREF(resultobj);
                       resultobj = NULL;
                     }
-
-                  Py_XDECREF(retval);
-
-                  if (resultobj && view$argnum.len
-                      != wrapper$argnum->data.mem.length)
+                  else
                     {
-                      Py_XDECREF(resultobj);
-                      resultobj = NULL;
-                      PyErr_Format(PyExc_ValueError,
-                                   "Expected buffer of length %zu, got %zi",
-                                   wrapper$argnum->data.mem.length,
-                                   view$argnum.len);
+                      Py_DECREF(retval);
+
+                      retval = PyObject_CallMethod(bytesio$argnum,
+                                                   "getbuffer", NULL);
+                      if (retval == NULL
+                          || PyObject_GetBuffer(retval, &view$argnum,
+                                           PyBUF_SIMPLE|PyBUF_WRITABLE) < 0)
+                        {
+                          Py_XDECREF(resultobj);
+                          resultobj = NULL;
+                        }
+
+                      Py_XDECREF(retval);
+
+                      if (resultobj && view$argnum.len
+                          != new_size)
+                        {
+                          Py_XDECREF(resultobj);
+                          resultobj = NULL;
+                          PyErr_Format(PyExc_ValueError,
+                                       "Expected buffer of length %zu, got %zi",
+                                       new_size,
+                                       view$argnum.len);
+                        }
                     }
                 }
             }
+          if (resultobj)
+            memcpy(view$argnum.buf, new_data, new_size);
         }
-
-      if (resultobj)
-        memcpy(view$argnum.buf, wrapper$argnum->data.mem.buffer,
-               wrapper$argnum->data.mem.length);
+#if ! HAVE_DATA_H
+      free (new_data);
+#endif
     }
 
   /* Free the temporary wrapper, if any.  */
   if (wrapper$argnum)
     gpgme_data_release(wrapper$argnum);
   Py_XDECREF (bytesio$argnum);
-  if (wrapper$argnum && view$argnum.buf)
+  if (have_view$argnum && view$argnum.buf)
     PyBuffer_Release(&view$argnum);
 }
 
    SWIG.  */
 %{
 #include <gpgme.h>
-#include "src/data.h"  /* For struct gpgme_data.  */
 %}
 
 /* This is for notations, where we want to hide the length fields, and
index 43a55d0..d6cbb88 100644 (file)
@@ -254,6 +254,7 @@ object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
       if (data != input)
         Py_DECREF(data);
 
+      assert (view->obj);
       assert (view->ndim == 1);
       assert (view->shape == NULL);
       assert (view->strides == NULL);