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
6 changes: 6 additions & 0 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@

## Unreleased

### Fixes

* `Fletcher32.decode` now raises `ValueError` when a user-provided `out` buffer is not
exactly the size of the decoded payload, instead of writing past the end of a smaller
buffer or leaving the tail of a larger one untouched.

### Maintenance

* Build wheels for CPython 3.15 and free-threaded CPython 3.15t, using cibuildwheel 4.3.5,
Expand Down
12 changes: 9 additions & 3 deletions src/numcodecs/fletcher32.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,17 @@ class Fletcher32(Codec):
)

cdef uint8_t[::1] out_mv
cdef uint8_t* out_ptr
cdef Py_ssize_t payload_len
if out is not None:
out_mv = ensure_contiguous_ndarray(out).view("uint8")
out_ptr = &out_mv[0]
memcpy(out_ptr, b_ptr, b_len - FOOTER_LENGTH)
payload_len = b_len - FOOTER_LENGTH
if out_mv.size != payload_len:
raise ValueError(
f"Output buffer has {out_mv.size} bytes, but the"
f" decoded payload has {payload_len} bytes."
)
if payload_len:
memcpy(&out_mv[0], b_ptr, payload_len)
else:
out = b_mv[:-FOOTER_LENGTH]
return out
23 changes: 19 additions & 4 deletions src/numcodecs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ endif
have_sse2 = false
have_avx2 = false

# MSVC does not accept -msse2/-mavx2. It enables SSE2 by default on x86_64 and
# opts into AVX2 with /arch:AVX2, so probe the flag each compiler accepts.
msvc = cc.get_id() == 'msvc'
if msvc
sse2_flag = '/arch:SSE2'
avx2_flag = '/arch:AVX2'
else
sse2_flag = '-msse2'
avx2_flag = '-mavx2'
endif

if host_machine.cpu_family() == 'x86_64'
# Honor legacy environment variables for backwards compatibility with
# existing build scripts and packaging recipes.
Expand All @@ -28,13 +39,14 @@ if host_machine.cpu_family() == 'x86_64'
if get_option('sse2').disabled() or disable_sse2_env
have_sse2 = false
else
have_sse2 = cc.has_argument('-msse2')
# x86_64 MSVC enables SSE2 by default, so there is nothing to probe.
have_sse2 = msvc or cc.has_argument(sse2_flag)
endif

if get_option('avx2').disabled() or disable_avx2_env
have_avx2 = false
else
have_avx2 = cc.has_argument('-mavx2')
have_avx2 = cc.has_argument(avx2_flag)
endif
endif

Expand Down Expand Up @@ -176,7 +188,10 @@ if not blosc_dep.found()
'../../c-blosc/blosc/shuffle-sse2.c',
'../../c-blosc/blosc/bitshuffle-sse2.c',
)
blosc_c_args += ['-DSHUFFLE_SSE2_ENABLED', '-msse2']
blosc_c_args += ['-DSHUFFLE_SSE2_ENABLED']
if not msvc
blosc_c_args += [sse2_flag]
endif
if host_machine.system() == 'windows'
blosc_c_args += ['-D__SSE2__']
endif
Expand All @@ -187,7 +202,7 @@ if not blosc_dep.found()
'../../c-blosc/blosc/shuffle-avx2.c',
'../../c-blosc/blosc/bitshuffle-avx2.c',
)
blosc_c_args += ['-DSHUFFLE_AVX2_ENABLED', '-mavx2']
blosc_c_args += ['-DSHUFFLE_AVX2_ENABLED', avx2_flag]
if host_machine.system() == 'windows'
blosc_c_args += ['-D__AVX2__']
endif
Expand Down
41 changes: 41 additions & 0 deletions tests/test_fletcher32.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,44 @@ def test_out():
f = Fletcher32()
result = f.encode(data)
f.decode(result, out=data)


def test_out_too_large():
data = np.arange(16, dtype="uint8")
f = Fletcher32()
enc = f.encode(data)
out = np.full(data.size + 4, 0xAA, dtype="uint8")
with pytest.raises(ValueError, match="Output buffer has 20 bytes"):
f.decode(enc, out=out)
# the destination buffer must be left untouched
assert (out == 0xAA).all()


def test_out_too_small():
data = np.arange(16, dtype="uint8")
f = Fletcher32()
enc = f.encode(data)
out = np.full(data.size - 1, 0xAA, dtype="uint8")
with pytest.raises(ValueError, match="Output buffer has 15 bytes"):
f.decode(enc, out=out)
assert (out == 0xAA).all()


def test_out_correct_size():
data = np.arange(16, dtype="uint8")
f = Fletcher32()
enc = f.encode(data)
out = np.empty(data.size, dtype="uint8")
result = f.decode(enc, out=out)
assert result is out
assert (out == data).all()


def test_out_bytearray():
data = np.arange(16, dtype="uint8")
f = Fletcher32()
enc = f.encode(data)
out = bytearray(data.size)
result = f.decode(enc, out=out)
assert result is out
assert out == data.tobytes()
Loading