def serialize(self, stream: BinaryIO) -> int: backing = self.get_backing() bitlen = self.length() chunk_count = (bitlen + 255) // 256 # excludes delimit bit, this is the backing, not the serialized form byte_len = (bitlen + 7) // 8 tree_depth = self.tree_depth() full_chunks_count = max(0, chunk_count - 1) for chunk_index in range(full_chunks_count): chunk = backing.getter(to_gindex(chunk_index, tree_depth)) stream.write(chunk.root) if chunk_count > 0: last_chunk = backing.getter(to_gindex(chunk_count - 1, tree_depth)) # write the last chunk, may not be a full chunk last_chunk_bytes_count = byte_len - (full_chunks_count * 32) bytez = last_chunk.root[:last_chunk_bytes_count] # add in delimiting bit if bitlen % 8 == 0: bytez += b"\x01" else: bytez = bytez[:len(bytez) - 1] +\ (bytez[len(bytez) - 1] ^ (1 << (bitlen % 8))).to_bytes(length=1, byteorder='little') stream.write(bytez) else: stream.write(b"\x01") # empty bitlist still has a delimiting bit return (bitlen + 7 + 1) // 8 # includes delimit bit in length computation
def set(self, i: int, v: boolean) -> None: ll = self.length() if i >= ll: raise NavigationError(f"cannot set bit {i} in bits of length {ll}") chunk_i = i >> 8 chunk_setter_link: Link = self.get_backing().setter(to_gindex(chunk_i, self.__class__.tree_depth())) chunk = self.get_backing().getter(to_gindex(chunk_i, self.__class__.tree_depth())) new_chunk = _new_chunk_with_bit(chunk, i & 0xff, v) self.set_backing(chunk_setter_link(new_chunk))
def get(self, i: int) -> View: elem_type: Type[View] = self.item_elem_cls(i) # basic types are more complicated: we operate on subsections packed into a bottom chunk if self.is_packed(): elems_per_chunk = 32 // elem_type.type_byte_length() chunk_i = i // elems_per_chunk chunk = self.get_backing().getter(to_gindex(chunk_i, self.tree_depth())) return cast(Type[BasicView], elem_type).basic_view_from_backing(chunk, i % elems_per_chunk) else: return elem_type.view_from_backing( self.get_backing().getter(to_gindex(i, self.tree_depth())), lambda v: self.set(i, v))
def key_to_static_gindex(cls, key: Any) -> Gindex: depth = cls.tree_depth() byte_limit = cls.limit() if key < 0 or key >= byte_limit: raise KeyError chunk_i = key // 32 return to_gindex(chunk_i, depth)
def key_to_static_gindex(cls, key: Any) -> Gindex: fields = cls.fields() try: field_index = list(fields.keys()).index(key) except ValueError: # list.index raises ValueError if the element (a key here) is missing raise KeyError return to_gindex(field_index, cls.tree_depth())
def key_to_static_gindex(cls, key: Any) -> Gindex: depth = cls.tree_depth() bit_len = cls.vector_length() if key < 0 or key >= bit_len: raise KeyError chunk_i = key // 256 return to_gindex(chunk_i, depth)
def serialize(self, stream: BinaryIO) -> int: backing = self.get_backing() bitlen = self.length() chunk_count = (bitlen + 255) // 256 # excludes delimit bit, this is the backing, not the serialized form byte_len = (bitlen + 7) // 8 tree_depth = self.tree_depth() full_chunks_count = max(0, chunk_count - 1) for chunk_index in range(full_chunks_count): chunk: Node = backing.getter(to_gindex(chunk_index, tree_depth)) stream.write(chunk.root) if chunk_count > 0: last_chunk = backing.getter(to_gindex(chunk_count - 1, tree_depth)) # write the last chunk, may not be a full chunk last_chunk_bytes_count = byte_len - (full_chunks_count * 32) stream.write(last_chunk.root[:last_chunk_bytes_count]) return byte_len
def pop(self): ll = self.length() if ll == 0: raise Exception("list is empty, cannot pop") i = ll - 1 chunk_i = i // 256 target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i & 0xff == 0: set_last = self.get_backing().setter(target) next_backing = set_last(zero_node(0)) else: set_last = self.get_backing().setter(target) chunk = self.get_backing().getter(target) next_backing = set_last(_new_chunk_with_bit(chunk, ll & 0xff, boolean(False))) # if possible, summarize can_summarize = (target & 1) == 0 if can_summarize: # summarize to the highest node possible. # I.e. the resulting target must be a right-hand, unless it's the only content node. while (target & 1) == 0 and target != 0b10: target >>= 1 summary_fn = next_backing.summarize_into(target) next_backing = summary_fn() set_length = next_backing.rebind_right new_length = uint256(ll - 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def pop(self): ll = self.length() if ll == 0: raise Exception("list is empty, cannot pop") i = ll - 1 target: Gindex can_summarize: bool if self.__class__.is_packed(): next_backing = self.get_backing() elem_type: Type[View] = self.__class__.element_cls() if isinstance(elem_type, BasicTypeDef): elems_per_chunk = 32 // elem_type.type_byte_length() chunk_i = i // elems_per_chunk target = to_gindex(chunk_i, self.__class__.tree_depth()) if i % elems_per_chunk == 0: chunk = zero_node(0) else: chunk = next_backing.getter(target) set_last = next_backing.setter(target) chunk = cast(BasicView, elem_type.default(None)).backing_from_base( chunk, i % elems_per_chunk) next_backing = set_last(chunk) can_summarize = (target & 1) == 0 and i % elems_per_chunk == 0 else: raise Exception( "cannot pop a packed element that is not a basic type") else: target = to_gindex(i, self.__class__.tree_depth()) set_last = self.get_backing().setter(target) next_backing = set_last(zero_node(0)) can_summarize = (target & 1) == 0 # if possible, summarize if can_summarize: # summarize to the highest node possible. # I.e. the resulting target must be a right-hand, unless it's the only content node. while (target & 1) == 0 and target != 0b10: target >>= 1 summary_fn = next_backing.summarize_into(target) next_backing = summary_fn() set_length = next_backing.rebind_right new_length = uint256(ll - 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def get(self, i: int) -> boolean: ll = self.length() if i >= ll: raise NavigationError(f"cannot get bit {i} in bits of length {ll}") chunk_i = i >> 8 chunk = self.get_backing().getter(to_gindex(chunk_i, self.__class__.tree_depth())) chunk_byte = chunk.root[(i & 0xff) >> 3] return boolean((chunk_byte >> (i & 0x7)) & 1)
def view_from_backing(cls: Type[BV], node: Node, hook: Optional[ViewHook] = None) -> BV: depth = cls.tree_depth() byte_len = cls.vector_length() if depth == 0: return cls.decode_bytes(node.merkle_root()[:byte_len]) else: chunk_count = (byte_len + 31) // 32 chunks = [node.getter(to_gindex(i, depth)) for i in range(chunk_count)] bytez = b"".join(ch.merkle_root() for ch in chunks)[:byte_len] return cls.decode_bytes(bytez)
def key_to_static_gindex(cls, key: Any) -> Gindex: if key < 0: raise KeyError if cls.is_packed(): elems_per_chunk = 32 // cls.element_cls().type_byte_length() chunk_i = key // elems_per_chunk else: chunk_i = key return to_gindex(chunk_i, cls.tree_depth())
def view_from_backing(cls: Type[BL], node: Node, hook: Optional[ViewHook] = None) -> BL: contents_depth = cls.contents_depth() contents_node = node.get_left() length = uint256.view_from_backing(node.get_right()) if length > cls.limit(): raise Exception("ByteList backing declared length exceeds limit") if contents_depth == 0: return cls.decode_bytes(contents_node.root[:length]) else: chunk_count = (length + 31) // 32 chunks = [contents_node.getter(to_gindex(i, contents_depth)) for i in range(chunk_count)] bytez = b"".join(ch.root for ch in chunks)[:length] return cls.decode_bytes(bytez)
def set(self, i: int, v: View) -> None: elem_type: Type[View] = self.item_elem_cls(i) # if not the right type, try to coerce it if not isinstance(v, elem_type): v = elem_type.coerce_view(v) if self.is_packed(): # basic types are more complicated: we operate on a subsection of a bottom chunk if isinstance(v, BasicView): elems_per_chunk = 32 // v.type_byte_length() chunk_i = i // elems_per_chunk target = to_gindex(chunk_i, self.tree_depth()) chunk_setter_link: Link = self.get_backing().setter(target) chunk = self.get_backing().getter(target) new_chunk = v.backing_from_base(chunk, i % elems_per_chunk) self.set_backing(chunk_setter_link(new_chunk)) else: raise Exception( "cannot pack subtree elements that are not basic types") else: setter_link: Link = self.get_backing().setter( to_gindex(i, self.tree_depth())) self.set_backing(setter_link(v.get_backing()))
def append(self, v: View): ll = self.length() if ll >= self.__class__.limit(): raise Exception("list is maximum capacity, cannot append") i = ll elem_type: Type[View] = self.__class__.element_cls() if not isinstance(v, elem_type): v = elem_type.coerce_view(v) if self.__class__.is_packed(): next_backing = self.get_backing() if isinstance(elem_type, BasicTypeDef): if not isinstance(v, BasicView): raise Exception("input element is not a basic view") basic_v: BasicView = v elems_per_chunk = 32 // elem_type.type_byte_length() chunk_i = i // elems_per_chunk target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i % elems_per_chunk == 0: set_last = next_backing.setter(target, expand=True) chunk = zero_node(0) else: set_last = next_backing.setter(target) chunk = next_backing.getter(target) chunk = basic_v.backing_from_base(chunk, i % elems_per_chunk) next_backing = set_last(chunk) else: raise Exception( "cannot append a packed element that is not a basic type") else: target: Gindex = to_gindex(i, self.__class__.tree_depth()) set_last = self.get_backing().setter(target, expand=True) next_backing = set_last(v.get_backing()) set_length = next_backing.rebind_right new_length = uint256(ll + 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def append(self, v: boolean): ll = self.length() if ll >= self.__class__.limit(): raise Exception("list is maximum capacity, cannot append") i = ll chunk_i = i // 256 target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i & 0xff == 0: set_last = self.get_backing().setter(target, expand=True) next_backing = set_last(_new_chunk_with_bit(zero_node(0), 0, v)) else: set_last = self.get_backing().setter(target) chunk = self.get_backing().getter(target) next_backing = set_last(_new_chunk_with_bit(chunk, i & 0xff, v)) set_length = next_backing.rebind_right new_length = uint256(ll + 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)