On Fri, Jun 03, 2022 at 05:26:34PM -0500, Eric Blake wrote:
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.
The new message isn't difficult to understand.
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 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);
Could use pr_wrap ',' to wrap this nicely, although the old code
wasn't wrapped nicely either.
| 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(-)
Nice simplification!
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
_______________________________________________
Libguestfs mailing list
Libguestfs(a)redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs
Reviewed-by: Richard W.M. Jones <rjones(a)redhat.com>
--
Richard Jones, Virtualization Group, Red Hat
http://people.redhat.com/~rjones
Read my programming and virtualization blog:
http://rwmj.wordpress.com
nbdkit - Flexible, fast NBD server with plugins
https://gitlab.com/nbdkit/nbdkit