123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219(* weak hash table *)(* bucket array *)moduleBucket:sigtype('a,'b)tvalcreate:int->('a,'b)tvaladd:('a,'b)t->'a->'b->unitvalfind:('a,'b)t->('a->'b->bool)->('a*'b)option(** [find t f]: the first [(k,v)] pair [f k v = true], if exists, is returned as [Some (k,v)].
If none found, returns None *)valremove:('a,'b)t->('a->'b->bool)->('a*'b)option(** [remove t f]: the first [(k,v)] pair [f k v = true], if exists, is removed from [t] and returns the [Some (k,v)].
If none found, returns None. *)valremoveq:('a,'b)t->'a->'boption(** [removeq t k] removes the binding of the pointer equal [k] from [t] and returns its value if exists.
Otherwise it returns [None]. *)vallength:('a,'b)t->int(** return the number of full elements *)end=structtype('a,'b)t={mutablekeys:'aWeak.t;mutablevalues:'boptionarray;(* CR: No point of having option. We can safely use Obj *)mutablesize:int;(* size of keys *)init_size:int;(* initial size *)mutablecur:int;(* keys must be all empty from cur to size-1 *)mutablenelems:int;(* elements in the bucket *)}letcreatesize={keys=Weak.createsize;(* CR size > 0 and max_array_length *)values=Array.makesizeNone;size=size;init_size=size;cur=0;nelems=0;}(* If [t == t'], compaction in place. Otherwise, compaction by copy.
At compaction by copy, there is no check of destination size.
*)letreccompacttt'to_from=iffrom=t.sizethenbegin(* finished. clear from to_ to the end *)fori=to_tot'.size-1doWeak.sett'.keysiNone;Array.unsafe_sett'.valuesiNone;done;to_(* returns # of filled elements *)endelsematchWeak.gett.keysfromwith|None->compacttt'to_(from+1)|somev->Weak.sett'.keysto_somev;Array.unsafe_sett'.valuesto_(Array.unsafe_gett.valuesfrom);compacttt'(to_+1)(from+1)letcompacttt'=compacttt'00letenlarget=letnewsize=t.size*2in(* CR: Sys.max_array_length *)letkeys=t.keysinletkeys'=Weak.createnewsizeinletvalues=t.valuesinletvalues'=Array.makenewsizeNoneinWeak.blitkeys0keys'0t.size;Array.blitvalues0values'0t.size;t.keys<-keys';t.values<-values';t.size<-newsize;t.cur<-t.sizeletshrinkt=letnewsize=max(t.size/2)t.init_sizeinifnewsize<t.nelemsthenbeginlett'=createnewsizein(* t' is a different bucket but contents will be copied to t *)(* compaction by copy *)ignore(compacttt');t.keys<-t'.keys;t.values<-t'.values;t.size<-t'.size;t.cur<-t'.cur;endletfind_gentf=letrecfindtfi=ifi=t.sizethenNoneelsematchWeak.gett.keysiwith|None->findtf(i+1)|Somek->matchArray.unsafe_gett.valuesiwith|None->assertfalse|Somev->iffkvthenSome(k,v,i)elsefindtf(i+1)infindtf0letfindtf=matchfind_gentfwith|None->None|Some(k,v,_)->Some(k,v)letremovetf=matchfind_gentfwith|None->None|Some(k,v,i)->Weak.sett.keysiNone;Array.unsafe_sett.valuesiNone;t.nelems<-t.nelems-1;shrinkt;Some(k,v)letcompact_and_may_enlarget=ifcompacttt=t.sizethenenlargetletremoveqtk=matchremovet(funk'_v->k==k')with|Some(_,v)->Somev|None->Noneletremoveq_gctk=assert(removeqtk<>None)letrecaddtkv=ift.cur<t.sizethenbeginGc.finalise(removeq_gct)k;Weak.sett.keyst.cur(Somek);Array.unsafe_sett.valuest.cur(Somev);t.cur<-t.cur+1;t.nelems<-t.nelems+1;endelsebegin(* compact and may enlarge it then try again *)compact_and_may_enlarget;addtkvendletlengtht=t.nelemsendmoduleMake(K:Hashtbl.HashedType):sigtype'atvalcreate:int->'atvaladd:'at->K.t->'a->unitvalfind:'at->K.t->(K.t*'a)optionvalfindq:'at->K.t->'aoptionvalmem:'at->K.t->boolvalmemq:'at->K.t->boolvalremove:'at->K.t->(K.t*'a)optionvalremoveq:'at->K.t->'aoptionvallength:'at->intend=structtype'at=(K.t,'a)Bucket.tarrayletcreatesize=Array.initsize(fun_->Bucket.create10)letget_buckettk=letpos=(K.hashk)mod(Array.lengtht)inArray.unsafe_gettposletaddtkv=letbucket=get_buckettkinBucket.addbucketkvletfindtk=letbucket=get_buckettkinBucket.findbucket(funk'_->K.equalkk')letfindqtk=letbucket=get_buckettkinmatchBucket.findbucket(funk'_->k==k')with|Some(_,v)->Somev|None->Noneletmemtk=findtk<>Noneletmemqtk=findqtk<>Noneletremovetk=letbucket=get_buckettkinBucket.removebucket(funk'_->K.equalkk')letremoveqtk=letbucket=get_buckettkinBucket.removeqbucketk(* CR jfuruse: O(n) where n is the size of buckets *)letlengtht=Array.fold_left(funstb->st+Bucket.lengthb)0tendmoduleO=Make(structtypet=Obj.tlethash=Hashtbl.hashletequal=(=)end)modulePoly:sigtype('a,'b)tvalcreate:int->('a,'b)tvaladd:('a,'b)t->'a->'b->unitvalfind:('a,'b)t->'a->('a*'b)optionvalfindq:('a,'b)t->'a->'boptionvalremove:('a,'b)t->'a->('a*'b)optionvalremoveq:('a,'b)t->'a->'boptionvallength:('a,'b)t->intend=structtype('a,'b)t='bO.tletcoerce_opt=function|None->None|Some(k,v)->Some(Obj.objk,v)letcreate=O.createletaddtkv=O.addt(Obj.reprk)vletfindtk=coerce_opt(O.findt(Obj.reprk))letfindqtk=O.findqt(Obj.reprk)letremovetk=coerce_opt(O.removet(Obj.reprk))letremoveqtk=O.removeqt(Obj.reprk)letlength=O.lengthendincludePoly