"""Caching utilities — ByteCache, SimpleByteCache, LHMCache. Ported from net.i2p.util.{ByteCache, SimpleByteCache, LHMCache}. """ import threading from collections import OrderedDict from typing import Dict, Optional class LHMCache(OrderedDict): """LRU cache backed by OrderedDict. When the cache exceeds max_size, the oldest entry is evicted. Not thread-safe — caller must synchronize. """ def __init__(self, max_size: int) -> None: super().__init__() self._max_size = max_size def __setitem__(self, key, value): if key in self: self.move_to_end(key) super().__setitem__(key, value) while len(self) > self._max_size: self.popitem(last=False) def __getitem__(self, key): value = super().__getitem__(key) self.move_to_end(key) return value def get(self, key, default=None): if key in self: return self[key] return default class SimpleByteCache: """Per-size byte array cache for reuse. Reduces allocation pressure. In Python, this is less critical than Java but maintains API compatibility. """ _instances: Dict[int, "SimpleByteCache"] = {} _lock = threading.Lock() def __init__(self, cache_size: int, size: int) -> None: self._size = size self._cache_size = cache_size self._pool: list[bytes] = [] self._pool_lock = threading.Lock() @classmethod def get_instance(cls, size: int, cache_size: int = 32) -> "SimpleByteCache": with cls._lock: if size not in cls._instances: cls._instances[size] = SimpleByteCache(cache_size, size) return cls._instances[size] def acquire(self) -> bytearray: """Get a byte array from cache or create a new one.""" with self._pool_lock: if self._pool: return bytearray(self._pool.pop()) return bytearray(self._size) def release(self, data: bytes | bytearray) -> None: """Return a byte array to the cache.""" with self._pool_lock: if len(self._pool) < self._cache_size: # Zero it out if isinstance(data, bytearray): for i in range(len(data)): data[i] = 0 self._pool.append(bytes(data)) @classmethod def clear_all(cls) -> None: with cls._lock: for cache in cls._instances.values(): with cache._pool_lock: cache._pool.clear() class ByteCache: """Byte array cache using ByteArray wrappers. Simplified for Python — wraps SimpleByteCache. """ _instances: Dict[tuple, "ByteCache"] = {} _lock = threading.Lock() def __init__(self, cache_size: int, size: int) -> None: self._inner = SimpleByteCache.get_instance(size, cache_size) self._size = size @classmethod def get_instance(cls, cache_size: int, size: int) -> "ByteCache": key = (cache_size, size) with cls._lock: if key not in cls._instances: cls._instances[key] = ByteCache(cache_size, size) return cls._instances[key] def acquire(self) -> bytearray: return self._inner.acquire() def release(self, data: bytes | bytearray, should_zero: bool = True) -> None: self._inner.release(data) @classmethod def clear_all(cls) -> None: SimpleByteCache.clear_all()