def test_uint_math(): assert uint8(0) + uint8(uint32(16)) == uint8( 16) # allow explicit casting to make invalid addition valid expect_op_error(lambda: uint8(0) - uint8(1), "no underflows allowed") expect_op_error(lambda: uint8(1) + uint8(255), "no overflows allowed") expect_op_error(lambda: uint8(0) + 256, "no overflows allowed") expect_op_error(lambda: uint8(42) + uint32(123), "no mixed types") expect_op_error(lambda: uint32(42) + uint8(123), "no mixed types") assert type(uint32(1234) + 56) == uint32
def deserialize(cls: Type[M], stream: BinaryIO, scope: int) -> M: elem_cls = cls.element_cls() if elem_cls.is_fixed_byte_length(): elem_byte_length = elem_cls.type_byte_length() if scope % elem_byte_length != 0: raise Exception( f"scope {scope} does not match element byte length {elem_byte_length} multiple" ) count = scope // elem_byte_length if not cls.is_valid_count(count): raise Exception(f"count {count} is invalid") return cls( elem_cls.deserialize(stream, elem_byte_length) for _ in range(count)) # type: ignore else: if scope == 0: if not cls.is_valid_count(0): raise Exception("scope cannot be 0, count must not be 0") return cls() first_offset = decode_offset(stream) if first_offset > scope: raise Exception( f"first offset is too big: {first_offset}, scope: {scope}") if first_offset % OFFSET_BYTE_LENGTH != 0: raise Exception( f"first offset {first_offset} is not a multiple of offset length {OFFSET_BYTE_LENGTH}" ) count = first_offset // OFFSET_BYTE_LENGTH if not cls.is_valid_count(count): raise Exception(f"count {count} is invalid") # count - 1: we already have the first offset offsets = [first_offset ] + [decode_offset(stream) for _ in range(count - 1)] + [uint32(scope)] elem_min, elem_max = elem_cls.min_byte_length( ), elem_cls.max_byte_length() elems = [] for i in range(count): start, end = offsets[i], offsets[i + 1] if end < start: raise Exception( f"offsets[{i}] value {start} is invalid, next offset is {end}" ) elem_size = end - start if not (elem_min <= elem_size <= elem_max): raise Exception( f"offset[{i}] value {start} is invalid, next offset is {end}," f" implied size is {elem_size}, size bounds: [{elem_min}, {elem_max}]" ) elems.append(elem_cls.deserialize(stream, elem_size)) return cls(*elems) # type: ignore
def encode_offset(stream: BinaryIO, offset: int): return uint32(offset).serialize(stream)
def test_list(): typ = List[uint64, 128] assert issubclass(typ, List) assert issubclass(typ, View) assert isinstance(typ, TypeDef) assert not typ.is_fixed_byte_length() assert len(typ()) == 0 # empty assert len(typ(uint64(0))) == 1 # single arg assert len(typ(uint64(i) for i in range(10))) == 10 # generator assert len(typ(uint64(0), uint64(1), uint64(2))) == 3 # args assert isinstance(typ(1, 2, 3, 4, 5)[4], uint64) # coercion assert isinstance(typ(i for i in range(10))[9], uint64) # coercion in generator v = typ(uint64(2), uint64(1)) v[0] = uint64(123) assert v[0] == 123 assert isinstance(v[0], uint64) assert isinstance(v, List) assert isinstance(v, View) assert len(typ([i for i in range(10)])) == 10 # cast py list to SSZ list foo = List[uint32, 128](0 for i in range(128)) foo[0] = 123 foo[1] = 654 foo[127] = 222 assert sum(foo) == 999 try: foo[3] = 2**32 # out of bounds except ValueError: pass for i in range(128): foo.pop() assert len(foo) == 128 - 1 - i for i in range(128): foo.append(uint32(i)) assert len(foo) == i + 1 assert foo[i] == i try: foo[3] = uint64(2**32 - 1) # within bounds, wrong type assert False except ValueError: pass try: foo[128] = 100 assert False except IndexError: pass try: foo[-1] = 100 # valid in normal python lists assert False except IndexError: pass try: foo[128] = 100 # out of bounds assert False except IndexError: pass
def test_container(): class Foo(Container): a: uint8 b: uint32 empty = Foo() assert empty.a == uint8(0) assert empty.b == uint32(0) assert issubclass(Foo, Container) assert issubclass(Foo, View) assert Foo.is_fixed_byte_length() x = Foo(a=uint8(123), b=uint32(45)) assert x.a == 123 assert x.b == 45 assert isinstance(x.a, uint8) assert isinstance(x.b, uint32) assert x.__class__.is_fixed_byte_length() class Bar(Container): a: uint8 b: List[uint8, 1024] assert not Bar.is_fixed_byte_length() y = Bar(a=123, b=List[uint8, 1024](uint8(1), uint8(2))) assert y.a == 123 assert isinstance(y.a, uint8) assert len(y.b) == 2 assert isinstance(y.a, uint8) assert not y.__class__.is_fixed_byte_length() assert y.b[0] == 1 v: List = y.b assert v.__class__.element_cls() == uint8 assert v.__class__.limit() == 1024 field_values = list(y) assert field_values == [y.a, y.b] f_a, f_b = y assert f_a == y.a assert f_b == y.b y.a = 42 try: y.a = 256 # out of bounds assert False except ValueError: pass try: y.a = uint16(255) # within bounds, wrong type assert False except ValueError: pass try: y.not_here = 5 assert False except AttributeError: pass try: Foo(wrong_field_name=100) assert False except AttributeError: pass
"ff" * 64 + "01", h(h("ff" * 32, "ff" * 32), h(chunk("01"), chunk(""))), "0x" + ("ff" * 64) + "01"), ("odd bitlist", Bitlist[513], Bitlist[513](1 for i in range(513)), "ff" * 64 + "03", h(h(h("ff" * 32, "ff" * 32), h(chunk("01"), chunk(""))), chunk("0102")), "0x" + ("ff" * 64) + "03"), ("uint8 00", uint8, uint8(0x00), "00", chunk("00"), 0), ("uint8 01", uint8, uint8(0x01), "01", chunk("01"), 1), ("uint8 ab", uint8, uint8(0xab), "ab", chunk("ab"), 0xab), ("byte 00", byte, byte(0x00), "00", chunk("00"), 0), ("byte 01", byte, byte(0x01), "01", chunk("01"), 1), ("byte ab", byte, byte(0xab), "ab", chunk("ab"), 0xab), ("uint16 0000", uint16, uint16(0x0000), "0000", chunk("0000"), 0), ("uint16 abcd", uint16, uint16(0xabcd), "cdab", chunk("cdab"), 0xabcd), ("uint32 00000000", uint32, uint32(0x00000000), "00000000", chunk("00000000"), 0), ("uint32 01234567", uint32, uint32(0x01234567), "67452301", chunk("67452301"), 0x01234567), ("small (4567, 0123)", SmallTestStruct, SmallTestStruct(A=0x4567, B=0x0123), "67452301", h(chunk("6745"), chunk("2301")), { 'A': 0x4567, 'B': 0x0123 }), ("small [4567, 0123]::2", Vector[uint16, 2], Vector[uint16, 2](uint16(0x4567), uint16(0x0123)), "67452301", chunk("67452301"), (0x4567, 0x0123)), ("uint32 01234567", uint32, uint32(0x01234567), "67452301",
def encode_offset(offset: int) -> bytes: return uint32(offset).encode_bytes()