Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 35 additions & 19 deletions src/blosc2/blosc2_ext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import cython
from cpython cimport (
Py_buffer,
PyBUF_SIMPLE,
PyBUF_WRITABLE,
PyBuffer_Release,
PyBytes_AsString,
PyBytes_FromStringAndSize,
Expand Down Expand Up @@ -3364,6 +3365,18 @@ cpdef compress(src, int32_t typesize=8, int clevel=9, filter=blosc2.Filter.SHUFF
raise ValueError("Cannot compress")


cdef int32_t _checked_dst_size(Py_ssize_t dst_len, int32_t nbytes) except -1:
"""The destsize for decompressing into a dst of `dst_len` bytes: its length, up to the INT_MAX that
C-Blosc2 takes. C-Blosc2 refuses a chunk larger than destsize; this raises the documented errors first,
for an empty dst or one smaller than `nbytes` (the header's size of the decompressed data)."""
if dst_len == 0:
raise ValueError("The dst length must be greater than 0")
if dst_len < nbytes:
raise RuntimeError(f"The dst length ({dst_len} bytes) is smaller than the decompressed size of src "
f"({nbytes} bytes)")
return <int32_t>min(dst_len, INT_MAX)


def decompress(src, dst=None, as_bytearray=False):
cdef int32_t nbytes
cdef int32_t cbytes
Expand All @@ -3374,18 +3387,20 @@ def decompress(src, dst=None, as_bytearray=False):
typed_view_src = mem_view_src.cast('B')
_check_comp_length('src', len(typed_view_src))
blosc2_cbuffer_sizes(<void*>&typed_view_src[0], &nbytes, &cbytes, &blocksize)
cdef int32_t srcsize = <int32_t>min(len(typed_view_src), INT_MAX)
cdef Py_buffer buf
if dst is not None:
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE)
if buf.len == 0:
raise ValueError("The dst length must be greater than 0")
size = blosc1_decompress(<void*>&typed_view_src[0], buf.buf, buf.len)
PyBuffer_Release(&buf)
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE | PyBUF_WRITABLE)
try:
size = blosc2_decompress(<void*>&typed_view_src[0], srcsize, buf.buf,
_checked_dst_size(buf.len, nbytes))
finally:
PyBuffer_Release(&buf)
else:
dst = PyBytes_FromStringAndSize(NULL, nbytes)
if dst is None:
raise RuntimeError("Could not get a bytes object")
size = blosc1_decompress(<void*>&typed_view_src[0], <void*> <char *> dst, len(dst))
size = blosc2_decompress(<void*>&typed_view_src[0], srcsize, <void*> <char *> dst, nbytes)
if size >= 0:
return bytearray(dst) if as_bytearray else dst
if size < 0:
Expand Down Expand Up @@ -3720,20 +3735,21 @@ def decompress2(src, dst=None, **kwargs):
cdef int32_t nbytes
cdef int32_t cbytes
cdef int32_t blocksize
cdef int32_t srcsize = <int32_t>typed_view_src.nbytes
cdef int32_t srcsize = <int32_t>min(len(typed_view_src), INT_MAX)
blosc2_cbuffer_sizes(<void*>&typed_view_src[0], &nbytes, &cbytes, &blocksize)
cdef Py_buffer buf
if dst is not None:
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE)
if buf.len == 0:
blosc2_free_ctx(dctx)
raise ValueError("The dst length must be greater than 0")
view = <void*>&typed_view_src[0]
# For lazy chunks, blosc2_cbuffer_sizes() only reports the header cbytes.
# The decode context needs the full source buffer length.
size = _decompress_ctx(dctx, view, srcsize, buf.buf, nbytes)
blosc2_free_ctx(dctx)
PyBuffer_Release(&buf)
try:
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE | PyBUF_WRITABLE)
try:
size = _decompress_ctx(dctx, view, srcsize, buf.buf, _checked_dst_size(buf.len, nbytes))
finally:
PyBuffer_Release(&buf)
finally:
blosc2_free_ctx(dctx)
else:
dst = PyBytes_FromStringAndSize(NULL, nbytes)
if dst is None:
Expand Down Expand Up @@ -3875,15 +3891,15 @@ def vldecompress_block(src, int32_t nblock, **kwargs):
cdef blosc2_dparams dparams
create_dparams_from_kwargs(&dparams, kwargs)

cdef blosc2_context *dctx = blosc2_create_dctx(dparams)
if dctx == NULL:
raise RuntimeError("Could not create decompression context")

