def test_signedness(): # 0000001 item_count<<1 # 7 item_size # ffffffe0 offset<<2 # 1 kind p = 0x00000017ffffffe1 assert ptr.offset(p) == -8
def test_new_struct(): p = ptr.new_struct(100, 2, 4) assert ptr.kind(p) == ptr.STRUCT assert ptr.offset(p) == 100 assert ptr.struct_data_size(p) == 2 assert ptr.struct_ptrs_size(p) == 4 assert p == 0x0004000200000190
def test_new_list(): p = ptr.new_list(64, 7, 200) assert ptr.kind(p) == ptr.LIST assert ptr.offset(p) == 64 assert ptr.list_size_tag(p) == 7 assert ptr.list_item_count(p) == 200 assert p == 0x0000064700000101
def test_kind_offset(): p = 0x0004000200000190 assert ptr.kind(p) == ptr.STRUCT assert ptr.offset(p) == 100 # p2 = 0x0000064700000101 assert ptr.kind(p2) == ptr.LIST
def visit(self, buf, p, offset): kind = ptr.kind(p) offset = ptr.deref(p, offset) if kind == ptr.STRUCT: data_size = ptr.struct_data_size(p) ptrs_size = ptr.struct_ptrs_size(p) return self.visit_struct(buf, p, offset, data_size, ptrs_size) elif kind == ptr.LIST: item_size = ptr.list_size_tag(p) count = ptr.list_item_count(p) if item_size == ptr.LIST_SIZE_COMPOSITE: tag = buf.read_ptr(offset) count = ptr.offset(tag) data_size = ptr.struct_data_size(tag) ptrs_size = ptr.struct_ptrs_size(tag) return self.visit_list_composite(buf, p, offset, count, data_size, ptrs_size) elif item_size == ptr.LIST_SIZE_PTR: return self.visit_list_ptr(buf, p, offset, count) elif item_size == ptr.LIST_SIZE_BIT: return self.visit_list_bit(buf, p, offset, count) else: return self.visit_list_primitive(buf, p, offset, item_size, count) elif kind == ptr.FAR: raise NotImplementedError('Far pointer not supported') else: assert False, 'unknown ptr kind'
def _copy_list_composite(src, p, src_pos, dst, dst_pos): src_pos = ptr.deref(p, src_pos) total_words = ptr.list_item_count(p) # n of words NOT including the tag body_length = (total_words + 1) * 8 # total length INCLUDING the tag # # check that there is enough data for both the tag AND the whole body; # this way we do the bound checking only once check_bounds(src, body_length, src_pos) tag = read_int64_fast(src, src_pos) count = ptr.offset(tag) data_size = ptr.struct_data_size(tag) ptrs_size = ptr.struct_ptrs_size(tag) # # allocate the list and copy the whole body at once dst_pos = dst.alloc_list(dst_pos, ptr.LIST_SIZE_COMPOSITE, total_words, body_length) dst.write_slice(dst_pos, src, src_pos, body_length) # # iterate over the elements, fix the pointers and copy the content i = 0 item_length = (data_size + ptrs_size) * 8 ptrs_section_offset = 0 for i in range(count): ptrs_section_offset = 8 + item_length * i + data_size * 8 _copy_many_ptrs(ptrs_size, src, src_pos + ptrs_section_offset, dst, dst_pos + ptrs_section_offset)
def _split(self, extra_offset): """ Split the body and the extra part. The extra part must be placed at the specified offset, in words. The ptrs in the body will be adjusted accordingly. """ body_start = self._get_body_start() body_end = self._get_body_end() if self._ptrs_size == 0: # easy case, just copy the body return self._buf.s[body_start:body_end], '' # # hard case. The layout of self._buf is like this: # +----------+------+------+----------+-------------+ # | garbage0 | data | ptrs | garbage1 | extra | # +----------+------+------+----------+-------------+ # | | ^ ^ # +-----------------+ | # | | # +-------------------+ # # We recompute the pointers assumining len(garbage1) == extra_offset # # 1) the data section is copied verbatim # 2) the offset of pointers in ptrs are adjusted # 3) extra is copied verbatim # extra_start = self._get_extra_start() extra_end = self._get_end() # # 1) data section data_size = self._data_size data_buf = self._buf.s[body_start:body_start+data_size*8] # # 2) ptrs section # for each ptr: # ptr.offset += (extra_offset - old_extra_offset)/8 # # NOTE: ptr.offset is in words, extra_start and body_end in bytes old_extra_offset = (extra_start - body_end)/8 additional_offset = extra_offset - old_extra_offset # # iterate over and fix the pointers parts = [data_buf] for j in range(self._ptrs_size): # read pointer, update its offset, and pack it p = self._read_raw_ptr(j*8) if p != 0: assert ptr.kind(p) != ptr.FAR p = ptr.new_generic(ptr.kind(p), ptr.offset(p)+additional_offset, ptr.extra(p)) s = struct.pack('q', p) parts.append(s) # body_buf = ''.join(parts) # 3) extra part extra_buf = self._buf.s[extra_start:extra_end] # return body_buf, extra_buf
def test_ListPtr(): # 0000064 item_count<<1 # 7 item_size # 00000100 offset<<2 # 1 kind p = 0x0000064700000101 assert ptr.kind(p) == ptr.LIST assert ptr.offset(p) == 64 assert ptr.list_size_tag(p) == 7 assert ptr.list_item_count(p) == 200
def test_struct_ptr(): # 0004 ptrs size # 0002 data size # 00000190 offset<<2 # 0 kind p = 0x0004000200000190 assert ptr.kind(p) == ptr.STRUCT assert ptr.offset(p) == 100 assert ptr.struct_data_size(p) == 2 assert ptr.struct_ptrs_size(p) == 4
def _set_list_tag(self, size_tag, item_count): self._size_tag = size_tag if size_tag == ptr.LIST_SIZE_COMPOSITE: tag = self._seg.read_ptr(self._offset) self._tag = tag self._item_count = ptr.offset(tag) self._item_length = (ptr.struct_data_size(tag)+ptr.struct_ptrs_size(tag))*8 self._item_offset = 8 else: self._tag = -1 self._item_count = item_count self._item_length = ptr.list_item_length(size_tag) self._item_offset = 0
def ptr(self, offset, s): p = struct.unpack('q', s)[0] if ptr.kind(p) not in (ptr.STRUCT, ptr.LIST, ptr.FAR): return ' ' * 25 # # try to display only "reasonable" ptrs; if the fields are too big, it # probably means that the current word is not a pointer def if_in_range(x, min, max): if min <= x < max: return str(x) else: return '?' # if p == 0: return 'NULL'.ljust(25) if ptr.kind(p) == ptr.STRUCT: descr = 'struct {:>4} {:>3}'.format( if_in_range(ptr.struct_data_size(p), 0, 100), if_in_range(ptr.struct_ptrs_size(p), 0, 100)) elif ptr.kind(p) == ptr.LIST: tag = '<%s>' % self._list_tag(ptr.list_size_tag(p)) descr = 'list{:<5} {:>5}'.format( tag, if_in_range(ptr.list_item_count(p), 0, 65536)) elif ptr.kind(p) == ptr.FAR: descr = 'far {:>7} {:>3}'.format( ptr.far_landing_pad(p), if_in_range(ptr.far_target(p), 0, 100)) else: descr = 'unknown ptr ' # if -1000 < ptr.offset(p) < 1000: dest = ptr.deref(p, offset) dest = self.addr(dest) dest = dest.ljust(16) else: dest = '? ' line = '{0} to {1}'.format(descr, dest) if '?' in line: return Color.set(Color.lightgray, line) else: return line
def endof(seg, p, offset): """ Check whether the given object is compact, and in that case compute its end boundary. If it's not compact, return -1. An object is compact if: 1. there is no gap between its data section and its ptrs section 2. there is no gap between children 3. its children are compact 4. there are no FAR pointers """ kind = ptr.kind(p) offset = ptr.deref(p, offset) if kind == ptr.STRUCT: data_size = ptr.struct_data_size(p) ptrs_size = ptr.struct_ptrs_size(p) return _endof_struct(seg, p, offset, data_size, ptrs_size) elif kind == ptr.LIST: item_size = ptr.list_size_tag(p) count = ptr.list_item_count(p) if item_size == ptr.LIST_SIZE_COMPOSITE: tag = seg.read_ptr(offset) count = ptr.offset(tag) data_size = ptr.struct_data_size(tag) ptrs_size = ptr.struct_ptrs_size(tag) return _endof_list_composite(seg, p, offset, count, data_size, ptrs_size) elif item_size == ptr.LIST_SIZE_PTR: return _endof_list_ptr(seg, p, offset, count) elif item_size == ptr.LIST_SIZE_BIT: return _endof_list_bit(seg, p, offset, count) else: return _endof_list_primitive(seg, p, offset, item_size, count) elif kind == ptr.FAR: return -1 else: assert False, 'unknown ptr kind'
def read_far_ptr(self, offset): """ Read and return the ptr referenced by this far pointer """ p = self.read_ptr(offset) segment_start = self.segment_offsets[ptr.far_target(p)] # in bytes offset = segment_start + ptr.far_offset(p) * 8 if ptr.far_landing_pad(p) == 0: # simple case: the landing pad is a normal pointer, just read it p = self.read_ptr(offset) return offset, p else: # complex case. From capnproto specs: # If B == 1, then the "landing pad" is itself another far # pointer that is interpreted differently: This far pointer # (which always has B = 0) points to the start of the object's # content, located in some other segment. The landing pad is # itself immediately followed by a tag word. The tag word # looks exactly like an intra-segment pointer to the target # object would look, except that the offset is always zero. # # read the 2nd far pointer and the tag word p = self.read_ptr(offset) ptag = self.read_ptr(offset + 8) assert ptr.kind(p) == ptr.FAR assert ptr.far_landing_pad(p) == 0 assert ptr.offset(ptag) == 0 # compute the absolute offset which the object is located at segment_start = self.segment_offsets[ptr.far_target(p)] # in bytes offset = segment_start + ptr.far_offset(p) * 8 # # ptag is a pointer which perfectly describes the object we want # to read. Remember that normally when ptr_offset==0, capnproto # expects the object to start at offset+8. So here we return # offset-8, so that the object will be read at the expected place return offset - 8, ptag
def test_deref(): p = 0x0004000200000190 assert ptr.offset(p) == 100 offset = ptr.deref(p, 8) assert offset == 816
def unpack(p): assert ptr.kind(p) == ptr.LIST return ptr.offset(p), ptr.list_size_tag(p), ptr.list_item_count(p)
def unpack(p): assert ptr.kind(p) == ptr.STRUCT return ptr.offset(p), ptr.struct_data_size(p), ptr.struct_ptrs_size(p)