[Libguestfs] [libnbd PATCH 4/5] python: Make nbd.Buffer lighter-weight

Eric Blake eblake at redhat.com
Fri Jun 3 22:26:34 UTC 2022


Instead of storing a PyCapsule in _o and repeatedly doing lookups to
dereference a stored malloc'd pointer, it is easier to just directly
store a Python buffer-like object as _o.  Then, instead of using
Py_{INC,DEC}REF across the paired aio_p{read,write} and corresponding
completion function, we now rely on PyObject_GetBuffer and
ByBuffer_Release.  The use of memoryview protects us from the python
user changing the buffer out from under our feet:

$ ./run nbdsh
nbd> h.connect_command(['nbdkit','-s','memory','10'])
nbd> b = bytearray(10)
nbd> b1 = nbd.Buffer.from_bytearray(b)
nbd> h.aio_pread(b1, 0)
1
nbd> b.pop()
Traceback (most recent call last):
  File "/usr/lib64/python3.10/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
BufferError: Existing exports of data: object cannot be re-sized
nbd> h.poll(-1)
1
nbd> h.aio_command_completed(1)
True
nbd> b1 = None
nbd> b.pop()
0

This ALSO means that we're doing less copying! now that
nbd.Buffer.from_bytearray() is reusing an existing python object
instead of copying to a C malloc'd buffer, we have noticeable effects
on performance.  For example, on my machine:

