-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from Bhargavasomu/bytes
Add support for Bytes and Bytearray objects
- Loading branch information
Showing
7 changed files
with
275 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
BYTES_PREFIX_LENGTH = 4 | ||
LIST_PREFIX_LENGTH = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from ssz.constants import ( | ||
BYTES_PREFIX_LENGTH, | ||
) | ||
from ssz.exceptions import ( | ||
DeserializationError, | ||
SerializationError, | ||
) | ||
|
||
|
||
class Bytes: | ||
""" | ||
A sedes for byte objects. | ||
""" | ||
|
||
def serialize(self, val): | ||
if not isinstance(val, (bytes, bytearray)): | ||
raise SerializationError( | ||
"Can only serialize bytes or bytearray objects", | ||
val | ||
) | ||
|
||
object_len = len(val) | ||
if object_len >= 2 ** (BYTES_PREFIX_LENGTH * 8): | ||
raise SerializationError( | ||
f'Object too long for its length to fit into {BYTES_PREFIX_LENGTH} bytes' | ||
f'after serialization', | ||
val | ||
) | ||
|
||
# Convert the length of bytes to a 4 bytes value | ||
object_len_bytes = object_len.to_bytes(BYTES_PREFIX_LENGTH, 'big') | ||
|
||
return object_len_bytes + val | ||
|
||
def deserialize_segment(self, data, start_index): | ||
""" | ||
Deserialize the data from the given start_index | ||
""" | ||
# Make sure we have sufficient data for inferring length of bytes object | ||
if len(data) < start_index + BYTES_PREFIX_LENGTH: | ||
raise DeserializationError( | ||
'Insufficient data: Cannot retrieve the length of bytes object', | ||
data | ||
) | ||
|
||
# object_len contains the length of the original bytes object | ||
object_len = int.from_bytes(data[start_index:start_index + BYTES_PREFIX_LENGTH], 'big') | ||
# object_start_index is the start index of bytes object in the serialized bytes string | ||
object_start_index = start_index + BYTES_PREFIX_LENGTH | ||
object_end_index = object_start_index + object_len | ||
|
||
# Make sure we have sufficent data for inferring the whole bytes object | ||
if len(data) < object_end_index: | ||
raise DeserializationError( | ||
'Insufficient data: Cannot retrieve the whole list bytes object', | ||
data | ||
) | ||
|
||
return data[object_start_index:object_end_index], object_end_index | ||
|
||
def deserialize(self, data): | ||
deserialized_data, end_index = self.deserialize_segment(data, 0) | ||
if end_index != len(data): | ||
raise DeserializationError( | ||
'Data to be deserialized is too long', | ||
data | ||
) | ||
|
||
return deserialized_data | ||
|
||
|
||
bytes_sedes = Bytes() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import pytest | ||
|
||
from ssz import ( | ||
DeserializationError, | ||
SerializationError, | ||
decode, | ||
encode, | ||
) | ||
from ssz.sedes import ( | ||
bytes_sedes, | ||
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value,expected', | ||
( | ||
(b"", b'\x00\x00\x00\x00'), | ||
(b"I", b'\x00\x00\x00\x01I'), | ||
(b"foo", b'\x00\x00\x00\x03foo'), | ||
(b"hello", b'\x00\x00\x00\x05hello'), | ||
(bytearray(b""), b'\x00\x00\x00\x00'), | ||
(bytearray(b"I"), b'\x00\x00\x00\x01I'), | ||
(bytearray(b"foo"), b'\x00\x00\x00\x03foo'), | ||
(bytearray(b"hello"), b'\x00\x00\x00\x05hello'), | ||
), | ||
) | ||
def test_bytes_serialize_values(value, expected): | ||
assert bytes_sedes.serialize(value) == expected | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value', | ||
( | ||
# Non-byte objects | ||
None, | ||
1, | ||
1.0, | ||
'', | ||
'True', | ||
[1, 2, 3], | ||
(1, 2, 3), | ||
{b"0": b"1"}, | ||
{b"0", b"1", b"2", b"3"}, | ||
), | ||
) | ||
def test_bytes_serialize_bad_values(value): | ||
with pytest.raises(SerializationError): | ||
bytes_sedes.serialize(value) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value,expected', | ||
( | ||
(b'\x00\x00\x00\x00', b""), | ||
(b'\x00\x00\x00\x01I', b"I"), | ||
(b'\x00\x00\x00\x03foo', b"foo"), | ||
(b'\x00\x00\x00\x05hello', b"hello"), | ||
), | ||
) | ||
def test_bytes_deserialize_values(value, expected): | ||
assert bytes_sedes.deserialize(value) == expected | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value', | ||
( | ||
# Less than 4 bytes of serialized data | ||
b'\x00\x00\x01', | ||
# Insufficient serialized object data as per found out byte object length | ||
b'\x00\x00\x00\x04', | ||
# Serialized data given is more than what is required | ||
b'\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00' | ||
), | ||
) | ||
def test_bytes_deserialization_bad_value(value): | ||
with pytest.raises(DeserializationError): | ||
bytes_sedes.deserialize(value) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value,expected', | ||
( | ||
(b"", b''), | ||
(b"I", b'I'), | ||
(b"foo", b'foo'), | ||
(b"hello", b'hello'), | ||
(bytearray(b""), b''), | ||
(bytearray(b"I"), b'I'), | ||
(bytearray(b"foo"), b'foo'), | ||
(bytearray(b"hello"), b'hello'), | ||
), | ||
) | ||
def test_bytes_round_trip(value, expected): | ||
assert bytes_sedes.deserialize(bytes_sedes.serialize(value)) == expected | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value,sedes', | ||
( | ||
(b"", 'bytes_sedes'), | ||
(b"I", 'bytes_sedes'), | ||
(b"foo", 'bytes_sedes'), | ||
(b"hello", 'bytes_sedes'), | ||
(b"", bytes_sedes), | ||
(b"I", bytes_sedes), | ||
(b"foo", bytes_sedes), | ||
(b"hello", bytes_sedes), | ||
), | ||
) | ||
def test_bytes_round_trip_codec(value, sedes): | ||
if isinstance(sedes, str): | ||
sedes_obj = eval(sedes) | ||
else: | ||
sedes_obj = sedes | ||
assert decode(encode(value, sedes), sedes_obj) == value | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'value', | ||
( | ||
b"", | ||
b"I", | ||
b"foo", | ||
b"hello", | ||
), | ||
) | ||
def test_bytes_round_trip_no_sedes(value): | ||
assert decode(encode(value), bytes_sedes) == value |
Oops, something went wrong.