From c5a55416965d1d5f1aa0a53f1a4d24ab0a4a3e0b Mon Sep 17 00:00:00 2001 From: Eivind Jahren Date: Wed, 4 Aug 2021 08:03:03 +0200 Subject: [PATCH] Fix bug with incorrect array length in read --- src/ecl_data_io/_unformatted/common.py | 16 ++++++++++++++ src/ecl_data_io/_unformatted/read.py | 20 ++++++++---------- tests/test_formatted_common.py | 29 +++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/ecl_data_io/_unformatted/common.py b/src/ecl_data_io/_unformatted/common.py index 53c61fb..fd95968 100644 --- a/src/ecl_data_io/_unformatted/common.py +++ b/src/ecl_data_io/_unformatted/common.py @@ -29,3 +29,19 @@ def item_size(type_keyword): if type_keyword[0:2] == b"C0": return int(type_keyword[2:4].decode("ascii")) return static_item_sizes.get(type_keyword, None) + + +def bytes_in_array(array_length, item_type): + """ + :param array_length: Number of items in the array + :param item_type: Type of items in the array + :returns: Number of bytes used to store an array of + given type and length + """ + g_len = group_len(item_type) + full_groups = array_length // g_len + + if array_length % g_len: + return (full_groups + 1) * 8 + array_length * item_size(item_type) + else: + return full_groups * 8 + array_length * item_size(item_type) diff --git a/src/ecl_data_io/_unformatted/read.py b/src/ecl_data_io/_unformatted/read.py index 04f4631..cc199ba 100644 --- a/src/ecl_data_io/_unformatted/read.py +++ b/src/ecl_data_io/_unformatted/read.py @@ -1,9 +1,8 @@ import io -import numpy as np - import ecl_data_io.types as ecl_io_types -from ecl_data_io._unformatted.common import group_len, item_size +import numpy as np +from ecl_data_io._unformatted.common import bytes_in_array, group_len, item_size from ecl_data_io.array_entry import EclArray from ecl_data_io.errors import EclParsingError @@ -61,7 +60,9 @@ def _read_record_marker(self, expected_value): """ value = int.from_bytes(self.stream.read(4), byteorder="big", signed=True) if value != expected_value: - raise EclParsingError(f"Unexpected size of record {value}") + raise EclParsingError( + f"Unexpected size of record {value} ({value.to_bytes(4, byteorder='big', signed=True)})" + ) def _read_keyword(self): """ @@ -102,7 +103,9 @@ def _read(self): return start_marker_value = int.from_bytes(start_marker, byteorder="big", signed=True) if start_marker_value != 16: - raise EclParsingError(f"Unexpected size of record {start_marker_value}") + raise EclParsingError( + f"Unexpected size of record {start_marker_value} ({start_marker})" + ) self._read_keyword() self._length = self._read_length() self._read_type() @@ -133,12 +136,7 @@ def _read(self): f"has item length {type_len} which requires 0 number of" f"elements, but found {self._length} amount of elements." ) - - g_len = group_len(self.type) - num_groups = self._length // g_len + 1 - if self._length == 0: - num_groups = 0 - bytes_to_skip = num_groups * 8 + self._length * type_len + bytes_to_skip = bytes_in_array(self._length, self.type) self._data_start = self.stream.tell() self.stream.seek(bytes_to_skip, io.SEEK_CUR) diff --git a/tests/test_formatted_common.py b/tests/test_formatted_common.py index 5758477..fecce5b 100644 --- a/tests/test_formatted_common.py +++ b/tests/test_formatted_common.py @@ -1,4 +1,7 @@ -from ecl_data_io._unformatted.common import group_len, item_size +import hypothesis.strategies as st +import pytest +from ecl_data_io._unformatted.common import bytes_in_array, group_len, item_size +from hypothesis import given def test_group_len(): @@ -21,3 +24,27 @@ def test_item_size(): assert item_size(b"DOUB") == 8 assert item_size(b"MESS") == 0 assert item_size(b"x231") is None + + +def test_bytes_in_array(): + assert bytes_in_array(1000, b"DOUB") == 8008 + assert bytes_in_array(1000, b"REAL") == 4008 + assert bytes_in_array(900, b"REAL") == 900 * 4 + 8 + assert bytes_in_array(1100, b"REAL") == 1000 * 4 + 8 + 100 * 4 + 8 + assert bytes_in_array(0, b"REAL") == 0 + + +@pytest.mark.parametrize( + "item_type", + [ + b"C032", + b"CHAR", + b"INTE", + b"REAL", + b"LOGI", + b"DOUB", + ], +) +@given(length=st.integers(min_value=1, max_value=104)) +def test_bytes_in_array_one_group(item_type, length): + assert bytes_in_array(length, item_type) == 8 + length * item_size(item_type)