diff --git a/src/pysparkplug/_datatype.py b/src/pysparkplug/_datatype.py index 0bd3c3d..01f51f0 100644 --- a/src/pysparkplug/_datatype.py +++ b/src/pysparkplug/_datatype.py @@ -122,10 +122,13 @@ def _int_encoder(value: int, bits: int) -> int: def _int_decoder(value: int, bits: int) -> int: - max_val: int = 2**bits - if not 0 <= value < max_val: - raise OverflowError(f"Int{bits} overflow with value {value}") - return value - (max_val if value >= 2 ** (bits - 1) else 0) + mask = (1 << bits) - 1 # e.g. 0xFFFF for 16 bits + value &= mask # drop any higher bits + sign_bit = 1 << (bits - 1) # e.g. 0x8000 for 16 bits + if value & sign_bit: # if sign bit is set + value -= 1 << bits + + return value def _encode_numeric_array(values: Sequence[float], format_char: str) -> bytes: diff --git a/test/unit_tests/test_datatype.py b/test/unit_tests/test_datatype.py index 37026b6..9244da1 100644 --- a/test/unit_tests/test_datatype.py +++ b/test/unit_tests/test_datatype.py @@ -134,11 +134,15 @@ def test_integer_encoding(self): decoded, value, f"Failed to encode/decode {value} with {dtype}" ) - def test_decoder_errors(self) -> None: - with self.assertRaises(OverflowError): - # too large - DataType.INT8.decode(2**9) + def test_decoding_sign_extended_values(self): + value = -87 + signExtendedValue = ( + value & 0xFFFFFFFF # packed into uint32 for Google Protobuf + ) + decoded = DataType.INT16.decode(signExtendedValue) + self.assertEqual(decoded, value) + def test_decoder_errors(self) -> None: with self.assertRaises(ValueError): # not enough bytes DataType.INT16_ARRAY.decode(b"\x00")