frompypy.rpython.lltypesystemimportlltype,llmemoryfrompypy.rlib.objectmodelimportfree_non_gc_object,we_are_translatedfrompypy.rlib.rarithmeticimportr_uint,LONG_BITfrompypy.rlib.debugimportll_assertfrompypy.tool.identity_dictimportidentity_dictdefmangle_hash(i):# To hash pointers in dictionaries. Assumes that i shows some# alignment (to 4, 8, maybe 16 bytes), so we use the following# formula to avoid the trailing bits being always 0.returni^(i>>4)# ____________________________________________________________DEFAULT_CHUNK_SIZE=1019defget_chunk_manager(chunk_size=DEFAULT_CHUNK_SIZE,cache={}):try:returncache[chunk_size]exceptKeyError:passCHUNK=lltype.ForwardReference()CHUNK.become(lltype.Struct('AddressChunk',('next',lltype.Ptr(CHUNK)),('items',lltype.FixedSizeArray(llmemory.Address,chunk_size))))null_chunk=lltype.nullptr(CHUNK)classFreeList(object):_alloc_flavor_="raw"def__init__(self):self.free_list=null_chunkdefget(self):ifnotself.free_list:# we zero-initialize the chunks to make the translation# backends happy, but we don't need to do it at run-time.zero=notwe_are_translated()returnlltype.malloc(CHUNK,flavor="raw",zero=zero,track_allocation=False)result=self.free_listself.free_list=result.nextreturnresultdefput(self,chunk):ifwe_are_translated():chunk.next=self.free_listself.free_list=chunkelse:# Don't cache the old chunks but free them immediately.# Helps debugging, and avoids that old chunks full of# addresses left behind by a test end up in genc...lltype.free(chunk,flavor="raw",track_allocation=False)unused_chunks=FreeList()cache[chunk_size]=unused_chunks,null_chunkreturnunused_chunks,null_chunkdefget_address_stack(chunk_size=DEFAULT_CHUNK_SIZE,cache={}):try:returncache[chunk_size]exceptKeyError:passunused_chunks,null_chunk=get_chunk_manager(chunk_size)classAddressStack(object):_alloc_flavor_="raw"def__init__(self):self.chunk=unused_chunks.get()self.chunk.next=null_chunkself.used_in_last_chunk=0# invariant: self.used_in_last_chunk == 0 if and only if# the AddressStack is emptydefenlarge(self):new=unused_chunks.get()new.next=self.chunkself.chunk=newself.used_in_last_chunk=0enlarge._dont_inline_=Truedefshrink(self):old=self.chunkself.chunk=old.nextunused_chunks.put(old)self.used_in_last_chunk=chunk_sizeshrink._dont_inline_=Truedefappend(self,addr):used=self.used_in_last_chunkifused==chunk_size:self.enlarge()used=0self.chunk.items[used]=addrself.used_in_last_chunk=used+1# always > 0 heredefnon_empty(self):returnself.used_in_last_chunk!=0defpop(self):used=self.used_in_last_chunk-1ll_assert(used>=0,"pop on empty AddressStack")result=self.chunk.items[used]self.used_in_last_chunk=usedifused==0andself.chunk.next:self.shrink()returnresultdefdelete(self):cur=self.chunkwhilecur:next=cur.nextunused_chunks.put(cur)cur=nextfree_non_gc_object(self)def_length_estimate(self):chunk=self.chunkcount=self.used_in_last_chunkwhilechunk:chunk=chunk.nextcount+=chunk_sizereturncountdefforeach(self,callback,arg):"""Invoke 'callback(address, arg)' for all addresses in the stack. Typically, 'callback' is a bound method and 'arg' can be None. """chunk=self.chunkcount=self.used_in_last_chunkwhilechunk:whilecount>0:count-=1callback(chunk.items[count],arg)chunk=chunk.nextcount=chunk_sizeforeach._annspecialcase_='specialize:arg(1)'defstack2dict(self):result=AddressDict(self._length_estimate())self.foreach(_add_in_dict,result)returnresultdeftolist(self):"""NOT_RPYTHON. Returns the content as a list."""lst=[]def_add(obj,lst):lst.append(obj)self.foreach(_add,lst)returnlstdefremove(self,addr):"""Remove 'addr' from the stack. The addr *must* be in the list, and preferrably near the top. """got=self.pop()chunk=self.chunkcount=self.used_in_last_chunkwhilegot!=addr:count-=1ifcount<0:chunk=chunk.nextcount=chunk_size-1next=chunk.items[count]chunk.items[count]=gotgot=nextcache[chunk_size]=AddressStackreturnAddressStackdef_add_in_dict(item,d):d.add(item)defget_address_deque(chunk_size=DEFAULT_CHUNK_SIZE,cache={}):try:returncache[chunk_size]exceptKeyError:passunused_chunks,null_chunk=get_chunk_manager(chunk_size)classAddressDeque(object):_alloc_flavor_="raw"def__init__(self):chunk=unused_chunks.get()chunk.next=null_chunkself.oldest_chunk=self.newest_chunk=chunkself.index_in_oldest=0self.index_in_newest=0defenlarge(self):new=unused_chunks.get()new.next=null_chunkself.newest_chunk.next=newself.newest_chunk=newself.index_in_newest=0enlarge._dont_inline_=Truedefshrink(self):old=self.oldest_chunkself.oldest_chunk=old.nextunused_chunks.put(old)self.index_in_oldest=0shrink._dont_inline_=Truedefappend(self,addr):index=self.index_in_newestifindex==chunk_size:self.enlarge()index=0self.newest_chunk.items[index]=addrself.index_in_newest=index+1defnon_empty(self):return(self.oldest_chunk!=self.newest_chunkorself.index_in_oldest<self.index_in_newest)defpopleft(self):ll_assert(self.non_empty(),"pop on empty AddressDeque")index=self.index_in_oldestifindex==chunk_size:self.shrink()index=0result=self.oldest_chunk.items[index]self.index_in_oldest=index+1returnresultdefforeach(self,callback,arg):"""Invoke 'callback(address, arg)' for all addresses in the deque. Typically, 'callback' is a bound method and 'arg' can be None. """chunk=self.oldest_chunkindex=self.index_in_oldestwhilechunkisnotself.newest_chunk:whileindex<chunk_size:callback(chunk.items[index],arg)index+=1chunk=chunk.nextindex=0limit=self.index_in_newestwhileindex<limit:callback(chunk.items[index],arg)index+=1foreach._annspecialcase_='specialize:arg(1)'defdelete(self):cur=self.oldest_chunkwhilecur:next=cur.nextunused_chunks.put(cur)cur=nextfree_non_gc_object(self)cache[chunk_size]=AddressDequereturnAddressDeque# ____________________________________________________________defAddressDict(length_estimate=0):ifwe_are_translated():frompypy.rpython.memoryimportlldictreturnlldict.newdict(length_estimate)else:returnBasicAddressDict()defnull_address_dict():frompypy.rpython.memoryimportlldictreturnlltype.nullptr(lldict.DICT)classBasicAddressDict(object):def__init__(self):self.data=identity_dict()# {_key(addr): value}def_key(self,addr):"NOT_RPYTHON: prebuilt AddressDicts are not supported"returnaddr._fixup().ptr._objdef_wrapkey(self,obj):returnllmemory.cast_ptr_to_adr(obj._as_ptr())defdelete(self):passdeflength(self):returnlen(self.data)defcontains(self,keyaddr):returnself._key(keyaddr)inself.datadefget(self,keyaddr,default=llmemory.NULL):returnself.data.get(self._key(keyaddr),default)defsetitem(self,keyaddr,valueaddr):assertkeyaddrself.data[self._key(keyaddr)]=valueaddrdefinsertclean(self,keyaddr,valueaddr):assertkeyaddrkey=self._key(keyaddr)assertkeynotinself.dataself.data[key]=valueaddrdefadd(self,keyaddr):self.setitem(keyaddr,llmemory.NULL)defclear(self):self.data.clear()defforeach(self,callback,arg):"""Invoke 'callback(key, value, arg)' for all items in the dict. Typically, 'callback' is a bound method and 'arg' can be None."""forkey,valueinself.data.iteritems():callback(self._wrapkey(key),value,arg)defcopy_and_update(dict,surviving,updated_address):"""Make a copy of 'dict' in which the keys are updated as follows: * if surviving(key) returns False, the item is removed * otherwise, updated_address(key) is inserted in the copy. """newdict=AddressDictifnotwe_are_translated():# when not translated, return a dict of the same kind as 'dict'ifnotisinstance(dict,BasicAddressDict):frompypy.rpython.memory.lldictimportnewdictresult=newdict(dict.length())dict.foreach(_get_updater(surviving,updated_address),result)returnresultcopy_and_update._annspecialcase_='specialize:arg(1,2)'def_get_updater(surviving,updated_address):defcallback(key,value,arg):ifsurviving(key):newkey=updated_address(key)arg.setitem(newkey,value)returncallback_get_updater._annspecialcase_='specialize:memo'defcopy_without_null_values(dict):"""Make a copy of 'dict' without the key/value pairs where value==NULL."""newdict=AddressDictifnotwe_are_translated():# when not translated, return a dict of the same kind as 'dict'ifnotisinstance(dict,BasicAddressDict):frompypy.rpython.memory.lldictimportnewdictresult=newdict()dict.foreach(_null_value_checker,result)returnresultdef_null_value_checker(key,value,arg):ifvalue:arg.setitem(key,value)