cdef const uint8_t[:] typed_view_src
mem_view_src = memoryview(src)
typed_view_src = mem_view_src.cast('B')
_check_comp_length('src', typed_view_src.nbytes)

cdef blosc2_context *dctx = blosc2_create_dctx(dparams)
if dctx == NULL:
raise RuntimeError("Could not create decompression context")

cdef uint8_t *dest = NULL
cdef int32_t destsize = 0
cdef int32_t rc
Expand Down Expand Up @@ -4424,7 +4440,7 @@ cdef class SChunk:
cdef int32_t blocksize
cdef Py_buffer buf
if dst is not None:
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE)
PyObject_GetBuffer(dst, &buf, PyBUF_SIMPLE | PyBUF_WRITABLE)
if buf.len == 0:
raise ValueError("The dst length must be greater than 0")
size = blosc2_schunk_decompress_chunk(self.schunk, nchunk, buf.buf, <int32_t>buf.len)
Expand Down
19 changes: 11 additions & 8 deletions src/blosc2/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def decompress(
dst: NumPy object or bytearray
The destination NumPy object or bytearray to fill,
the length of which must be greater than 0.
The user must ensure it has enough capacity to hold
It must be writable and have enough capacity to hold
the decompressed data.
Default is None, meaning that a new `bytes` or `bytearray` object
is created, filled and returned.
Expand All @@ -188,14 +188,16 @@ def decompress(

Raises
------
BufferError
Raised if :paramref:`dst` is read-only, like a `bytes` object or a read-only memoryview.
RuntimeError
Raised if the compressed data is corrupted or the output buffer is not large enough.
Also raised if a `bytes` object could not be obtained.
TypeError
Raised if :paramref:`src` does not support the Buffer Protocol.
ValueError
Raised if the length of :paramref:`src` is smaller than the minimum required length.
Also raised if `dst` is not `None` and its length is 0.
Also raised if `dst` is not `None` and its length is 0, or if it is a read-only NumPy array.

Examples
--------
Expand Down Expand Up @@ -2247,10 +2249,9 @@ def decompress2(src: object, dst: object | bytearray = None, **kwargs: dict) ->
bytearray, memoryview, or numpy.ndarray.
dst: NumPy object or bytearray, optional
The destination NumPy object or bytearray to fill. The length
must be greater than 0. The user must ensure
it has enough capacity for the decompressed
data. Default is `None`, meaning a new bytes object
is created, filled and returned.
must be greater than 0. It must be writable and have enough
capacity for the decompressed data. Default is `None`,
meaning a new bytes object is created, filled and returned.

kwargs: dict, optional
Decompression parameters. The default values are in :class:`blosc2.DParams`.
Expand All @@ -2272,15 +2273,17 @@ def decompress2(src: object, dst: object | bytearray = None, **kwargs: dict) ->

Raises
------
BufferError
If :paramref:`dst` is read-only, like a `bytes` object or a read-only memoryview.
RuntimeError
If the data cannot be compressed into :paramref:`dst`.
If the data cannot be decompressed into :paramref:`dst`.
If an internal error occurs, likely due to an invalid parameter
If :paramref:`dst` is `None` and a bytes object could not be created to store the result.
TypeError
If :paramref:`src` does not support the Buffer Protocol.
ValueError
If the length of :paramref:`src` is smaller than the minimum.
If :paramref:`dst` is not None and its length is 0.
If :paramref:`dst` is not None and its length is 0, or if it is a read-only NumPy array.
"""
if kwargs is not None and "dparams" in kwargs:
if len(kwargs) > 1:
Expand Down
8 changes: 6 additions & 2 deletions src/blosc2/schunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,8 +860,8 @@ def decompress_chunk(self, nchunk: int, dst: object = None) -> str | bytes:
The index of the chunk that will be decompressed.
dst: NumPy object or bytearray
The destination NumPy object or bytearray to fill, the length
of which must be greater than 0. The user must ensure
that it has enough capacity to host the decompressed
of which must be greater than 0. It must be writable and
have enough capacity to host the decompressed
chunk. Default is None, meaning that a new bytes object
is created, filled and returned.

Expand All @@ -874,8 +874,12 @@ def decompress_chunk(self, nchunk: int, dst: object = None) -> str | bytes:

Raises
------
BufferError
If :paramref:`dst` is read-only, like a `bytes` object or a read-only memoryview.
RunTimeError
If a problem is detected.
ValueError
If the length of :paramref:`dst` is 0, or if it is a read-only NumPy array.

Examples
--------
Expand Down
Loading
Loading