def f(i): b = B() lst = [i] lst[0] += 1 b.y = lst ll_assert(b.y is lst, "copying when reading out the attr?") return b.y[0]
def ll_compress(fnptr): for c, p in unroll_table: if fnptr == p: return c else: ll_assert(fnptr == last_p, "unexpected function pointer") return last_c
def ll_grow_and_append(ll_builder, ll_str, start, size): # A fast-path, meant for builders that only receive a single big # string before build() is called. Also works in some other cases. if (size > 1280 and ll_builder.current_pos == 0 and start == 0 and size == len(ll_str.chars)): try: total_size = ovfcheck(ll_builder.total_size + size) except OverflowError: pass else: PIECE = lltype.typeOf(ll_builder.extra_pieces).TO old_piece = lltype.malloc(PIECE) old_piece.buf = ll_str old_piece.prev_piece = ll_builder.extra_pieces ll_builder.total_size = total_size ll_builder.extra_pieces = old_piece return # First, the part that still fits in the current piece part1 = ll_builder.current_end - ll_builder.current_pos ll_assert(part1 < size, "part1 >= size") ll_builder.copy_string_contents(ll_str, ll_builder.current_buf, start, ll_builder.current_pos, part1) start += part1 size -= part1 # Allocate the new piece ll_grow_by(ll_builder, size) ll_assert(ll_builder.current_pos == 0, "current_pos must be 0 after grow()") # Finally, the second part of the string ll_builder.current_pos = size ll_builder.copy_string_contents(ll_str, ll_builder.current_buf, start, 0, size)
def _ll_dict_setitem_lookup_done(d, key, value, hash, i): valid = (i & HIGHEST_BIT) == 0 i = i & MASK ENTRY = lltype.typeOf(d.entries).TO.OF entry = d.entries[i] if not d.entries.everused(i): # a new entry that was never used before ll_assert(not valid, "valid but not everused") rc = d.resize_counter - 3 if rc <= 0: # if needed, resize the dict -- before the insertion ll_dict_resize(d) i = ll_dict_lookup_clean(d, hash) # then redo the lookup for 'key' entry = d.entries[i] rc = d.resize_counter - 3 ll_assert(rc > 0, "ll_dict_resize failed?") d.resize_counter = rc if hasattr(ENTRY, 'f_everused'): entry.f_everused = True entry.value = value else: # override an existing or deleted entry entry.value = value if valid: return entry.key = key if hasattr(ENTRY, 'f_hash'): entry.f_hash = hash if hasattr(ENTRY, 'f_valid'): entry.f_valid = True d.num_items += 1
def ll_listslice_startonly(RESLIST, l1, start): len1 = l1.ll_length() ll_assert(start >= 0, "unexpectedly negative list slice start") ll_assert(start <= len1, "list slice start larger than list length") newlength = len1 - start l = RESLIST.ll_newlist(newlength) ll_arraycopy(l1, l, start, 0, newlength) return l
def ll_shrink_final(ll_builder): final_size = ll_builder.current_pos ll_assert(final_size <= ll_builder.total_size, "final_size > ll_builder.total_size?") buf = rgc.ll_shrink_array(ll_builder.current_buf, final_size) ll_builder.current_buf = buf ll_builder.current_end = final_size ll_builder.total_size = final_size
def ll_pop_nonneg(func, l, index): ll_assert(index >= 0, "unexpectedly negative list pop index") if func is dum_checkidx: if index >= l.ll_length(): raise IndexError else: ll_assert(index < l.ll_length(), "list pop index out of bound") res = l.ll_getitem_fast(index) ll_delitem_nonneg(dum_nocheck, l, index) return res
def ll_insert_nonneg(l, index, newitem): length = l.ll_length() ll_assert(0 <= index, "negative list insertion index") ll_assert(index <= length, "list insertion index out of bound") l._ll_resize_ge(length + 1) # see "a note about overflows" above dst = length while dst > index: src = dst - 1 l.ll_setitem_fast(dst, l.ll_getitem_fast(src)) dst = src l.ll_setitem_fast(index, newitem)
def ll_listdelslice_startonly(l, start): ll_assert(start >= 0, "del l[start:] with unexpectedly negative start") ll_assert(start <= l.ll_length(), "del l[start:] with start > len(l)") newlength = start null = ll_null_item(l) if null is not None: j = l.ll_length() - 1 while j >= newlength: l.ll_setitem_fast(j, null) j -= 1 l._ll_resize_le(newlength)
def ll_pop_default(func, l): length = l.ll_length() if func is dum_checkidx and (length == 0): raise IndexError ll_assert(length > 0, "pop from empty list") index = length - 1 newlength = index res = l.ll_getitem_fast(index) null = ll_null_item(l) if null is not None: l.ll_setitem_fast(index, null) l._ll_resize_le(newlength) return res
def ll_pop(func, l, index): length = l.ll_length() if index < 0: index += length if func is dum_checkidx: if index < 0 or index >= length: raise IndexError else: ll_assert(index >= 0, "negative list pop index out of bound") ll_assert(index < length, "list pop index out of bound") res = l.ll_getitem_fast(index) ll_delitem_nonneg(dum_nocheck, l, index) return res
def ll_setitem(func, l, index, newitem): if func is dum_checkidx: length = l.ll_length() if r_uint(index) >= r_uint(length): # see comments in ll_getitem(). index = r_uint(index) + r_uint(length) if index >= r_uint(length): raise IndexError index = intmask(index) else: if index < 0: index += l.ll_length() ll_assert(index >= 0, "negative list setitem index out of bound") l.ll_setitem_fast(index, newitem)
def ll_fold_pieces(ll_builder): final_size = BaseStringBuilderRepr.ll_getlength(ll_builder) ll_assert(final_size >= 0, "negative final_size") extra = ll_builder.extra_pieces ll_builder.extra_pieces = lltype.nullptr(lltype.typeOf(extra).TO) # result = ll_builder.mallocfn(final_size) piece = ll_builder.current_buf piece_lgt = ll_builder.current_pos ll_assert(ll_builder.current_end == len(piece.chars), "bogus last piece_lgt") ll_builder.total_size = final_size ll_builder.current_buf = result ll_builder.current_pos = final_size ll_builder.current_end = final_size dst = final_size while True: dst -= piece_lgt ll_assert(dst >= 0, "rbuilder build: overflow") ll_builder.copy_string_contents(piece, result, 0, dst, piece_lgt) if not extra: break piece = extra.buf piece_lgt = len(piece.chars) extra = extra.prev_piece ll_assert(dst == 0, "rbuilder build: underflow")
def copy_string_contents(src, dst, srcstart, dststart, length): """Copies 'length' characters from the 'src' string to the 'dst' string, starting at position 'srcstart' and 'dststart'.""" # xxx Warning: don't try to do this at home. It relies on a lot # of details to be sure that it works correctly in all cases. # Notably: no GC operation at all from the first cast_ptr_to_adr() # because it might move the strings. The keepalive_until_here() # are obscurely essential to make sure that the strings stay alive # longer than the raw_memcopy(). assert length >= 0 ll_assert(srcstart >= 0, "copystrc: negative srcstart") ll_assert(srcstart + length <= len(src.chars), "copystrc: src ovf") ll_assert(dststart >= 0, "copystrc: negative dststart") ll_assert(dststart + length <= len(dst.chars), "copystrc: dst ovf") # # If the 'split_gc_address_space' option is set, we must copy # manually, character-by-character if rgc.must_split_gc_address_space(): i = 0 while i < length: dst.chars[dststart + i] = src.chars[srcstart + i] i += 1 return # # # from here, no GC operations can happen asrc = _get_raw_buf(SRC_TP, src, srcstart) adst = _get_raw_buf(DST_TP, dst, dststart) llmemory.raw_memcopy(asrc, adst, llmemory.sizeof(CHAR_TP) * length) # end of "no GC" section keepalive_until_here(src) keepalive_until_here(dst)
def ll_grow_and_append(ll_builder, ll_str, start, size): # First, the part that still fits in the current piece part1 = ll_builder.current_end - ll_builder.current_pos ll_assert(part1 < size, "part1 >= size") ll_builder.copy_string_contents(ll_str, ll_builder.current_buf, start, ll_builder.current_pos, part1) start += part1 size -= part1 # Allocate the new piece ll_grow_by(ll_builder, size) ll_assert(ll_builder.current_pos == 0, "current_pos must be 0 after grow()") # Finally, the second part of the string ll_builder.current_pos = size ll_builder.copy_string_contents(ll_str, ll_builder.current_buf, start, 0, size)
def ll_getitem(func, basegetitem, l, index): if func is dum_checkidx: length = l.ll_length() # common case: 0 <= index < length if r_uint(index) >= r_uint(length): # Failed, so either (-length <= index < 0), or we have to raise # IndexError. First add 'length' to get the final index, then # check that we now have (0 <= index < length). index = r_uint(index) + r_uint(length) if index >= r_uint(length): raise IndexError index = intmask(index) else: # We don't want checking, but still want to support index < 0. # Only call ll_length() if needed. if index < 0: index += l.ll_length() ll_assert(index >= 0, "negative list getitem index out of bound") return basegetitem(l, index)
def ll_pop_zero(func, l): length = l.ll_length() if func is dum_checkidx and (length == 0): raise IndexError ll_assert(length > 0, "pop(0) from empty list") newlength = length - 1 res = l.ll_getitem_fast(0) j = 0 j1 = j + 1 while j < newlength: l.ll_setitem_fast(j, l.ll_getitem_fast(j1)) j = j1 j1 += 1 null = ll_null_item(l) if null is not None: l.ll_setitem_fast(newlength, null) l._ll_resize_le(newlength) return res
def ll_delitem_nonneg(func, l, index): ll_assert(index >= 0, "unexpectedly negative list delitem index") length = l.ll_length() if func is dum_checkidx: if index >= length: raise IndexError else: ll_assert(index < length, "list delitem index out of bound") newlength = length - 1 j = index j1 = j + 1 while j < newlength: l.ll_setitem_fast(j, l.ll_getitem_fast(j1)) j = j1 j1 += 1 null = ll_null_item(l) if null is not None: l.ll_setitem_fast(newlength, null) l._ll_resize_le(newlength)
def ll_grow_by(ll_builder, needed): try: needed = ovfcheck(needed + ll_builder.total_size) needed = ovfcheck(needed + 63) & ~63 total_size = ll_builder.total_size + needed except OverflowError: raise MemoryError # new_string = ll_builder.mallocfn(needed) # PIECE = lltype.typeOf(ll_builder.extra_pieces).TO old_piece = lltype.malloc(PIECE) old_piece.buf = ll_builder.current_buf old_piece.prev_piece = ll_builder.extra_pieces ll_assert(bool(old_piece.buf), "no buf??") ll_builder.current_buf = new_string ll_builder.current_pos = 0 ll_builder.current_end = needed ll_builder.total_size = total_size ll_builder.extra_pieces = old_piece
def ll_extend_with_str_slice_startonly(lst, s, getstrlen, getstritem, start): len1 = lst.ll_length() len2 = getstrlen(s) count2 = len2 - start ll_assert(start >= 0, "unexpectedly negative str slice start") assert count2 >= 0, "str slice start larger than str length" try: newlength = ovfcheck(len1 + count2) except OverflowError: raise MemoryError lst._ll_resize_ge(newlength) i = start j = len1 while i < len2: c = getstritem(s, i) if listItemType(lst) is UniChar: c = unichr(ord(c)) lst.ll_setitem_fast(j, c) i += 1 j += 1
def _ll_list_resize_hint_really(l, newsize, overallocate): """ Ensure l.items has room for at least newsize elements. Note that l.items may change, and even if newsize is less than l.length on entry. """ # This over-allocates proportional to the list size, making room # for additional growth. The over-allocation is mild, but is # enough to give linear-time amortized behavior over a long # sequence of appends() in the presence of a poorly-performing # system malloc(). # The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... if newsize <= 0: ll_assert(newsize == 0, "negative list length") l.length = 0 l.items = _ll_new_empty_item_array(typeOf(l).TO) return elif overallocate: if newsize < 9: some = 3 else: some = 6 some += newsize >> 3 new_allocated = newsize + some else: new_allocated = newsize # new_allocated is a bit more than newsize, enough to ensure an amortized # linear complexity for e.g. repeated usage of l.append(). In case # it overflows sys.maxint, it is guaranteed negative, and the following # malloc() will fail. items = l.items newitems = malloc(typeOf(l).TO.items.TO, new_allocated) before_len = l.length if before_len: # avoids copying GC flags from the prebuilt_empty_array if before_len < newsize: p = before_len else: p = newsize rgc.ll_arraycopy(items, newitems, 0, 0, p) l.items = newitems
def ll_fold_pieces(ll_builder): final_size = BaseStringBuilderRepr.ll_getlength(ll_builder) ll_assert(final_size >= 0, "negative final_size") extra = ll_builder.extra_pieces ll_builder.extra_pieces = lltype.nullptr(lltype.typeOf(extra).TO) # # A fast-path if the builder contains exactly one big piece: # discard the allocated current_buf and put the big piece there if ll_builder.current_pos == 0 and not extra.prev_piece: piece = extra.buf ll_assert(final_size == len(piece.chars), "bogus final_size in fold_pieces") ll_builder.total_size = final_size ll_builder.current_buf = piece ll_builder.current_pos = final_size ll_builder.current_end = final_size return # result = ll_builder.mallocfn(final_size) piece = ll_builder.current_buf piece_lgt = ll_builder.current_pos ll_assert(ll_builder.current_end == len(piece.chars), "bogus last piece_lgt") ll_builder.total_size = final_size ll_builder.current_buf = result ll_builder.current_pos = final_size ll_builder.current_end = final_size dst = final_size while True: dst -= piece_lgt ll_assert(dst >= 0, "rbuilder build: overflow") ll_builder.copy_string_contents(piece, result, 0, dst, piece_lgt) if not extra: break piece = extra.buf piece_lgt = len(piece.chars) extra = extra.prev_piece ll_assert(dst == 0, "rbuilder build: underflow")
def ll_listslice_startstop(RESLIST, l1, start, stop): length = l1.ll_length() ll_assert(start >= 0, "unexpectedly negative list slice start") ll_assert(start <= length, "list slice start larger than list length") ll_assert(stop >= start, "list slice stop smaller than start") if stop > length: stop = length newlength = stop - start l = RESLIST.ll_newlist(newlength) ll_arraycopy(l1, l, start, 0, newlength) return l
def copy_string_contents(src, dst, srcstart, dststart, length): """Copies 'length' characters from the 'src' string to the 'dst' string, starting at position 'srcstart' and 'dststart'.""" # xxx Warning: don't try to do this at home. It relies on a lot # of details to be sure that it works correctly in all cases. # Notably: no GC operation at all from the first cast_ptr_to_adr() # because it might move the strings. The keepalive_until_here() # are obscurely essential to make sure that the strings stay alive # longer than the raw_memcopy(). assert length >= 0 ll_assert(srcstart >= 0, "copystrc: negative srcstart") ll_assert(srcstart + length <= len(src.chars), "copystrc: src ovf") ll_assert(dststart >= 0, "copystrc: negative dststart") ll_assert(dststart + length <= len(dst.chars), "copystrc: dst ovf") # from here, no GC operations can happen asrc = _get_raw_buf(SRC_TP, src, srcstart) adst = _get_raw_buf(DST_TP, dst, dststart) llmemory.raw_memcopy(asrc, adst, llmemory.sizeof(CHAR_TP) * length) # end of "no GC" section keepalive_until_here(src) keepalive_until_here(dst)
def ll_listsetslice(l1, start, stop, l2): len1 = l1.ll_length() len2 = l2.ll_length() ll_assert(start >= 0, "l[start:x] = l with unexpectedly negative start") ll_assert(start <= len1, "l[start:x] = l with start > len(l)") ll_assert(stop <= len1, "stop cannot be past the end of l1") if len2 == stop - start: ll_arraycopy(l2, l1, 0, start, len2) elif len2 < stop - start: ll_arraycopy(l2, l1, 0, start, len2) ll_arraycopy(l1, l1, stop, start + len2, len1 - stop) l1._ll_resize_le(len1 + len2 - (stop - start)) else: # len2 > stop - start: try: newlength = ovfcheck(len1 + len2) except OverflowError: raise MemoryError l1._ll_resize_ge(newlength) ll_arraycopy(l1, l1, stop, start + len2, len1 - stop) ll_arraycopy(l2, l1, 0, start, len2)
def ll_listdelslice_startstop(l, start, stop): length = l.ll_length() ll_assert(start >= 0, "del l[start:x] with unexpectedly negative start") ll_assert(start <= length, "del l[start:x] with start > len(l)") ll_assert(stop >= start, "del l[x:y] with x > y") if stop > length: stop = length newlength = length - (stop - start) j = start i = stop while j < newlength: l.ll_setitem_fast(j, l.ll_getitem_fast(i)) i += 1 j += 1 null = ll_null_item(l) if null is not None: j = length - 1 while j >= newlength: l.ll_setitem_fast(j, null) j -= 1 l._ll_resize_le(newlength)
def ll_setitem_fast(l, index, item): ll_assert(index < l.length, "setitem out of bounds") l.ll_items()[index] = item
def ll_fixed_newlist(LIST, length): ll_assert(length >= 0, "negative fixed list length") l = malloc(LIST, length) return l
def ll_fixed_getitem_fast(l, index): ll_assert(index < len(l), "fixed getitem out of bounds") return l[index]
def ll_newlist(LIST, length): ll_assert(length >= 0, "negative list length") l = malloc(LIST) l.length = length l.items = malloc(LIST.items.TO, length) return l
def ll_strfasthash(s): ll_assert(s.hash != 0, "ll_strfasthash: hash==0") return s.hash # assumes that the hash is already computed
def ll_stritem_nonneg(s, i): chars = s.chars ll_assert(i >= 0, "negative str getitem index") ll_assert(i < len(chars), "str getitem index out of bound") return chars[i]
def mallocstr(length): ll_assert(length >= 0, "negative string length") r = malloc(TP, length) if not we_are_translated() or not malloc_zero_filled: r.hash = 0 return r
def ll_list2fixed_exact(l): ll_assert(l.length == len(l.items), "ll_list2fixed_exact: bad length") return l.items
def ll_getitem_fast(l, index): ll_assert(index < l.length, "getitem out of bounds") return l.ll_items()[index]
def ll_fixed_setitem_fast(l, index, item): ll_assert(index < len(l), "fixed setitem out of bounds") l[index] = item
def ll_newlist_hint(LIST, lengthhint): ll_assert(lengthhint >= 0, "negative list length") l = malloc(LIST) l.length = 0 l.items = malloc(LIST.items.TO, lengthhint) return l
def ll_getitem_foldable_nonneg(l, index): ll_assert(index >= 0, "unexpectedly negative list getitem index") return l.ll_getitem_fast(index)
def ll_strsetitem_nonneg(s, i, item): chars = s.chars ll_assert(i >= 0, "negative str getitem index") ll_assert(i < len(chars), "str getitem index out of bound") chars[i] = chr(item)
def ll_setitem_nonneg(func, l, index, newitem): ll_assert(index >= 0, "unexpectedly negative list setitem index") if func is dum_checkidx: if index >= l.ll_length(): raise IndexError l.ll_setitem_fast(index, newitem)