fromrpython.tool.pairtypeimportpairtypefromrpython.flowspace.modelimportConstantfromrpython.rtyper.rdictimportAbstractDictRepr,AbstractDictIteratorReprfromrpython.rtyper.lltypesystemimportlltypefromrpython.rlibimportobjectmodel,jitfromrpython.rlib.debugimportll_assertfromrpython.rlib.rarithmeticimportr_uint,intmask,LONG_BITfromrpython.rtyperimportrmodelfromrpython.rtyper.errorimportTyperErrorHIGHEST_BIT=r_uint(intmask(1<<(LONG_BIT-1)))MASK=r_uint(intmask(HIGHEST_BIT-1))# ____________________________________________________________## generic implementation of RPython dictionary, with parametric DICTKEY and# DICTVALUE types.## XXX for immutable dicts, the array should be inlined and# resize_counter and everused are not needed.## struct dictentry {# DICTKEY key;# bool f_valid; # (optional) the entry is filled# bool f_everused; # (optional) the entry is or has ever been filled# DICTVALUE value;# int f_hash; # (optional) key hash, if hard to recompute# }## struct dicttable {# int num_items;# int resize_counter;# Array *entries;# (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;# (Function DICTKEY -> int) *fnkeyhash;# }##classDictRepr(AbstractDictRepr):def__init__(self,rtyper,key_repr,value_repr,dictkey,dictvalue,custom_eq_hash=None,force_non_null=False):self.rtyper=rtyperself.DICT=lltype.GcForwardReference()self.lowleveltype=lltype.Ptr(self.DICT)self.custom_eq_hash=custom_eq_hashisnotNoneifnotisinstance(key_repr,rmodel.Repr):# not computed yet, done by setup()assertcallable(key_repr)self._key_repr_computer=key_reprelse:self.external_key_repr,self.key_repr=self.pickkeyrepr(key_repr)ifnotisinstance(value_repr,rmodel.Repr):# not computed yet, done by setup()assertcallable(value_repr)self._value_repr_computer=value_reprelse:self.external_value_repr,self.value_repr=self.pickrepr(value_repr)self.dictkey=dictkeyself.dictvalue=dictvalueself.dict_cache={}self._custom_eq_hash_repr=custom_eq_hashself.force_non_null=force_non_null# setup() needs to be called to finish this initializationdef_externalvsinternal(self,rtyper,item_repr):returnrmodel.externalvsinternal(self.rtyper,item_repr)def_setup_repr(self):if'key_repr'notinself.__dict__:key_repr=self._key_repr_computer()self.external_key_repr,self.key_repr=self.pickkeyrepr(key_repr)if'value_repr'notinself.__dict__:self.external_value_repr,self.value_repr=self.pickrepr(self._value_repr_computer())ifisinstance(self.DICT,lltype.GcForwardReference):self.DICTKEY=self.key_repr.lowleveltypeself.DICTVALUE=self.value_repr.lowleveltype# compute the shape of the DICTENTRY structureentryfields=[]entrymeths={'allocate':lltype.typeMethod(_ll_malloc_entries),'delete':_ll_free_entries,'must_clear_key':(isinstance(self.DICTKEY,lltype.Ptr)andself.DICTKEY._needsgc()),'must_clear_value':(isinstance(self.DICTVALUE,lltype.Ptr)andself.DICTVALUE._needsgc()),}# * the keyentryfields.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_values_value=self.dictvalue.s_valuenullkeymarker=notself.key_repr.can_ll_be_null(s_key)nullvaluemarker=notself.value_repr.can_ll_be_null(s_value)ifself.force_non_null:ifnotnullkeymarker:rmodel.warning("%s can be null, but forcing non-null in dict key"%s_key)nullkeymarker=Trueifnotnullvaluemarker:rmodel.warning("%s can be null, but forcing non-null in dict value"%s_value)nullvaluemarker=Truedummykeyobj=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 objectsifnullkeymarkeranddummykeyobj:# all the state can be encoded in the keyentrymeths['everused']=ll_everused_from_keyentrymeths['dummy_obj']=dummykeyobjentrymeths['valid']=ll_valid_from_keyentrymeths['mark_deleted']=ll_mark_deleted_in_key# the key is overwritten by 'dummy' when the entry is deletedentrymeths['must_clear_key']=Falseelifnullvaluemarkeranddummyvalueobj:# all the state can be encoded in the valueentrymeths['everused']=ll_everused_from_valueentrymeths['dummy_obj']=dummyvalueobjentrymeths['valid']=ll_valid_from_valueentrymeths['mark_deleted']=ll_mark_deleted_in_value# value is overwritten by 'dummy' when entry is deletedentrymeths['must_clear_value']=Falseelse:# 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?ifdummykeyobj:entrymeths['dummy_obj']=dummykeyobjentrymeths['valid']=ll_valid_from_keyentrymeths['mark_deleted']=ll_mark_deleted_in_key# key is overwritten by 'dummy' when entry is deletedentrymeths['must_clear_key']=Falseelifdummyvalueobj:entrymeths['dummy_obj']=dummyvalueobjentrymeths['valid']=ll_valid_from_valueentrymeths['mark_deleted']=ll_mark_deleted_in_value# value is overwritten by 'dummy' when entry is deletedentrymeths['must_clear_value']=Falseelse:entryfields.append(("f_valid",lltype.Bool))entrymeths['valid']=ll_valid_from_flagentrymeths['mark_deleted']=ll_mark_deleted_in_flag# * the valueentryfields.append(("value",self.DICTVALUE))# * the hash, if neededifself.custom_eq_hash:fasthashfn=Noneelse:fasthashfn=self.key_repr.get_ll_fasthash_function()iffasthashfnisNone:entryfields.append(("f_hash",lltype.Signed))entrymeths['hash']=ll_hash_from_cacheelse:entrymeths['hash']=ll_hash_recomputedentrymeths['fasthashfn']=fasthashfn# Build the lltype data structuresself.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))]ifself.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 comparell_keyhash=self.key_repr.get_ll_hash_function()ll_keyeq=self.key_repr.get_ll_eq_function()# can be Nonell_keyhash=lltype.staticAdtMethod(ll_keyhash)ifll_keyeqisnotNone:ll_keyeq=lltype.staticAdtMethod(ll_keyeq)adtmeths={'keyhash':ll_keyhash,'keyeq':ll_keyeq,'paranoia':False,}adtmeths['KEY']=self.DICTKEYadtmeths['VALUE']=self.DICTVALUEadtmeths['allocate']=lltype.typeMethod(_ll_malloc_dict)self.DICT.become(lltype.GcStruct("dicttable",adtmeths=adtmeths,*fields))defconvert_const(self,dictobj):fromrpython.rtyper.lltypesystemimportllmemory# get object from bound dict methods#dictobj = getattr(dictobj, '__self__', dictobj)ifdictobjisNone:returnlltype.nullptr(self.DICT)ifnotisinstance(dictobj,(dict,objectmodel.r_dict)):raiseTypeError("expected a dict: %r"%(dictobj,))try:key=Constant(dictobj)returnself.dict_cache[key]exceptKeyError:self.setup()l_dict=ll_newdict_size(self.DICT,len(dictobj))self.dict_cache[key]=l_dictr_key=self.key_reprifr_key.lowleveltype==llmemory.Address:raiseTypeError("No prebuilt dicts of address keys")r_value=self.value_reprifisinstance(dictobj,objectmodel.r_dict):ifself.r_rdict_eqfn.lowleveltype!=lltype.Void:l_fn=self.r_rdict_eqfn.convert_const(dictobj.key_eq)l_dict.fnkeyeq=l_fnifself.r_rdict_hashfn.lowleveltype!=lltype.Void:l_fn=self.r_rdict_hashfn.convert_const(dictobj.key_hash)l_dict.fnkeyhash=l_fnfordictkeycontainer,dictvalueindictobj._dict.items():llkey=r_key.convert_const(dictkeycontainer.key)llvalue=r_value.convert_const(dictvalue)ll_dict_insertclean(l_dict,llkey,llvalue,dictkeycontainer.hash)returnl_dictelse:fordictkey,dictvalueindictobj.items():llkey=r_key.convert_const(dictkey)llvalue=r_value.convert_const(dictvalue)ll_dict_insertclean(l_dict,llkey,llvalue,l_dict.keyhash(llkey))returnl_dictdefrtype_len(self,hop):v_dict,=hop.inputargs(self)returnhop.gendirectcall(ll_dict_len,v_dict)defrtype_is_true(self,hop):v_dict,=hop.inputargs(self)returnhop.gendirectcall(ll_dict_is_true,v_dict)defmake_iterator_repr(self,*variant):returnDictIteratorRepr(self,*variant)defrtype_method_get(self,hop):v_dict,v_key,v_default=hop.inputargs(self,self.key_repr,self.value_repr)hop.exception_cannot_occur()v_res=hop.gendirectcall(ll_get,v_dict,v_key,v_default)returnself.recast_value(hop.llops,v_res)defrtype_method_setdefault(self,hop):v_dict,v_key,v_default=hop.inputargs(self,self.key_repr,self.value_repr)hop.exception_cannot_occur()v_res=hop.gendirectcall(ll_setdefault,v_dict,v_key,v_default)returnself.recast_value(hop.llops,v_res)defrtype_method_copy(self,hop):v_dict,=hop.inputargs(self)hop.exception_cannot_occur()returnhop.gendirectcall(ll_copy,v_dict)defrtype_method_update(self,hop):v_dic1,v_dic2=hop.inputargs(self,self)hop.exception_cannot_occur()returnhop.gendirectcall(ll_update,v_dic1,v_dic2)def_rtype_method_kvi(self,hop,ll_func):v_dic,=hop.inputargs(self)r_list=hop.r_resultcLIST=hop.inputconst(lltype.Void,r_list.lowleveltype.TO)hop.exception_cannot_occur()returnhop.gendirectcall(ll_func,cLIST,v_dic)defrtype_method_keys(self,hop):returnself._rtype_method_kvi(hop,ll_dict_keys)defrtype_method_values(self,hop):returnself._rtype_method_kvi(hop,ll_dict_values)defrtype_method_items(self,hop):returnself._rtype_method_kvi(hop,ll_dict_items)defrtype_method_iterkeys(self,hop):hop.exception_cannot_occur()returnDictIteratorRepr(self,"keys").newiter(hop)defrtype_method_itervalues(self,hop):hop.exception_cannot_occur()returnDictIteratorRepr(self,"values").newiter(hop)defrtype_method_iteritems(self,hop):hop.exception_cannot_occur()returnDictIteratorRepr(self,"items").newiter(hop)defrtype_method_clear(self,hop):v_dict,=hop.inputargs(self)hop.exception_cannot_occur()returnhop.gendirectcall(ll_clear,v_dict)defrtype_method_popitem(self,hop):v_dict,=hop.inputargs(self)r_tuple=hop.r_resultcTUPLE=hop.inputconst(lltype.Void,r_tuple.lowleveltype)hop.exception_is_here()returnhop.gendirectcall(ll_popitem,cTUPLE,v_dict)defrtype_method_pop(self,hop):ifhop.nb_args==2:v_args=hop.inputargs(self,self.key_repr)target=ll_popelifhop.nb_args==3:v_args=hop.inputargs(self,self.key_repr,self.value_repr)target=ll_pop_defaulthop.exception_is_here()v_res=hop.gendirectcall(target,*v_args)returnself.recast_value(hop.llops,v_res)class__extend__(pairtype(DictRepr,rmodel.Repr)):defrtype_getitem((r_dict,r_key),hop):v_dict,v_key=hop.inputargs(r_dict,r_dict.key_repr)ifnotr_dict.custom_eq_hash:hop.has_implicit_exception(KeyError)# record that we know about ithop.exception_is_here()v_res=hop.gendirectcall(ll_dict_getitem,v_dict,v_key)returnr_dict.recast_value(hop.llops,v_res)defrtype_delitem((r_dict,r_key),hop):v_dict,v_key=hop.inputargs(r_dict,r_dict.key_repr)ifnotr_dict.custom_eq_hash:hop.has_implicit_exception(KeyError)# record that we know about ithop.exception_is_here()returnhop.gendirectcall(ll_dict_delitem,v_dict,v_key)defrtype_setitem((r_dict,r_key),hop):v_dict,v_key,v_value=hop.inputargs(r_dict,r_dict.key_repr,r_dict.value_repr)ifr_dict.custom_eq_hash:hop.exception_is_here()else:hop.exception_cannot_occur()hop.gendirectcall(ll_dict_setitem,v_dict,v_key,v_value)defrtype_contains((r_dict,r_key),hop):v_dict,v_key=hop.inputargs(r_dict,r_dict.key_repr)hop.exception_is_here()returnhop.gendirectcall(ll_contains,v_dict,v_key)class__extend__(pairtype(DictRepr,DictRepr)):defconvert_from_to((r_dict1,r_dict2),v,llops):# check that we don't convert from Dicts with# different key/value typesifr_dict1.dictkeyisNoneorr_dict2.dictkeyisNone:returnNotImplementedifr_dict1.dictkeyisnotr_dict2.dictkey:returnNotImplementedifr_dict1.dictvalueisNoneorr_dict2.dictvalueisNone:returnNotImplementedifr_dict1.dictvalueisnotr_dict2.dictvalue:returnNotImplementedreturnv# ____________________________________________________________## Low-level methods. These can be run for testing, but are meant to# be direct_call'ed from rtyped flow graphs, which means that they will# get flowed and annotated, mostly with SomePtr.defll_everused_from_flag(entries,i):returnentries[i].f_everuseddefll_everused_from_key(entries,i):returnbool(entries[i].key)defll_everused_from_value(entries,i):returnbool(entries[i].value)defll_valid_from_flag(entries,i):returnentries[i].f_validdefll_mark_deleted_in_flag(entries,i):entries[i].f_valid=Falsedefll_valid_from_key(entries,i):ENTRIES=lltype.typeOf(entries).TOdummy=ENTRIES.dummy_obj.ll_dummy_valuereturnentries.everused(i)andentries[i].key!=dummydefll_mark_deleted_in_key(entries,i):ENTRIES=lltype.typeOf(entries).TOdummy=ENTRIES.dummy_obj.ll_dummy_valueentries[i].key=dummydefll_valid_from_value(entries,i):ENTRIES=lltype.typeOf(entries).TOdummy=ENTRIES.dummy_obj.ll_dummy_valuereturnentries.everused(i)andentries[i].value!=dummydefll_mark_deleted_in_value(entries,i):ENTRIES=lltype.typeOf(entries).TOdummy=ENTRIES.dummy_obj.ll_dummy_valueentries[i].value=dummydefll_hash_from_cache(entries,i):returnentries[i].f_hashdefll_hash_recomputed(entries,i):ENTRIES=lltype.typeOf(entries).TOreturnENTRIES.fasthashfn(entries[i].key)defll_get_value(d,i):returnd.entries[i].valuedefll_keyhash_custom(d,key):DICT=lltype.typeOf(d).TOreturnobjectmodel.hlinvoke(DICT.r_rdict_hashfn,d.fnkeyhash,key)defll_keyeq_custom(d,key1,key2):DICT=lltype.typeOf(d).TOreturnobjectmodel.hlinvoke(DICT.r_rdict_eqfn,d.fnkeyeq,key1,key2)defll_dict_len(d):returnd.num_itemsdefll_dict_is_true(d):# check if a dict is True, allowing for Nonereturnbool(d)andd.num_items!=0defll_dict_getitem(d,key):i=ll_dict_lookup(d,key,d.keyhash(key))ifnoti&HIGHEST_BIT:returnll_get_value(d,i)else:raiseKeyErrordefll_dict_setitem(d,key,value):hash=d.keyhash(key)i=ll_dict_lookup(d,key,hash)return_ll_dict_setitem_lookup_done(d,key,value,hash,i)# It may be safe to look inside always, it has a few branches though, and their# frequencies needs to be investigated.@jit.look_inside_iff(lambdad,key,value,hash,i:jit.isvirtual(d)andjit.isconstant(key))def_ll_dict_setitem_lookup_done(d,key,value,hash,i):valid=(i&HIGHEST_BIT)==0i=i&MASKENTRY=lltype.typeOf(d.entries).TO.OFentry=d.entries[i]ifnotd.entries.everused(i):# a new entry that was never used beforell_assert(notvalid,"valid but not everused")rc=d.resize_counter-3ifrc<=0:# if needed, resize the dict -- before the insertionll_dict_resize(d)i=ll_dict_lookup_clean(d,hash)# then redo the lookup for 'key'entry=d.entries[i]rc=d.resize_counter-3ll_assert(rc>0,"ll_dict_resize failed?")d.resize_counter=rcifhasattr(ENTRY,'f_everused'):entry.f_everused=Trueentry.value=valueelse:# override an existing or deleted entryentry.value=valueifvalid:returnentry.key=keyifhasattr(ENTRY,'f_hash'):entry.f_hash=hashifhasattr(ENTRY,'f_valid'):entry.f_valid=Trued.num_items+=1defll_dict_insertclean(d,key,value,hash):# Internal routine used by ll_dict_resize() to insert an item which is# known to be absent from the dict. This routine also assumes that# the dict contains no deleted entries. This routine has the advantage# of never calling d.keyhash() and d.keyeq(), so it cannot call back# to user code. ll_dict_insertclean() doesn't resize the dict, either.i=ll_dict_lookup_clean(d,hash)ENTRY=lltype.typeOf(d.entries).TO.OFentry=d.entries[i]entry.value=valueentry.key=keyifhasattr(ENTRY,'f_hash'):entry.f_hash=hashifhasattr(ENTRY,'f_valid'):entry.f_valid=Trueifhasattr(ENTRY,'f_everused'):entry.f_everused=Trued.num_items+=1d.resize_counter-=3defll_dict_delitem(d,key):i=ll_dict_lookup(d,key,d.keyhash(key))ifi&HIGHEST_BIT:raiseKeyError_ll_dict_del(d,i)@jit.look_inside_iff(lambdad,i:jit.isvirtual(d)andjit.isconstant(i))def_ll_dict_del(d,i):d.entries.mark_deleted(i)d.num_items-=1# clear the key and the value if they are GC pointersENTRIES=lltype.typeOf(d.entries).TOENTRY=ENTRIES.OFentry=d.entries[i]ifENTRIES.must_clear_key:entry.key=lltype.nullptr(ENTRY.key.TO)ifENTRIES.must_clear_value:entry.value=lltype.nullptr(ENTRY.value.TO)## The rest is commented out: like CPython we no longer shrink the# dictionary here. It may shrink later if we try to append a number# of new items to it. Unsure if this behavior was designed in# CPython or is accidental. A design reason would be that if you# delete all items in a dictionary (e.g. with a series of# popitem()), then CPython avoids shrinking the table several times.#num_entries = len(d.entries)#if num_entries > DICT_INITSIZE and d.num_items <= num_entries / 4:# ll_dict_resize(d)# A previous xxx: move the size checking and resize into a single# call which is opaque to the JIT when the dict isn't virtual, to# avoid extra branches.defll_dict_resize(d):old_entries=d.entriesold_size=len(old_entries)# make a 'new_size' estimate and shrink it if there are many# deleted entry markers. See CPython for why it is a good idea to# quadruple the dictionary size as long as it's not too big.num_items=d.num_items+1ifnum_items>50000:new_estimate=num_items*2else:new_estimate=num_items*4new_size=DICT_INITSIZEwhilenew_size<=new_estimate:new_size*=2#d.entries=lltype.typeOf(old_entries).TO.allocate(new_size)d.num_items=0d.resize_counter=new_size*2i=0whilei<old_size:ifold_entries.valid(i):hash=old_entries.hash(i)entry=old_entries[i]ll_dict_insertclean(d,entry.key,entry.value,hash)i+=1old_entries.delete()ll_dict_resize.oopspec='dict.resize(d)'# ------- a port of CPython's dictobject.c's lookdict implementation -------PERTURB_SHIFT=5@jit.look_inside_iff(lambdad,key,hash:jit.isvirtual(d)andjit.isconstant(key))defll_dict_lookup(d,key,hash):entries=d.entriesENTRIES=lltype.typeOf(entries).TOdirect_compare=nothasattr(ENTRIES,'no_direct_compare')mask=len(entries)-1i=r_uint(hash&mask)# do the first try before any loopingifentries.valid(i):checkingkey=entries[i].keyifdirect_compareandcheckingkey==key:returni# found the entryifd.keyeqisnotNoneandentries.hash(i)==hash:# correct hash, maybe the key is e.g. a different pointer to# an equal objectfound=d.keyeq(checkingkey,key)ifd.paranoia:if(entries!=d.entriesornotentries.valid(i)orentries[i].key!=checkingkey):# the compare did major nasty stuff to the dict: start overreturnll_dict_lookup(d,key,hash)iffound:returni# found the entryfreeslot=-1elifentries.everused(i):freeslot=intmask(i)else:returni|HIGHEST_BIT# pristine entry -- lookup failed# In the loop, a deleted entry (everused and not valid) is by far# (factor of 100s) the least likely outcome, so test for that last.perturb=r_uint(hash)while1:# compute the next index using unsigned arithmetici=(i<<2)+i+perturb+1i=i&mask# keep 'i' as a signed number here, to consistently pass signed# arguments to the small helper methods.ifnotentries.everused(i):iffreeslot==-1:freeslot=intmask(i)returnr_uint(freeslot)|HIGHEST_BITelifentries.valid(i):checkingkey=entries[i].keyifdirect_compareandcheckingkey==key:returniifd.keyeqisnotNoneandentries.hash(i)==hash:# correct hash, maybe the key is e.g. a different pointer to# an equal objectfound=d.keyeq(checkingkey,key)ifd.paranoia:if(entries!=d.entriesornotentries.valid(i)orentries[i].key!=checkingkey):# the compare did major nasty stuff to the dict:# start overreturnll_dict_lookup(d,key,hash)iffound:returni# found the entryeliffreeslot==-1:freeslot=intmask(i)perturb>>=PERTURB_SHIFTdefll_dict_lookup_clean(d,hash):# a simplified version of ll_dict_lookup() which assumes that the# key is new, and the dictionary doesn't contain deleted entries.# It only finds the next free slot for the given hash.entries=d.entriesmask=len(entries)-1i=r_uint(hash&mask)perturb=r_uint(hash)whileentries.everused(i):i=(i<<2)+i+perturb+1i=i&maskperturb>>=PERTURB_SHIFTreturni# ____________________________________________________________## Irregular operations.DICT_INITSIZE=8defll_newdict(DICT):d=DICT.allocate()d.entries=DICT.entries.TO.allocate(DICT_INITSIZE)d.num_items=0d.resize_counter=DICT_INITSIZE*2returnddefll_newdict_size(DICT,length_estimate):length_estimate=(length_estimate//2)*3n=DICT_INITSIZEwhilen<length_estimate:n*=2d=DICT.allocate()d.entries=DICT.entries.TO.allocate(n)d.num_items=0d.resize_counter=n*2returnd# rpython.memory.lldict uses a dict based on Struct and Array# instead of GcStruct and GcArray, which is done by using different# 'allocate' and 'delete' adtmethod implementations than the ones belowdef_ll_malloc_dict(DICT):returnlltype.malloc(DICT)def_ll_malloc_entries(ENTRIES,n):returnlltype.malloc(ENTRIES,n,zero=True)def_ll_free_entries(entries):passdefrtype_r_dict(hop,i_force_non_null=None):r_dict=hop.r_resultifnotr_dict.custom_eq_hash:raiseTyperError("r_dict() call does not return an r_dict instance")v_eqfn=hop.inputarg(r_dict.r_rdict_eqfn,arg=0)v_hashfn=hop.inputarg(r_dict.r_rdict_hashfn,arg=1)ifi_force_non_nullisnotNone:asserti_force_non_null==2hop.inputarg(lltype.Void,arg=2)cDICT=hop.inputconst(lltype.Void,r_dict.DICT)hop.exception_cannot_occur()v_result=hop.gendirectcall(ll_newdict,cDICT)ifr_dict.r_rdict_eqfn.lowleveltype!=lltype.Void:cname=hop.inputconst(lltype.Void,'fnkeyeq')hop.genop('setfield',[v_result,cname,v_eqfn])ifr_dict.r_rdict_hashfn.lowleveltype!=lltype.Void:cname=hop.inputconst(lltype.Void,'fnkeyhash')hop.genop('setfield',[v_result,cname,v_hashfn])returnv_result# ____________________________________________________________## Iteration.classDictIteratorRepr(AbstractDictIteratorRepr):def__init__(self,r_dict,variant="keys"):self.r_dict=r_dictself.variant=variantself.lowleveltype=lltype.Ptr(lltype.GcStruct('dictiter',('dict',r_dict.lowleveltype),('index',lltype.Signed)))self.ll_dictiter=ll_dictiterself.ll_dictnext=ll_dictnext_group[variant]defll_dictiter(ITERPTR,d):iter=lltype.malloc(ITERPTR.TO)iter.dict=diter.index=0returniterdef_make_ll_dictnext(kind):# make three versions of the following function: keys, values, items@jit.look_inside_iff(lambdaRETURNTYPE,iter:jit.isvirtual(iter)and(iter.dictisNoneorjit.isvirtual(iter.dict)))@jit.oopspec("dictiter.next%s(iter)"%kind)defll_dictnext(RETURNTYPE,iter):# note that RETURNTYPE is None for keys and valuesdict=iter.dictifdict:entries=dict.entriesindex=iter.indexassertindex>=0entries_len=len(entries)whileindex<entries_len:entry=entries[index]is_valid=entries.valid(index)index=index+1ifis_valid:iter.index=indexifRETURNTYPEislltype.Void:returnNoneelifkind=='items':r=lltype.malloc(RETURNTYPE.TO)r.item0=recast(RETURNTYPE.TO.item0,entry.key)r.item1=recast(RETURNTYPE.TO.item1,entry.value)returnrelifkind=='keys':returnentry.keyelifkind=='values':returnentry.value# clear the reference to the dict and prevent restartsiter.dict=lltype.nullptr(lltype.typeOf(iter).TO.dict.TO)raiseStopIterationreturnll_dictnextll_dictnext_group={'keys':_make_ll_dictnext('keys'),'values':_make_ll_dictnext('values'),'items':_make_ll_dictnext('items')}# _____________________________________________________________# methodsdefll_get(dict,key,default):i=ll_dict_lookup(dict,key,dict.keyhash(key))ifnoti&HIGHEST_BIT:returnll_get_value(dict,i)else:returndefaultdefll_setdefault(dict,key,default):hash=dict.keyhash(key)i=ll_dict_lookup(dict,key,hash)ifnoti&HIGHEST_BIT:returnll_get_value(dict,i)else:_ll_dict_setitem_lookup_done(dict,key,default,hash,i)returndefaultdefll_copy(dict):DICT=lltype.typeOf(dict).TOdictsize=len(dict.entries)d=DICT.allocate()d.entries=DICT.entries.TO.allocate(dictsize)d.num_items=dict.num_itemsd.resize_counter=dict.resize_counterifhasattr(DICT,'fnkeyeq'):d.fnkeyeq=dict.fnkeyeqifhasattr(DICT,'fnkeyhash'):d.fnkeyhash=dict.fnkeyhashi=0whilei<dictsize:d_entry=d.entries[i]entry=dict.entries[i]ENTRY=lltype.typeOf(d.entries).TO.OFd_entry.key=entry.keyifhasattr(ENTRY,'f_valid'):d_entry.f_valid=entry.f_validifhasattr(ENTRY,'f_everused'):d_entry.f_everused=entry.f_everusedd_entry.value=entry.valueifhasattr(ENTRY,'f_hash'):d_entry.f_hash=entry.f_hashi+=1returndll_copy.oopspec='dict.copy(dict)'defll_clear(d):if(len(d.entries)==DICT_INITSIZEandd.resize_counter==DICT_INITSIZE*2):returnold_entries=d.entriesd.entries=lltype.typeOf(old_entries).TO.allocate(DICT_INITSIZE)d.num_items=0d.resize_counter=DICT_INITSIZE*2old_entries.delete()ll_clear.oopspec='dict.clear(d)'defll_update(dic1,dic2):entries=dic2.entriesd2len=len(entries)i=0whilei<d2len:ifentries.valid(i):entry=entries[i]hash=entries.hash(i)key=entry.keyj=ll_dict_lookup(dic1,key,hash)_ll_dict_setitem_lookup_done(dic1,key,entry.value,hash,j)i+=1ll_update.oopspec='dict.update(dic1, dic2)'# this is an implementation of keys(), values() and items()# in a single function.# note that by specialization on func, three different# and very efficient functions are created.defrecast(P,v):ifisinstance(P,lltype.Ptr):returnlltype.cast_pointer(P,v)else:returnvdef_make_ll_keys_values_items(kind):defll_kvi(LIST,dic):res=LIST.ll_newlist(dic.num_items)entries=dic.entriesdlen=len(entries)items=res.ll_items()i=0p=0whilei<dlen:ifentries.valid(i):ELEM=lltype.typeOf(items).TO.OFifELEMisnotlltype.Void:entry=entries[i]ifkind=='items':r=lltype.malloc(ELEM.TO)r.item0=recast(ELEM.TO.item0,entry.key)r.item1=recast(ELEM.TO.item1,entry.value)items[p]=relifkind=='keys':items[p]=recast(ELEM,entry.key)elifkind=='values':items[p]=recast(ELEM,entry.value)p+=1i+=1assertp==res.ll_length()returnresll_kvi.oopspec='dict.%s(dic)'%kindreturnll_kvill_dict_keys=_make_ll_keys_values_items('keys')ll_dict_values=_make_ll_keys_values_items('values')ll_dict_items=_make_ll_keys_values_items('items')defll_contains(d,key):i=ll_dict_lookup(d,key,d.keyhash(key))returnnoti&HIGHEST_BITPOPITEMINDEX=lltype.Struct('PopItemIndex',('nextindex',lltype.Signed))global_popitem_index=lltype.malloc(POPITEMINDEX,zero=True,immortal=True)def_ll_getnextitem(dic):entries=dic.entriesENTRY=lltype.typeOf(entries).TO.OFdmask=len(entries)-1ifhasattr(ENTRY,'f_hash'):ifentries.valid(0):return0base=entries[0].f_hashelse:base=global_popitem_index.nextindexcounter=0whilecounter<=dmask:i=(base+counter)&dmaskcounter+=1ifentries.valid(i):breakelse:raiseKeyErrorifhasattr(ENTRY,'f_hash'):entries[0].f_hash=base+counterelse:global_popitem_index.nextindex=base+counterreturnidefll_popitem(ELEM,dic):i=_ll_getnextitem(dic)entry=dic.entries[i]r=lltype.malloc(ELEM.TO)r.item0=recast(ELEM.TO.item0,entry.key)r.item1=recast(ELEM.TO.item1,entry.value)_ll_dict_del(dic,r_uint(i))returnrdefll_pop(dic,key):i=ll_dict_lookup(dic,key,dic.keyhash(key))ifnoti&HIGHEST_BIT:value=ll_get_value(dic,r_uint(i))_ll_dict_del(dic,r_uint(i))returnvalueelse:raiseKeyErrordefll_pop_default(dic,key,dfl):try:returnll_pop(dic,key)exceptKeyError:returndfl