def __init__(self, rtyper, r_key): self.rtyper = rtyper self.r_key = r_key fasthashfn = r_key.get_ll_fasthash_function() self.ll_keyhash = r_key.get_ll_hash_function() ll_keyeq = lltype.staticAdtMethod(r_key.get_ll_eq_function()) def ll_valid(entries, i): value = entries[i].value return bool(value) and bool(weakref_deref(rclass.OBJECTPTR, value)) def ll_everused(entries, i): return bool(entries[i].value) def ll_hash(entries, i): return fasthashfn(entries[i].key) entrymeths = { 'allocate': lltype.typeMethod(rdict._ll_malloc_entries), 'delete': rdict._ll_free_entries, 'valid': ll_valid, 'everused': ll_everused, 'hash': ll_hash, } WEAKDICTENTRY = lltype.Struct("weakdictentry", ("key", r_key.lowleveltype), ("value", llmemory.WeakRefPtr)) WEAKDICTENTRYARRAY = lltype.GcArray(WEAKDICTENTRY, adtmeths=entrymeths, hints={'weakarray': 'value'}) # NB. the 'hints' is not used so far ^^^ dictmeths = { 'll_get': self.ll_get, 'll_set': self.ll_set, 'keyeq': ll_keyeq, 'paranoia': False, } self.WEAKDICT = lltype.GcStruct( "weakvaldict", ("num_items", lltype.Signed), ("resize_counter", lltype.Signed), ("entries", lltype.Ptr(WEAKDICTENTRYARRAY)), adtmeths=dictmeths) self.lowleveltype = lltype.Ptr(self.WEAKDICT) self.dict_cache = {}
def _setup_repr(self): if 'key_repr' not in self.__dict__: key_repr = self._key_repr_computer() self.external_key_repr, self.key_repr = self.pickkeyrepr(key_repr) if 'value_repr' not in self.__dict__: self.external_value_repr, self.value_repr = self.pickrepr(self._value_repr_computer()) if isinstance(self.DICT, lltype.GcForwardReference): self.DICTKEY = self.key_repr.lowleveltype self.DICTVALUE = self.value_repr.lowleveltype # compute the shape of the DICTENTRY structure entryfields = [] entrymeths = { 'allocate': lltype.typeMethod(_ll_malloc_entries), 'delete': _ll_free_entries, 'must_clear_key': (isinstance(self.DICTKEY, lltype.Ptr) and self.DICTKEY._needsgc()), 'must_clear_value': (isinstance(self.DICTVALUE, lltype.Ptr) and self.DICTVALUE._needsgc()), } # * the key entryfields.append(("key", self.DICTKEY)) # * if NULL is not a valid ll value for the key or the value # field of the entry, it can be used as a marker for # never-used entries. Otherwise, we need an explicit flag. s_key = self.dictkey.s_value s_value = self.dictvalue.s_value nullkeymarker = not self.key_repr.can_ll_be_null(s_key) nullvaluemarker = not self.value_repr.can_ll_be_null(s_value) if self.force_non_null: if not nullkeymarker: rmodel.warning("%s can be null, but forcing non-null in dict key" % s_key) nullkeymarker = True if not nullvaluemarker: rmodel.warning("%s can be null, but forcing non-null in dict value" % s_value) nullvaluemarker = True dummykeyobj = self.key_repr.get_ll_dummyval_obj(self.rtyper, s_key) dummyvalueobj = self.value_repr.get_ll_dummyval_obj(self.rtyper, s_value) # * the state of the entry - trying to encode it as dummy objects if nullkeymarker and dummykeyobj: # all the state can be encoded in the key entrymeths['everused'] = ll_everused_from_key entrymeths['dummy_obj'] = dummykeyobj entrymeths['valid'] = ll_valid_from_key entrymeths['mark_deleted'] = ll_mark_deleted_in_key # the key is overwritten by 'dummy' when the entry is deleted entrymeths['must_clear_key'] = False elif nullvaluemarker and dummyvalueobj: # all the state can be encoded in the value entrymeths['everused'] = ll_everused_from_value entrymeths['dummy_obj'] = dummyvalueobj entrymeths['valid'] = ll_valid_from_value entrymeths['mark_deleted'] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_value'] = False else: # we need a flag to know if the entry was ever used # (we cannot use a NULL as a marker for this, because # the key and value will be reset to NULL to clear their # reference) entryfields.append(("f_everused", lltype.Bool)) entrymeths['everused'] = ll_everused_from_flag # can we still rely on a dummy obj to mark deleted entries? if dummykeyobj: entrymeths['dummy_obj'] = dummykeyobj entrymeths['valid'] = ll_valid_from_key entrymeths['mark_deleted'] = ll_mark_deleted_in_key # key is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_key'] = False elif dummyvalueobj: entrymeths['dummy_obj'] = dummyvalueobj entrymeths['valid'] = ll_valid_from_value entrymeths['mark_deleted'] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_value'] = False else: entryfields.append(("f_valid", lltype.Bool)) entrymeths['valid'] = ll_valid_from_flag entrymeths['mark_deleted'] = ll_mark_deleted_in_flag # * the value entryfields.append(("value", self.DICTVALUE)) # * the hash, if needed if self.custom_eq_hash: fasthashfn = None else: fasthashfn = self.key_repr.get_ll_fasthash_function() if getattr(self.key_repr.get_ll_eq_function(), 'no_direct_compare', False): entrymeths['no_direct_compare'] = True if fasthashfn is None: entryfields.append(("f_hash", lltype.Signed)) entrymeths['hash'] = ll_hash_from_cache else: entrymeths['hash'] = ll_hash_recomputed entrymeths['fasthashfn'] = fasthashfn # Build the lltype data structures self.DICTENTRY = lltype.Struct("dictentry", *entryfields) self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY, adtmeths=entrymeths) fields = [ ("num_items", lltype.Signed), ("resize_counter", lltype.Signed), ("entries", lltype.Ptr(self.DICTENTRYARRAY)) ] if self.custom_eq_hash: self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr() fields.extend([ ("fnkeyeq", self.r_rdict_eqfn.lowleveltype), ("fnkeyhash", self.r_rdict_hashfn.lowleveltype) ]) adtmeths = { 'keyhash': ll_keyhash_custom, 'keyeq': ll_keyeq_custom, 'r_rdict_eqfn': self.r_rdict_eqfn, 'r_rdict_hashfn': self.r_rdict_hashfn, 'paranoia': True, } else: # figure out which functions must be used to hash and compare ll_keyhash = self.key_repr.get_ll_hash_function() ll_keyeq = self.key_repr.get_ll_eq_function() # can be None ll_keyhash = lltype.staticAdtMethod(ll_keyhash) if ll_keyeq is not None: ll_keyeq = lltype.staticAdtMethod(ll_keyeq) adtmeths = { 'keyhash': ll_keyhash, 'keyeq': ll_keyeq, 'paranoia': False, } adtmeths['KEY'] = self.DICTKEY adtmeths['VALUE'] = self.DICTVALUE adtmeths['allocate'] = lltype.typeMethod(_ll_malloc_dict) self.DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths, *fields))
def ll_both_none(lst1, lst2): return not lst1 and not lst2 # ____________________________________________________________ # # Accessor methods 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 ll_newlist = typeMethod(ll_newlist) ll_newlist.oopspec = 'newlist(length)' 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 ll_newlist_hint = typeMethod(ll_newlist_hint) ll_newlist_hint.oopspec = 'newlist_hint(lengthhint)' # should empty lists start with no allocated memory, or with a preallocated # minimal number of entries? XXX compare memory usage versus speed, and # check how many always-empty lists there are in a typical pypy-c run... INITIAL_EMPTY_LIST_ALLOCATION = 0
def _setup_repr(self): if 'key_repr' not in self.__dict__: key_repr = self._key_repr_computer() self.external_key_repr, self.key_repr = self.pickkeyrepr(key_repr) if 'value_repr' not in self.__dict__: self.external_value_repr, self.value_repr = self.pickrepr(self._value_repr_computer()) if isinstance(self.DICT, lltype.GcForwardReference): self.DICTKEY = self.key_repr.lowleveltype self.DICTVALUE = self.value_repr.lowleveltype # compute the shape of the DICTENTRY structure entryfields = [] entrymeths = { 'allocate': lltype.typeMethod(_ll_malloc_entries), 'delete': _ll_free_entries, 'must_clear_key': (isinstance(self.DICTKEY, lltype.Ptr) and self.DICTKEY._needsgc()), 'must_clear_value': (isinstance(self.DICTVALUE, lltype.Ptr) and self.DICTVALUE._needsgc()), } # * the key entryfields.append(("key", self.DICTKEY)) # * if NULL is not a valid ll value for the key or the value # field of the entry, it can be used as a marker for # never-used entries. Otherwise, we need an explicit flag. s_key = self.dictkey.s_value s_value = self.dictvalue.s_value nullkeymarker = not self.key_repr.can_ll_be_null(s_key) nullvaluemarker = not self.value_repr.can_ll_be_null(s_value) if self.force_non_null: if not nullkeymarker: rmodel.warning("%s can be null, but forcing non-null in dict key" % s_key) nullkeymarker = True if not nullvaluemarker: rmodel.warning("%s can be null, but forcing non-null in dict value" % s_value) nullvaluemarker = True dummykeyobj = self.key_repr.get_ll_dummyval_obj(self.rtyper, s_key) dummyvalueobj = self.value_repr.get_ll_dummyval_obj(self.rtyper, s_value) # * the state of the entry - trying to encode it as dummy objects if nullkeymarker and dummykeyobj: # all the state can be encoded in the key entrymeths['everused'] = ll_everused_from_key entrymeths['dummy_obj'] = dummykeyobj entrymeths['valid'] = ll_valid_from_key entrymeths['mark_deleted'] = ll_mark_deleted_in_key # the key is overwritten by 'dummy' when the entry is deleted entrymeths['must_clear_key'] = False elif nullvaluemarker and dummyvalueobj: # all the state can be encoded in the value entrymeths['everused'] = ll_everused_from_value entrymeths['dummy_obj'] = dummyvalueobj entrymeths['valid'] = ll_valid_from_value entrymeths['mark_deleted'] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_value'] = False else: # we need a flag to know if the entry was ever used # (we cannot use a NULL as a marker for this, because # the key and value will be reset to NULL to clear their # reference) entryfields.append(("f_everused", lltype.Bool)) entrymeths['everused'] = ll_everused_from_flag # can we still rely on a dummy obj to mark deleted entries? if dummykeyobj: entrymeths['dummy_obj'] = dummykeyobj entrymeths['valid'] = ll_valid_from_key entrymeths['mark_deleted'] = ll_mark_deleted_in_key # key is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_key'] = False elif dummyvalueobj: entrymeths['dummy_obj'] = dummyvalueobj entrymeths['valid'] = ll_valid_from_value entrymeths['mark_deleted'] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_value'] = False else: entryfields.append(("f_valid", lltype.Bool)) entrymeths['valid'] = ll_valid_from_flag entrymeths['mark_deleted'] = ll_mark_deleted_in_flag # * the value entryfields.append(("value", self.DICTVALUE)) # * the hash, if needed if self.custom_eq_hash: fasthashfn = None else: fasthashfn = self.key_repr.get_ll_fasthash_function() if fasthashfn is None: entryfields.append(("f_hash", lltype.Signed)) entrymeths['hash'] = ll_hash_from_cache else: entrymeths['hash'] = ll_hash_recomputed entrymeths['fasthashfn'] = fasthashfn # Build the lltype data structures self.DICTENTRY = lltype.Struct("dictentry", *entryfields) self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY, adtmeths=entrymeths) fields = [ ("num_items", lltype.Signed), ("resize_counter", lltype.Signed), ("entries", lltype.Ptr(self.DICTENTRYARRAY)) ] if self.custom_eq_hash: self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr() fields.extend([ ("fnkeyeq", self.r_rdict_eqfn.lowleveltype), ("fnkeyhash", self.r_rdict_hashfn.lowleveltype) ]) adtmeths = { 'keyhash': ll_keyhash_custom, 'keyeq': ll_keyeq_custom, 'r_rdict_eqfn': self.r_rdict_eqfn, 'r_rdict_hashfn': self.r_rdict_hashfn, 'paranoia': True, } else: # figure out which functions must be used to hash and compare ll_keyhash = self.key_repr.get_ll_hash_function() ll_keyeq = self.key_repr.get_ll_eq_function() # can be None ll_keyhash = lltype.staticAdtMethod(ll_keyhash) if ll_keyeq is not None: ll_keyeq = lltype.staticAdtMethod(ll_keyeq) adtmeths = { 'keyhash': ll_keyhash, 'keyeq': ll_keyeq, 'paranoia': False, } adtmeths['KEY'] = self.DICTKEY adtmeths['VALUE'] = self.DICTVALUE adtmeths['allocate'] = lltype.typeMethod(_ll_malloc_dict) self.DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths, *fields))
def get_ll_dict( DICTKEY, DICTVALUE, get_custom_eq_hash=None, DICT=None, ll_fasthash_function=None, ll_hash_function=None, ll_eq_function=None, method_cache={}, dummykeyobj=None, dummyvalueobj=None, rtyper=None, ): # get the actual DICT type. if DICT is None, it's created, otherwise # forward reference is becoming DICT if DICT is None: DICT = lltype.GcForwardReference() # compute the shape of the DICTENTRY structure entryfields = [] entrymeths = { "allocate": lltype.typeMethod(_ll_malloc_entries), "delete": _ll_free_entries, "must_clear_key": (isinstance(DICTKEY, lltype.Ptr) and DICTKEY._needsgc()), "must_clear_value": (isinstance(DICTVALUE, lltype.Ptr) and DICTVALUE._needsgc()), } if getattr(ll_eq_function, "no_direct_compare", False): entrymeths["no_direct_compare"] = True # * the key entryfields.append(("key", DICTKEY)) # * the state of the entry - trying to encode it as dummy objects if dummykeyobj: # all the state can be encoded in the key entrymeths["dummy_obj"] = dummykeyobj entrymeths["valid"] = ll_valid_from_key entrymeths["mark_deleted"] = ll_mark_deleted_in_key # the key is overwritten by 'dummy' when the entry is deleted entrymeths["must_clear_key"] = False elif dummyvalueobj: # all the state can be encoded in the value entrymeths["dummy_obj"] = dummyvalueobj entrymeths["valid"] = ll_valid_from_value entrymeths["mark_deleted"] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths["must_clear_value"] = False else: # we need a flag to know if the entry was ever used entryfields.append(("f_valid", lltype.Bool)) entrymeths["valid"] = ll_valid_from_flag entrymeths["mark_deleted"] = ll_mark_deleted_in_flag # * the value entryfields.append(("value", DICTVALUE)) if ll_fasthash_function is None: entryfields.append(("f_hash", lltype.Signed)) entrymeths["hash"] = ll_hash_from_cache else: entrymeths["hash"] = ll_hash_recomputed entrymeths["fasthashfn"] = ll_fasthash_function # Build the lltype data structures DICTENTRY = lltype.Struct("odictentry", *entryfields) DICTENTRYARRAY = lltype.GcArray(DICTENTRY, adtmeths=entrymeths) fields = [ ("num_live_items", lltype.Signed), ("num_ever_used_items", lltype.Signed), ("resize_counter", lltype.Signed), ("indexes", llmemory.GCREF), ("lookup_function_no", lltype.Signed), ("entries", lltype.Ptr(DICTENTRYARRAY)), ] if get_custom_eq_hash is not None: r_rdict_eqfn, r_rdict_hashfn = get_custom_eq_hash() fields.extend([("fnkeyeq", r_rdict_eqfn.lowleveltype), ("fnkeyhash", r_rdict_hashfn.lowleveltype)]) adtmeths = { "keyhash": ll_keyhash_custom, "keyeq": ll_keyeq_custom, "r_rdict_eqfn": r_rdict_eqfn, "r_rdict_hashfn": r_rdict_hashfn, "paranoia": True, } else: # figure out which functions must be used to hash and compare ll_keyhash = ll_hash_function ll_keyeq = ll_eq_function ll_keyhash = lltype.staticAdtMethod(ll_keyhash) if ll_keyeq is not None: ll_keyeq = lltype.staticAdtMethod(ll_keyeq) adtmeths = {"keyhash": ll_keyhash, "keyeq": ll_keyeq, "paranoia": False} adtmeths["KEY"] = DICTKEY adtmeths["VALUE"] = DICTVALUE adtmeths["lookup_function"] = lltype.staticAdtMethod(ll_call_lookup_function) adtmeths["allocate"] = lltype.typeMethod(_ll_malloc_dict) DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths, *fields)) return DICT
elif weakref_deref(rclass.OBJECTPTR, key): return True else: # The entry might be a dead weakref still holding a strong # reference to the value; for this case, we clear the old # value from the entry, if any. entries[i].value = NULLVALUE return False def ll_everused(entries, i): return bool(entries[i].key) entrymeths = { 'allocate': lltype.typeMethod(rdict._ll_malloc_entries), 'delete': rdict._ll_free_entries, 'valid': ll_valid, 'everused': ll_everused, 'hash': rdict.ll_hash_from_cache, 'no_direct_compare': True, } WEAKDICTENTRYARRAY = lltype.GcArray(WEAKDICTENTRY, adtmeths=entrymeths, hints={'weakarray': 'key'}) # NB. the 'hints' is not used so far ^^^ @jit.dont_look_inside def ll_new_weakdict(): d = lltype.malloc(WEAKDICT)
elif weakref_deref(rclass.OBJECTPTR, key): return True else: # The entry might be a dead weakref still holding a strong # reference to the value; for this case, we clear the old # value from the entry, if any. entries[i].value = NULLVALUE return False def ll_everused(entries, i): return bool(entries[i].key) entrymeths = { "allocate": lltype.typeMethod(rdict._ll_malloc_entries), "delete": rdict._ll_free_entries, "valid": ll_valid, "everused": ll_everused, "hash": rdict.ll_hash_from_cache, "no_direct_compare": True, } WEAKDICTENTRYARRAY = lltype.GcArray(WEAKDICTENTRY, adtmeths=entrymeths, hints={"weakarray": "key"}) # NB. the 'hints' is not used so far ^^^ @jit.dont_look_inside def ll_new_weakdict(): d = lltype.malloc(WEAKDICT) d.entries = WEAKDICT.entries.TO.allocate(rdict.DICT_INITSIZE) d.num_items = 0
def get_ll_dict(DICTKEY, DICTVALUE, get_custom_eq_hash=None, DICT=None, ll_fasthash_function=None, ll_hash_function=None, ll_eq_function=None, method_cache={}, dummykeyobj=None, dummyvalueobj=None, rtyper=None): # get the actual DICT type. if DICT is None, it's created, otherwise # forward reference is becoming DICT if DICT is None: DICT = lltype.GcForwardReference() # compute the shape of the DICTENTRY structure entryfields = [] entrymeths = { 'allocate': lltype.typeMethod(_ll_malloc_entries), 'delete': _ll_free_entries, 'must_clear_key': (isinstance(DICTKEY, lltype.Ptr) and DICTKEY._needsgc()), 'must_clear_value': (isinstance(DICTVALUE, lltype.Ptr) and DICTVALUE._needsgc()), } if getattr(ll_eq_function, 'no_direct_compare', False): entrymeths['no_direct_compare'] = True # * the key entryfields.append(("key", DICTKEY)) # * the state of the entry - trying to encode it as dummy objects if dummykeyobj: # all the state can be encoded in the key entrymeths['dummy_obj'] = dummykeyobj entrymeths['valid'] = ll_valid_from_key entrymeths['mark_deleted'] = ll_mark_deleted_in_key # the key is overwritten by 'dummy' when the entry is deleted entrymeths['must_clear_key'] = False elif dummyvalueobj: # all the state can be encoded in the value entrymeths['dummy_obj'] = dummyvalueobj entrymeths['valid'] = ll_valid_from_value entrymeths['mark_deleted'] = ll_mark_deleted_in_value # value is overwritten by 'dummy' when entry is deleted entrymeths['must_clear_value'] = False else: # we need a flag to know if the entry was ever used entryfields.append(("f_valid", lltype.Bool)) entrymeths['valid'] = ll_valid_from_flag entrymeths['mark_deleted'] = ll_mark_deleted_in_flag # * the value entryfields.append(("value", DICTVALUE)) if ll_fasthash_function is None: entryfields.append(("f_hash", lltype.Signed)) entrymeths['hash'] = ll_hash_from_cache else: entrymeths['hash'] = ll_hash_recomputed entrymeths['fasthashfn'] = ll_fasthash_function # Build the lltype data structures DICTENTRY = lltype.Struct("odictentry", *entryfields) DICTENTRYARRAY = lltype.GcArray(DICTENTRY, adtmeths=entrymeths) fields = [("num_live_items", lltype.Signed), ("num_ever_used_items", lltype.Signed), ("resize_counter", lltype.Signed), ("indexes", llmemory.GCREF), ("lookup_function_no", lltype.Signed), ("entries", lltype.Ptr(DICTENTRYARRAY))] if get_custom_eq_hash is not None: r_rdict_eqfn, r_rdict_hashfn = get_custom_eq_hash() fields.extend([("fnkeyeq", r_rdict_eqfn.lowleveltype), ("fnkeyhash", r_rdict_hashfn.lowleveltype)]) adtmeths = { 'keyhash': ll_keyhash_custom, 'keyeq': ll_keyeq_custom, 'r_rdict_eqfn': r_rdict_eqfn, 'r_rdict_hashfn': r_rdict_hashfn, 'paranoia': True, } else: # figure out which functions must be used to hash and compare ll_keyhash = ll_hash_function ll_keyeq = ll_eq_function ll_keyhash = lltype.staticAdtMethod(ll_keyhash) if ll_keyeq is not None: ll_keyeq = lltype.staticAdtMethod(ll_keyeq) adtmeths = { 'keyhash': ll_keyhash, 'keyeq': ll_keyeq, 'paranoia': False, } adtmeths['KEY'] = DICTKEY adtmeths['VALUE'] = DICTVALUE adtmeths['lookup_function'] = lltype.staticAdtMethod( ll_call_lookup_function) adtmeths['allocate'] = lltype.typeMethod(_ll_malloc_dict) DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths, *fields)) return DICT