$ export script='
m=1024*1024
size=h.get_size()
zero=bytearray(m)
for i in range(size // m):
  c = h.aio_pwrite(nbd.Buffer.from_bytearray(zero), m*i)
  while not h.aio_command_completed(c):
    h.poll(-1)
'
$ time nbdkit -U - null 10G --run 'nbdsh -u $uri -c "$script"'

takes 2.9s pre-patch, and 2.1s post-patch.

I noticed that nbd.Buffer(-1) changes from:
RuntimeError: length < 0
to
SystemError: Negative size passed to PyByteArray_FromStringAndSize
It would not be hard to revert that part, if needed.

Pure Python doesn't have any quick isinstance() test for whether an
object is buffer-like (only C has that, in PyObject_CheckBuffer) [1].
That makes it interesting to preserve our pre-existing semantics (from
the recent commit d477f7c7) where nbd.Buffer(int) gives uninitialized
memory, but nbd.Buffer.from_bytearray(int) relies on the
bytearray(int) constructor to give us initialized memory.  For the
constructor, I had to use C, but for .from_bytearray (which is now
slightly misnamed, oh well), I chose to stick to a python try/except
instead of deferring to a C helper function.

[1] https://stackoverflow.com/questions/30017991/check-if-an-object-supports-the-buffer-protocol-python

The generated code changes as follows:

| --- python/methods.h.bak	2022-06-03 17:10:24.533842689 -0500
| +++ python/methods.h	2022-06-03 17:10:33.833858093 -0500
| @@ -28,16 +28,11 @@
|
|  #include <assert.h>
|
| -struct py_aio_buffer {
| -  Py_ssize_t len;
| -  void *data;
| -};
| -
|  extern char **nbd_internal_py_get_string_list (PyObject *);
|  extern void nbd_internal_py_free_string_list (char **);
|  extern int nbd_internal_py_get_sockaddr (PyObject *,
|      struct sockaddr_storage *, socklen_t *);
| -extern struct py_aio_buffer *nbd_internal_py_get_aio_buffer (PyObject *);
| +extern PyObject *nbd_internal_py_get_aio_buffer (PyObject *);
|  extern PyObject *nbd_internal_py_get_nbd_buffer_type (void);
|
|  static inline struct nbd_handle *
| @@ -66,9 +61,6 @@
|  extern PyObject *nbd_internal_py_close (PyObject *self, PyObject *args);
|  extern PyObject *nbd_internal_py_display_version (PyObject *self, PyObject *args);
|  extern PyObject *nbd_internal_py_alloc_aio_buffer (PyObject *self, PyObject *args);
| -extern PyObject *nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args);
| -extern PyObject *nbd_internal_py_aio_buffer_to_bytearray (PyObject *self, PyObject *args);
| -extern PyObject *nbd_internal_py_aio_buffer_size (PyObject *self, PyObject *args);
|  extern PyObject *nbd_internal_py_aio_buffer_is_zero (PyObject *self, PyObject *args);
|  extern PyObject *nbd_internal_py_set_debug (PyObject *self, PyObject *args);
|  extern PyObject *nbd_internal_py_get_debug (PyObject *self, PyObject *args);
| --- python/methods.c.bak	2022-06-03 17:10:25.741844689 -0500
| +++ python/methods.c	2022-06-03 17:13:09.689116273 -0500
| @@ -37,7 +37,7 @@
|   */
|  struct user_data {
|    PyObject *fn;    /* Optional pointer to Python function. */
| -  PyObject *buf;   /* Optional pointer to persistent buffer. */
| +  Py_buffer view;  /* persistent buffer, if view->obj set. */
|  };
|
|  static struct user_data *
| @@ -58,7 +58,7 @@ free_user_data (void *user_data)
|
|    if (data) {
|      Py_XDECREF (data->fn);
| -    Py_XDECREF (data->buf);
| +    PyBuffer_Release(&data->view);
|      free (data);
|    }
|  }
| @@ -3163,7 +3163,7 @@ nbd_internal_py_aio_pread (PyObject *sel
|    int64_t ret;
|    PyObject *py_ret = NULL;
|    PyObject *buf; /* instance of nbd.Buffer */
| -  struct py_aio_buffer *buf_buf; /* Contents of nbd.Buffer */
| +  PyObject *buf_buf; /* Contents of nbd.Buffer */
|    uint64_t offset_u64;
|    unsigned long long offset; /* really uint64_t */
|    struct user_data *completion_user_data = NULL;
| @@ -3196,12 +3196,11 @@ nbd_internal_py_aio_pread (PyObject *sel
|    flags_u32 = flags;
|    buf_buf = nbd_internal_py_get_aio_buffer (buf);
|    if (!buf_buf) goto out;
| -  /* Increment refcount since buffer may be saved by libnbd. */
| -  Py_INCREF (buf);
| -  completion_user_data->buf = buf;
| +  if (PyObject_GetBuffer(buf_buf, &completion_user_data->view,
| +                         PyBUF_CONTIG) < 0) goto out;
|    offset_u64 = offset;
|
| -  ret = nbd_aio_pread (h, buf_buf->data, buf_buf->len, offset_u64, completion, flags_u32);
| +  ret = nbd_aio_pread (h, completion_user_data->view.buf, completion_user_data->view.len, offset_u64, completion, flags_u32);
|    completion_user_data = NULL;
|    if (ret == -1) {
|      raise_exception ();
| @@ -3210,6 +3209,7 @@ nbd_internal_py_aio_pread (PyObject *sel
|    py_ret = PyLong_FromLongLong (ret);
|
|   out:
| +  Py_XDECREF (buf_buf);
|    free_user_data (completion_user_data);
|    return py_ret;
|  }
| @@ -3222,7 +3222,7 @@ nbd_internal_py_aio_pread_structured (Py
|    int64_t ret;
|    PyObject *py_ret = NULL;
|    PyObject *buf; /* instance of nbd.Buffer */
| -  struct py_aio_buffer *buf_buf; /* Contents of nbd.Buffer */
| +  PyObject *buf_buf; /* Contents of nbd.Buffer */
|    uint64_t offset_u64;
|    unsigned long long offset; /* really uint64_t */
|    struct user_data *chunk_user_data = NULL;
| @@ -3259,9 +3259,8 @@ nbd_internal_py_aio_pread_structured (Py
|    flags_u32 = flags;
|    buf_buf = nbd_internal_py_get_aio_buffer (buf);
|    if (!buf_buf) goto out;
| -  /* Increment refcount since buffer may be saved by libnbd. */
| -  Py_INCREF (buf);
| -  completion_user_data->buf = buf;
| +  if (PyObject_GetBuffer(buf_buf, &completion_user_data->view,
| +                         PyBUF_CONTIG) < 0) goto out;
|    offset_u64 = offset;
|    chunk.user_data = chunk_user_data = alloc_user_data ();
|    if (chunk_user_data == NULL) goto out;
| @@ -3274,7 +3273,7 @@ nbd_internal_py_aio_pread_structured (Py
|    Py_INCREF (py_chunk_fn);
|    chunk_user_data->fn = py_chunk_fn;
|
| -  ret = nbd_aio_pread_structured (h, buf_buf->data, buf_buf->len, offset_u64, chunk, completion, flags_u32);
| +  ret = nbd_aio_pread_structured (h, completion_user_data->view.buf, completion_user_data->view.len, offset_u64, chunk, completion, flags_u32);
|    chunk_user_data = NULL;
|    completion_user_data = NULL;
|    if (ret == -1) {
| @@ -3284,6 +3283,7 @@ nbd_internal_py_aio_pread_structured (Py
|    py_ret = PyLong_FromLongLong (ret);
|
|   out:
| +  Py_XDECREF (buf_buf);
|    free_user_data (chunk_user_data);
|    free_user_data (completion_user_data);
|    return py_ret;
| @@ -3297,7 +3297,7 @@ nbd_internal_py_aio_pwrite (PyObject *se
|    int64_t ret;
|    PyObject *py_ret = NULL;
|    PyObject *buf; /* instance of nbd.Buffer */
| -  struct py_aio_buffer *buf_buf; /* Contents of nbd.Buffer */
| +  PyObject *buf_buf; /* Contents of nbd.Buffer */
|    uint64_t offset_u64;
|    unsigned long long offset; /* really uint64_t */
|    struct user_data *completion_user_data = NULL;
| @@ -3330,12 +3330,11 @@ nbd_internal_py_aio_pwrite (PyObject *se
|    flags_u32 = flags;
|    buf_buf = nbd_internal_py_get_aio_buffer (buf);
|    if (!buf_buf) goto out;
| -  /* Increment refcount since buffer may be saved by libnbd. */
| -  Py_INCREF (buf);
| -  completion_user_data->buf = buf;
| +  if (PyObject_GetBuffer(buf_buf, &completion_user_data->view,
| +                         PyBUF_CONTIG_RO) < 0) goto out;
|    offset_u64 = offset;
|
| -  ret = nbd_aio_pwrite (h, buf_buf->data, buf_buf->len, offset_u64, completion, flags_u32);
| +  ret = nbd_aio_pwrite (h, completion_user_data->view.buf, completion_user_data->view.len, offset_u64, completion, flags_u32);
|    completion_user_data = NULL;
|    if (ret == -1) {
|      raise_exception ();
| @@ -3344,6 +3343,7 @@ nbd_internal_py_aio_pwrite (PyObject *se
|    py_ret = PyLong_FromLongLong (ret);
|
|   out:
| +  Py_XDECREF (buf_buf);
|    free_user_data (completion_user_data);
|    return py_ret;
|  }
| --- python/nbd.py.bak	2022-06-03 17:10:23.350840729 -0500
| +++ python/nbd.py	2022-06-03 17:10:33.852858124 -0500
| @@ -128,19 +128,21 @@ class Buffer(object):
|
|      @classmethod
|      def from_bytearray(cls, ba):
| -        '''create an AIO buffer from a bytearray'''
| -        o = libnbdmod.aio_buffer_from_bytearray(ba)
| +        '''create an AIO buffer from a bytearray or other buffer'''
|          self = cls(0)
| -        self._o = o
| +        try:
| +            self._o = memoryview(ba).cast('B')
| +        except TypeError:
| +            self._o = bytearray(ba)
|          return self
|
|      def to_bytearray(self):
|          '''copy an AIO buffer into a bytearray'''
| -        return libnbdmod.aio_buffer_to_bytearray(self)
| +        return bytearray(self._o)
|
|      def size(self):
|          '''return the size of an AIO buffer'''
| -        return libnbdmod.aio_buffer_size(self)
| +        return len(self._o)
|
|      def is_zero(self, offset=0, size=-1):
|          '''
| @@ -154,7 +156,7 @@ class Buffer(object):
|          always returns true.  If size > 0, we check the interval
|          [offset..offset+size-1].
|          '''
| -        return libnbdmod.aio_buffer_is_zero(self, offset, size)
| +        return libnbdmod.aio_buffer_is_zero(self._o, offset, size)
|
|
|  class NBD(object):
---
 generator/Python.ml |  52 ++++++------
 python/handle.c     | 188 +++++++-------------------------------------
 2 files changed, 52 insertions(+), 188 deletions(-)

diff --git a/generator/Python.ml b/generator/Python.ml
index b862b44..270858f 100644
--- a/generator/Python.ml
+++ b/generator/Python.ml
@@ -34,16 +34,11 @@ let
   pr "#include <assert.h>\n";
   pr "\n";
   pr "\
-struct py_aio_buffer {
-  Py_ssize_t len;
-  void *data;
-};
-
 extern char **nbd_internal_py_get_string_list (PyObject *);
 extern void nbd_internal_py_free_string_list (char **);
 extern int nbd_internal_py_get_sockaddr (PyObject *,
     struct sockaddr_storage *, socklen_t *);
-extern struct py_aio_buffer *nbd_internal_py_get_aio_buffer (PyObject *);
+extern PyObject *nbd_internal_py_get_aio_buffer (PyObject *);
 extern PyObject *nbd_internal_py_get_nbd_buffer_type (void);

 static inline struct nbd_handle *
@@ -77,9 +72,6 @@ let
   ) ([ "create"; "close";
        "display_version";
        "alloc_aio_buffer";
-       "aio_buffer_from_bytearray";
-       "aio_buffer_to_bytearray";
-       "aio_buffer_size";
        "aio_buffer_is_zero" ] @ List.map fst handle_calls);

   pr "\n";
@@ -109,9 +101,6 @@ let
   ) ([ "create"; "close";
        "display_version";
        "alloc_aio_buffer";
-       "aio_buffer_from_bytearray";
-       "aio_buffer_to_bytearray";
-       "aio_buffer_size";
        "aio_buffer_is_zero" ] @ List.map fst handle_calls);
   pr "  { NULL, NULL, 0, NULL }\n";
   pr "};\n";
@@ -301,7 +290,7 @@ let
     | BytesPersistIn (n, _)
     | BytesPersistOut (n, _) ->
        pr "  PyObject *%s; /* instance of nbd.Buffer */\n" n;
-       pr "  struct py_aio_buffer *%s_buf; /* Contents of nbd.Buffer */\n" n
+       pr "  PyObject *%s_buf; /* Contents of nbd.Buffer */\n" n
     | Closure { cbname } ->
        pr "  struct user_data *%s_user_data = NULL;\n" cbname;
        pr "  PyObject *py_%s_fn;\n" cbname;
@@ -436,12 +425,16 @@ let
     | BytesOut (n, count) ->
        pr "  %s = PyByteArray_FromStringAndSize (NULL, %s);\n" n count;
        pr "  if (%s == NULL) goto out;\n" n
-    | BytesPersistIn (n, _) | BytesPersistOut (n, _) ->
+    | BytesPersistIn (n, _) ->
        pr "  %s_buf = nbd_internal_py_get_aio_buffer (%s);\n" n n;
        pr "  if (!%s_buf) goto out;\n" n;
-       pr "  /* Increment refcount since buffer may be saved by libnbd. */\n";
-       pr "  Py_INCREF (%s);\n" n;
-       pr "  completion_user_data->buf = %s;\n" n
+       pr "  if (PyObject_GetBuffer(%s_buf, &completion_user_data->view,\n" n;
+       pr "                         PyBUF_CONTIG_RO) < 0) goto out;\n"
+    | BytesPersistOut (n, _) ->
+       pr "  %s_buf = nbd_internal_py_get_aio_buffer (%s);\n" n n;
+       pr "  if (!%s_buf) goto out;\n" n;
+       pr "  if (PyObject_GetBuffer(%s_buf, &completion_user_data->view,\n" n;
+       pr "                         PyBUF_CONTIG) < 0) goto out;\n"
     | Closure { cbname } ->
        pr "  %s.user_data = %s_user_data = alloc_user_data ();\n" cbname cbname;
        pr "  if (%s_user_data == NULL) goto out;\n" cbname;
@@ -482,8 +475,8 @@ let
     | Bool n -> pr ", %s" n
     | BytesIn (n, _) -> pr ", %s.buf, %s.len" n n
     | BytesOut (n, count) -> pr ", PyByteArray_AS_STRING (%s), %s" n count
-    | BytesPersistIn (n, _)
-    | BytesPersistOut (n, _) -> pr ", %s_buf->data, %s_buf->len" n n
+    | BytesPersistIn _ | BytesPersistOut _ ->
+       pr ", completion_user_data->view.buf, completion_user_data->view.len"
     | Closure { cbname } -> pr ", %s" cbname
     | Enum (n, _) -> pr ", %s" n
     | Flags (n, _) -> pr ", %s_u32" n
@@ -576,7 +569,8 @@ let
        pr "  if (%s.obj)\n" n;
        pr "    PyBuffer_Release (&%s);\n" n
     | BytesOut (n, _) -> pr "  Py_XDECREF (%s);\n" n
-    | BytesPersistIn _ | BytesPersistOut _ -> ()
+    | BytesPersistIn (n, _) | BytesPersistOut (n, _) ->
+       pr "  Py_XDECREF (%s_buf);\n" n
     | Closure { cbname } ->
        pr "  free_user_data (%s_user_data);\n" cbname
     | Enum _ -> ()
@@ -625,7 +619,7 @@ let
   pr " */\n";
   pr "struct user_data {\n";
   pr "  PyObject *fn;    /* Optional pointer to Python function. */\n";
-  pr "  PyObject *buf;   /* Optional pointer to persistent buffer. */\n";
+  pr "  Py_buffer view;  /* persistent buffer, if view->obj set. */\n";
   pr "};\n";
   pr "\n";
   pr "static struct user_data *\n";
@@ -646,7 +640,7 @@ let
   pr "\n";
   pr "  if (data) {\n";
   pr "    Py_XDECREF (data->fn);\n";
-  pr "    Py_XDECREF (data->buf);\n";
+  pr "    PyBuffer_Release(&data->view);\n";
   pr "    free (data);\n";
   pr "  }\n";
   pr "}\n";
@@ -768,19 +762,21 @@ let

     @classmethod
     def from_bytearray(cls, ba):
-        '''create an AIO buffer from a bytearray'''
-        o = libnbdmod.aio_buffer_from_bytearray(ba)
+        '''create an AIO buffer from a bytearray or other buffer'''
         self = cls(0)
-        self._o = o
+        try:
+            self._o = memoryview(ba).cast('B')
+        except TypeError:
+            self._o = bytearray(ba)
         return self

     def to_bytearray(self):
         '''copy an AIO buffer into a bytearray'''
-        return libnbdmod.aio_buffer_to_bytearray(self)
+        return bytearray(self._o)

     def size(self):
         '''return the size of an AIO buffer'''
-        return libnbdmod.aio_buffer_size(self)
+        return len(self._o)

     def is_zero(self, offset=0, size=-1):
         '''
@@ -794,7 +790,7 @@ let
         always returns true.  If size > 0, we check the interval
         [offset..offset+size-1].
         '''
-        return libnbdmod.aio_buffer_is_zero(self, offset, size)
+        return libnbdmod.aio_buffer_is_zero(self._o, offset, size)


 class NBD(object):
diff --git a/python/handle.c b/python/handle.c
index f84c6e0..7f67159 100644
--- a/python/handle.c
+++ b/python/handle.c
@@ -98,205 +98,73 @@ nbd_internal_py_display_version (PyObject *self, PyObject *args)

 static const char aio_buffer_name[] = "nbd.Buffer";

-struct py_aio_buffer *
+PyObject *
 nbd_internal_py_get_aio_buffer (PyObject *buffer)
 {
-  if (PyObject_IsInstance (buffer, nbd_internal_py_get_nbd_buffer_type ())) {
-    PyObject *capsule = PyObject_GetAttrString(buffer, "_o");
-    return PyCapsule_GetPointer (capsule, aio_buffer_name);
-  }
+  if (PyObject_IsInstance (buffer, nbd_internal_py_get_nbd_buffer_type ()))
+    return PyObject_GetAttrString(buffer, "_o");

   PyErr_SetString (PyExc_TypeError,
                    "aio_buffer: expecting nbd.Buffer instance");
   return NULL;
 }

-static void
-free_aio_buffer (PyObject *capsule)
-{
-  struct py_aio_buffer *buf = PyCapsule_GetPointer (capsule, aio_buffer_name);
-
-  if (buf)
-    free (buf->data);
-  free (buf);
-}
-
 /* Allocate a persistent buffer used for nbd_aio_pread. */
 PyObject *
 nbd_internal_py_alloc_aio_buffer (PyObject *self, PyObject *args)
 {
-  struct py_aio_buffer *buf;
-  PyObject *ret;
-
-  buf = malloc (sizeof *buf);
-  if (buf == NULL) {
-    PyErr_NoMemory ();
-    return NULL;
-  }
-
-  if (!PyArg_ParseTuple (args, (char *) "n:nbd_internal_py_alloc_aio_buffer",
-                         &buf->len)) {
-    free (buf);
-    return NULL;
-  }
-
-  if (buf->len < 0) {
-    PyErr_SetString (PyExc_RuntimeError, "length < 0");
-    free (buf);
-    return NULL;
-  }
-  buf->data = malloc (buf->len);
-  if (buf->data == NULL) {
-    PyErr_NoMemory ();
-    free (buf);
-    return NULL;
-  }
-
-  ret = PyCapsule_New (buf, aio_buffer_name, free_aio_buffer);
-  if (ret == NULL) {
-    free (buf->data);
-    free (buf);
-    return NULL;
-  }
-
-  return ret;
-}
-
-PyObject *
-nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args)
-{
-  PyObject *obj;
-  PyObject *arr = NULL;
   Py_ssize_t len;
-  void *data;
-  struct py_aio_buffer *buf;
-  PyObject *ret;

-  if (!PyArg_ParseTuple (args,
-                         (char *) "O:nbd_internal_py_aio_buffer_from_bytearray",
-                         &obj))
+  if (!PyArg_ParseTuple (args, (char *) "n:nbd_internal_py_alloc_aio_buffer",
+                         &len))
     return NULL;

-  if (! PyByteArray_Check (obj)) {
-    arr = PyByteArray_FromObject (obj);
-    if (arr == NULL)
-      return NULL;
-    obj = arr;
-  }
-  data = PyByteArray_AsString (obj);
-  if (!data) {
-    PyErr_SetString (PyExc_RuntimeError,
-                     "parameter is not a bytearray or buffer");
-    Py_XDECREF (arr);
-    return NULL;
-  }
-  len = PyByteArray_Size (obj);
-
-  buf = malloc (sizeof *buf);
-  if (buf == NULL) {
-    PyErr_NoMemory ();
-    Py_XDECREF (arr);
-    return NULL;
-  }
-
-  buf->len = len;
-  buf->data = malloc (len);
-  if (buf->data == NULL) {
-    PyErr_NoMemory ();
-    free (buf);
-    Py_XDECREF (arr);
-    return NULL;
-  }
-  memcpy (buf->data, data, len);
-  Py_XDECREF (arr);
-
-  ret = PyCapsule_New (buf, aio_buffer_name, free_aio_buffer);
-  if (ret == NULL) {
-    free (buf->data);
-    free (buf);
-    return NULL;
-  }
-
-  return ret;
-}
-
-PyObject *
-nbd_internal_py_aio_buffer_to_bytearray (PyObject *self, PyObject *args)
-{
-  PyObject *obj;
-  struct py_aio_buffer *buf;
-
-  if (!PyArg_ParseTuple (args,
-                         (char *) "O:nbd_internal_py_aio_buffer_to_bytearray",
-                         &obj))
-    return NULL;
-
-  buf = nbd_internal_py_get_aio_buffer (obj);
-  if (buf == NULL)
-    return NULL;
-
-  return PyByteArray_FromStringAndSize (buf->data, buf->len);
-}
-
-PyObject *
-nbd_internal_py_aio_buffer_size (PyObject *self, PyObject *args)
-{
-  PyObject *obj;
-  struct py_aio_buffer *buf;
-
-  if (!PyArg_ParseTuple (args,
-                         (char *) "O:nbd_internal_py_aio_buffer_size",
-                         &obj))
-    return NULL;
-
-  buf = nbd_internal_py_get_aio_buffer (obj);
-  if (buf == NULL)
-    return NULL;
-
-  return PyLong_FromSsize_t (buf->len);
+  /* Constructing bytearray(len) in python zeroes the memory; doing it this
+   * way gives uninitialized memory.  This correctly flags negative len.
+   */
+  return PyByteArray_FromStringAndSize (NULL, len);
 }

 PyObject *
 nbd_internal_py_aio_buffer_is_zero (PyObject *self, PyObject *args)
 {
-  PyObject *obj;
-  struct py_aio_buffer *buf;
+  Py_buffer buf;
   Py_ssize_t offset, size;
+  PyObject *ret = NULL;

   if (!PyArg_ParseTuple (args,
-                         (char *) "Onn:nbd_internal_py_aio_buffer_is_zero",
-                         &obj, &offset, &size))
+                         (char *) "y*nn:nbd_internal_py_aio_buffer_is_zero",
+                         &buf, &offset, &size))
     return NULL;

-  if (size == 0)
-    Py_RETURN_TRUE;
-
-  buf = nbd_internal_py_get_aio_buffer (obj);
-  if (buf == NULL)
-    return NULL;
+  if (size == 0) {
+    ret = Py_True;
+    Py_INCREF (ret);
+    goto out;
+  }

   /* Check the bounds of the offset. */
-  if (offset < 0 || offset > buf->len) {
+  if (offset < 0 || offset > buf.len) {
     PyErr_SetString (PyExc_IndexError, "offset out of range");
-    return NULL;
+    goto out;
   }

   /* Compute or check the length. */
   if (size == -1)
-    size = buf->len - offset;
+    size = buf.len - offset;
   else if (size < 0) {
     PyErr_SetString (PyExc_IndexError,
                      "size cannot be negative, "
                      "except -1 to mean to the end of the buffer");
-    return NULL;
+    goto out;
   }
-  else if ((size_t) offset + size > buf->len) {
+  else if ((size_t) offset + size > buf.len) {
     PyErr_SetString (PyExc_IndexError, "size out of range");
-    return NULL;
+    goto out;
   }

-  if (is_zero (buf->data + offset, size))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
+  ret = PyBool_FromLong (is_zero (buf.buf + offset, size));
+ out:
+  PyBuffer_Release(&buf);
+  return ret;
 }
-- 
2.36.1



More information about the Libguestfs mailing list