diff --git a/multiaddr/codecs/memory.py b/multiaddr/codecs/memory.py new file mode 100644 index 0000000..e3055e0 --- /dev/null +++ b/multiaddr/codecs/memory.py @@ -0,0 +1,28 @@ +import struct +from ..codecs import CodecBase +from ..exceptions import BinaryParseError + +SIZE = 64 +IS_PATH = False + +class Codec(CodecBase): + SIZE = SIZE + IS_PATH = IS_PATH + + def to_bytes(self, proto, string: str) -> bytes: + # Parse as unsigned 64-bit int + value = int(string, 10) + if value < 0 or value > 0xFFFFFFFFFFFFFFFF: + raise ValueError("Value out of range for uint64") + return struct.pack(">Q", value) # big-endian uint64 + + def to_string(self, proto, buf: bytes) -> str: + if len(buf) != 8: + raise BinaryParseError("Expected 8 bytes for uint64", buf, "memory") + value = struct.unpack(">Q", buf)[0] + return str(value) + + def memory_validate(self, b: bytes) -> None: + if len(b) != 8: + raise ValueError(f"Invalid length: must be exactly 8 bytes, got {len(b)}") + \ No newline at end of file diff --git a/multiaddr/protocols.py b/multiaddr/protocols.py index eb25f63..5de8990 100644 --- a/multiaddr/protocols.py +++ b/multiaddr/protocols.py @@ -78,6 +78,7 @@ P_SNI = 0x01C1 P_NOISE = 0x01C6 P_WEBTRANSPORT = 0x01D1 +P_MEMORY = 0x309 class Protocol: @@ -166,6 +167,7 @@ def __repr__(self) -> str: Protocol(P_P2P_CIRCUIT, "p2p-circuit", None), Protocol(P_WEBTRANSPORT, "webtransport", None), Protocol(P_UNIX, "unix", "fspath"), + Protocol(P_MEMORY, "memory", None) ] diff --git a/tests/test_codec.py b/tests/test_codec.py new file mode 100644 index 0000000..ffe2c04 --- /dev/null +++ b/tests/test_codec.py @@ -0,0 +1,66 @@ +import pytest +import struct + +from multiaddr.codecs import memory +from multiaddr.exceptions import BinaryParseError + + +def test_to_bytes_and_to_string_roundtrip(): + codec = memory.Codec() + + # some valid values + for val in [0, 1, 42, 2**32, 2**64 - 1]: + s = str(val) + b = codec.to_bytes(None, s) + # must be exactly 8 bytes + assert isinstance(b, bytes) + assert len(b) == 8 + # roundtrip back to string + out = codec.to_string(None, b) + assert out == s + +def test_invalid_string_to_bytes(): + codec = memory.Codec() + + # not a number + with pytest.raises(ValueError): + codec.to_bytes(None, "abc") + + # negative number + with pytest.raises(ValueError): + codec.to_bytes(None, "-1") + + # too large + with pytest.raises(ValueError): + codec.to_bytes(None, str(2**64)) + +def test_invalid_bytes_to_string(): + codec = memory.Codec() + + # too short + with pytest.raises(BinaryParseError): + codec.to_string(None, b"\x00\x01") + + # too long + with pytest.raises(BinaryParseError): + codec.to_string(None, b"\x00" * 9) + + +def test_specific_encoding(): + codec = memory.Codec() + + # 42 encoded in big-endian + expected_bytes = b"\x00\x00\x00\x00\x00\x00\x00*" + assert codec.to_bytes(None, "42") == expected_bytes + assert codec.to_string(None, expected_bytes) == "42" + +def test_memory_validate_function(): + # Directly test the helper + codec = memory.Codec() + + # Valid case + codec.memory_validate(b"\x00" * 8) # should not raise + + # Invalid length + with pytest.raises(ValueError): + codec.memory_validate(b"\x00" * 7) \ No newline at end of file