Source code for remerkleable.basic
from typing import Any, TypeVar, Type
from remerkleable.core import BasicView, View, ObjType, ObjParseException
V = TypeVar('V', bound=View)
# Not returning "NotImplemented" like regular operators,
# it's completely invalid, do not let the interpreter resort to the other operation hand.
[docs]class OperationNotSupported(Exception):
pass
[docs]class boolean(int, BasicView):
[docs] def encode_bytes(self) -> bytes:
return b"\x01" if self else b"\x00"
def __new__(cls, value: int): # int value, but can be any subclass of int (bool, Bit, Bool, etc...)
if value < 0 or value > 1:
raise ValueError(f"value {value} out of bounds for bit")
return super().__new__(cls, value)
def __add__(self, other):
raise OperationNotSupported(f"cannot add bool ({self} + {other})")
def __sub__(self, other):
raise OperationNotSupported(f"cannot sub bool ({self} - {other})")
def __mul__(self, other):
raise OperationNotSupported(f"cannot mul bool ({self} * {other})")
def __floordiv__(self, other): # Better known as "//"
raise OperationNotSupported(f"cannot floordiv bool ({self} // {other})")
def __truediv__(self, other):
raise OperationNotSupported(f"cannot truediv bool ({self} / {other})")
def __bool__(self):
return self > 0
[docs] @classmethod
def coerce_view(cls: Type[V], v: Any) -> V:
return cls(v)
[docs] @classmethod
def type_byte_length(cls) -> int:
return 1
[docs] @classmethod
def decode_bytes(cls: Type[V], bytez: bytes) -> V:
return cls(bytez != b"\x00")
[docs] @classmethod
def from_obj(cls: Type[V], obj: ObjType) -> V:
if not isinstance(obj, bool):
raise ObjParseException(f"obj '{obj}' is not a bool")
return cls(obj)
[docs] def to_obj(self) -> ObjType:
return bool(self)
[docs] @classmethod
def type_repr(cls) -> str:
return "boolean"
[docs]class uint(int, BasicView):
def __new__(cls, value: int):
if value < 0:
raise ValueError(f"unsigned type {cls} must not be negative")
byte_len = cls.type_byte_length()
if value.bit_length() > (byte_len << 3):
raise ValueError(f"value out of bounds for {cls}")
return super().__new__(cls, value)
def __add__(self, other):
return self.__class__(super().__add__(self.__class__.coerce_view(other)))
def __sub__(self, other):
return self.__class__(super().__sub__(self.__class__.coerce_view(other)))
def __mul__(self, other):
return self.__class__(super().__mul__(self.__class__.coerce_view(other)))
def __floordiv__(self, other): # Better known as "//"
return self.__class__(super().__floordiv__(self.__class__.coerce_view(other)))
def __truediv__(self, other):
raise OperationNotSupported(f"non-integer division '{self} / {other}' "
f"is not valid for {self.__class__.type_repr()} type")
[docs] @classmethod
def coerce_view(cls: Type[V], v: Any) -> V:
if isinstance(v, uint) and cls.type_byte_length() != v.__class__.type_byte_length():
raise ValueError("value must have equal byte length to coerce it")
if isinstance(v, bytes):
return cls.decode_bytes(v)
return cls(v)
[docs] @classmethod
def decode_bytes(cls: Type[V], bytez: bytes) -> V:
return cls(int.from_bytes(bytez, byteorder='little'))
[docs] def encode_bytes(self) -> bytes:
return self.to_bytes(length=self.__class__.type_byte_length(), byteorder='little')
[docs] @classmethod
def from_obj(cls: Type[V], obj: ObjType) -> V:
if not isinstance(obj, (int, str)):
raise ObjParseException(f"obj '{obj}' is not an int or str")
if isinstance(obj, str):
if obj.startswith('0x'):
return cls.decode_bytes(bytes.fromhex(obj[2:]))
obj = int(obj)
return cls(obj)
[docs] def to_obj(self) -> ObjType:
return int(self)
[docs] @classmethod
def type_repr(cls) -> str:
return f"uint{cls.type_byte_length()*8}"
[docs]class uint8(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 1
[docs]class uint16(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 2
[docs]class uint32(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 4
[docs]class uint64(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 8
# JSON encoder should be able to handle uint64, converting it to a string if necessary.
# no "to_obj" here.
[docs]class uint128(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 16
[docs] def to_obj(self) -> ObjType:
return "0x" + self.encode_bytes().hex()
[docs]class uint256(uint):
[docs] @classmethod
def type_byte_length(cls) -> int:
return 32
[docs] def to_obj(self) -> ObjType:
return "0x" + self.encode_bytes().hex()
[docs]class bit(boolean):
pass