"""Hash data structures — fixed-length byte wrappers. Ported from net.i2p.data.Hash, net.i2p.crypto.SHA1Hash, Hash384, Hash512. """ import hashlib class Hash: """32-byte SHA-256 hash container.""" HASH_LENGTH = 32 __slots__ = ("_data", "_cached_hash") def __init__(self, data: bytes | None = None) -> None: if data is not None: if len(data) != self.HASH_LENGTH: raise ValueError(f"Hash data must be {self.HASH_LENGTH} bytes, got {len(data)}") self._data = bytes(data) else: self._data = b"\x00" * self.HASH_LENGTH self._cached_hash = int.from_bytes(self._data[:4], "big") @classmethod def create(cls, data: bytes, offset: int = 0) -> "Hash": return cls(data[offset : offset + cls.HASH_LENGTH]) @property def data(self) -> bytes: return self._data def __eq__(self, other: object) -> bool: if isinstance(other, Hash): return self._data == other._data return NotImplemented def __hash__(self) -> int: return self._cached_hash def __repr__(self) -> str: return f"Hash({self._data.hex()})" def __bytes__(self) -> bytes: return self._data FAKE_HASH: "Hash" Hash.FAKE_HASH = Hash(b"\x00" * Hash.HASH_LENGTH) class SHA1Hash: """20-byte SHA-1 hash container.""" HASH_LENGTH = 20 __slots__ = ("_data", "_cached_hash") def __init__(self, data: bytes | None = None) -> None: if data is not None: if len(data) != self.HASH_LENGTH: raise ValueError(f"SHA1Hash data must be {self.HASH_LENGTH} bytes, got {len(data)}") self._data = bytes(data) else: self._data = b"\x00" * self.HASH_LENGTH self._cached_hash = int.from_bytes(self._data[:4], "big") @property def data(self) -> bytes: return self._data def __eq__(self, other: object) -> bool: if isinstance(other, SHA1Hash): return self._data == other._data return NotImplemented def __hash__(self) -> int: return self._cached_hash def __repr__(self) -> str: return f"SHA1Hash({self._data.hex()})" def __bytes__(self) -> bytes: return self._data class Hash384: """48-byte hash container.""" HASH_LENGTH = 48 __slots__ = ("_data",) def __init__(self, data: bytes | None = None) -> None: if data is not None: if len(data) != self.HASH_LENGTH: raise ValueError(f"Hash384 data must be {self.HASH_LENGTH} bytes, got {len(data)}") self._data = bytes(data) else: self._data = b"\x00" * self.HASH_LENGTH @property def data(self) -> bytes: return self._data def __eq__(self, other: object) -> bool: if isinstance(other, Hash384): return self._data == other._data return NotImplemented def __hash__(self) -> int: return int.from_bytes(self._data[:4], "big") def __bytes__(self) -> bytes: return self._data class Hash512: """64-byte hash container.""" HASH_LENGTH = 64 __slots__ = ("_data",) def __init__(self, data: bytes | None = None) -> None: if data is not None: if len(data) != self.HASH_LENGTH: raise ValueError(f"Hash512 data must be {self.HASH_LENGTH} bytes, got {len(data)}") self._data = bytes(data) else: self._data = b"\x00" * self.HASH_LENGTH @property def data(self) -> bytes: return self._data def __eq__(self, other: object) -> bool: if isinstance(other, Hash512): return self._data == other._data return NotImplemented def __hash__(self) -> int: return int.from_bytes(self._data[:4], "big") def __bytes__(self) -> bytes: return self._data