diff --git a/neo/rawio/blackrockrawio.py b/neo/rawio/blackrockrawio.py index 83d1a2771..ee3920820 100644 --- a/neo/rawio/blackrockrawio.py +++ b/neo/rawio/blackrockrawio.py @@ -1683,7 +1683,8 @@ def _read_nev_data(self, spec, filename): The NEV filename to read from """ packet_size_bytes = self._nev_basic_header["bytes_in_data_packets"] - header_size = self._nev_basic_header["bytes_in_headers"] + # int(): a uint32 scalar overflows against file sizes > 4 GiB under NumPy 2 (NEP 50) + header_size = int(self._nev_basic_header["bytes_in_headers"]) if self._nev_basic_header["ver_major"] >= 3: ts_format = "uint64" @@ -1995,7 +1996,7 @@ def _get_nev_rec_times(self): filename = ".".join([self._filenames["nev"], "nev"]) dt = [("timestamp", "uint32")] - offset = self._get_file_size(filename) - self._nev_params("bytes_in_data_packets") + offset = self._get_file_size(filename) - int(self._nev_params("bytes_in_data_packets")) last_data_packet = np.memmap(filename, offset=offset, dtype=dt, mode="r")[0] n_starts = [0 * self._nev_params("event_unit")] diff --git a/neo/test/rawiotest/test_blackrockrawio.py b/neo/test/rawiotest/test_blackrockrawio.py index 03c3481ae..20e69e3c9 100644 --- a/neo/test/rawiotest/test_blackrockrawio.py +++ b/neo/test/rawiotest/test_blackrockrawio.py @@ -3,6 +3,7 @@ """ import unittest +from unittest import mock from neo.rawio.blackrockrawio import BlackrockRawIO from neo.test.rawiotest.common_rawio_test import BaseTestRawIO @@ -374,6 +375,37 @@ def test_gap_tolerance_ms_parameter(self): segments_strict = reader_strict.segment_count(0) self.assertEqual(segments_strict, 3) # + def test_nev_over_4_gib_does_not_overflow(self): + """File sizes > 4 GiB must not overflow against the uint32 NEV header fields (NumPy 2, NEP 50).""" + file_size = 5 * 2**30 + basic_header = np.zeros( + 1, dtype=[("ver_major", "uint8"), ("bytes_in_headers", "uint32"), ("bytes_in_data_packets", "uint32")] + )[0] + basic_header["ver_major"] = 2 + basic_header["bytes_in_headers"] = 1000 + basic_header["bytes_in_data_packets"] = 104 + + reader = BlackrockRawIO.__new__(BlackrockRawIO) # skip parsing real files + reader._nev_basic_header = basic_header + reader._nev_params = lambda name: basic_header[name] + reader._filenames = {"nev": "fake"} + + class Stop(Exception): + pass + + with ( + mock.patch.object(reader, "_get_file_size", return_value=file_size), + mock.patch("neo.rawio.blackrockrawio.np.memmap", side_effect=Stop) as memmap, + ): + with self.assertRaises(Stop): + reader._read_nev_data("2.2", "fake.nev") + self.assertEqual(memmap.call_args.kwargs["offset"], 1000) + self.assertEqual(memmap.call_args.kwargs["shape"], ((file_size - 1000) // 104,)) + + with self.assertRaises(Stop): + reader._get_nev_rec_times() + self.assertEqual(memmap.call_args.kwargs["offset"], file_size - 104) + if __name__ == "__main__": unittest.main()