Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file owee_interval_map.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246(* Representation of intervals.
Even though the addresses are 64-bits, we assume that the higher-order bits
are 0 (true on linux for user-space addresses).
Therefore we can use tagged integers for better performance.
*)type'ainterval={lbound:int;rbound:int;value:'a;}letintervallrvalue={lbound=Int64.to_intl;rbound=Int64.to_intr;value}(* A specialized implementation of a balanced-binary tree
that lazily prune out-of-bound intervals during rebalancing.
Core balancing algorithm is taken from grenier.baltree
https://github.com/let-def/grenier/blob/master/baltree/bt1.mli
*)moduleTree:sigtype'at=private|Leaf|Nodeofint*'at*'ainterval*'atvalleaf:'atvalnode:'at->'ainterval->'at->'at(* [set_bound l] will cause [node] to prune intervals that end before [l]
during rebalancing. *)valset_bound:int->unitend=structtype'at=|Leaf|Nodeofint*'at*'ainterval*'atletsize=function|Node(s,_,_,_)->s|Leaf->0letsmaller_ellsminsmax=(smin<smax)&&((sminlandsmax)lsl1<smax)letdisbalancedsminsmax=smaller_ellsmin(smaxlsr1)letnode_lxr=Node(sizel+1+sizer,l,x,r)letrot_leftlxrk=matchrwith|Node(_,rl,y,rr)->k(klxrl)yrr|_->assertfalseletrot_rightlyrk=matchlwith|Node(_,ll,x,lr)->kllx(klryr)|_->assertfalseletinc_leftlxrk=letr=matchrwith|Node(_,rl,y,rr)whensmaller_ell(sizerr)(sizerl)->rot_rightrlyrrk|_->rinrot_leftlxrkletinc_rightlyrk=letl=matchlwith|Node(_,ll,x,lr)whensmaller_ell(sizell)(sizelr)->rot_leftllxlrk|_->linrot_rightlyrkletrecnode_leftlxr=ifdisbalanced(sizel)(sizer)theninc_leftlxrnode_leftelsenode_lxrletrecnode_rightlyr=ifdisbalanced(sizer)(sizel)theninc_rightlyrnode_rightelsenode_lyrletleaf=Leafletbound=refmin_intletset_boundx=bound:=xletrecnodelxr=ifx.rbound<!boundthenjoinlrelsematchl,rwith|Leaf,Leaf->node_leafxleaf|l,rwhensizel<sizer->node_leftlxr|l,r->node_rightlxrandjoinlr=matchl,rwith|Leaf,t|t,Leaf->t|Node(sl,ll,x,lr),Node(sr,rl,y,rr)->ifsl<=srthennode(joinlrl)yrrelsenodellx(joinlrr)endletint_compare:int->int->int=compare(* Implement a right-bound ordered interval map on top of Tree *)moduleRMap=structtype'at='aTree.tletempty=Tree.leafletrecaddi=function|Tree.Leaf->Tree.nodeemptyiempty|Tree.Node(_,l,j,r)->letc=int_comparei.rboundj.rboundinifc<0thenTree.node(addil)jrelseTree.nodelj(addir)letaddit=Tree.set_boundi.lbound;additletrecbuild_spineboundacc=function|Tree.Leaf->acc|Tree.Node(_,l,i,r)->letc=int_comparei.rboundboundinifc>=0thenbuild_spinebound((i,r)::acc)lelsebuild_spineboundaccrletrecexpand_spineacc=function|Tree.Leaf->acc|Tree.Node(_,l,i,r)->expand_spine((i,r)::acc)lletlist_fromrmapbound=letrecloopacc=function|[]->acc|(i,r)::spine'->loop(i::acc)(expand_spinespine'r)inloop[](build_spinebound[]rmap)end(*
Algorithm suggested by Tudor Brindus (@Xyene) and Timothy Li (@FatalEagle)
Implementation by Frédéric Bour (@let-def).
See https://github.com/let-def/owee/issues/23,
and https://github.com/let-def/owee/pull/24.
*)(*
The [intervals] array contain each interval definition.
The [maps.(i)] contains a tree of all intervals that overlap [intervals.(i)].
Both are ordered by left-bound of intervals.
[maps] is computed by a left-scan that is done lazily. [last] contains the
last index of [maps] that was computed.
Therefore all cells [maps.(i)] for [0 <= i <= last] are valid, while
[last < i < Array.length.(maps)] still have to be computed.
This is done by [initialize_until].
*)type'at={intervals:'aintervalarray;maps:'aRMap.tarray;mutablelast:int;}letcreatecount~f=letintervals=Array.initcountfinArray.fast_sort(funi1i2->int_comparei1.lboundi2.lbound)intervals;{intervals;maps=Array.makecountRMap.empty;last=-1}letiter(t:_t)~f=Array.iterft.intervals(* Lazy initialization of [t.maps] *)letinitialize_untiltj=letlast=t.lastinifj>lastthen(letcumulative=ref(iflast<0thenRMap.emptyelset.maps.(last))infori=last+1tojdoletinterval=t.intervals.(i)incumulative:=RMap.addinterval!cumulative;(*Printf.eprintf "size: %d\n" (Tree.size !cumulative);*)t.maps.(i)<-!cumulative;done;t.last<-j;)(* Left-leaning binary search on array of intervals *)letclosest_keyintervals(addr:int)=letl=ref0inletr=ref(Array.lengthintervals-1)inwhile!l<=!rdoletm=!l+(!r-!l)/2inletlb=intervals.(m).lboundiniflb<=addrthenl:=m+1elser:=m-1done;if(!l=Array.lengthintervals)||(intervals.(!l).lbound>addr)thendecrl;assert(!l=-1||intervals.(!l).lbound<=addr);!l(* Query algorithm:
- find the closest interval that starts before [addr]
- return the list of all overlapping intervals that end after [addr] *)letqueryt(addr:int64)=letaddr=Int64.to_intaddrinletl=closest_keyt.intervalsaddrinifl=-1then[]else(initialize_untiltl;RMap.list_fromt.maps.(l)addr)(*
Uncomment: switch to eager creation and print counters on interval
computation
let create count ~f =
let result = create count ~f in
let t0 = Sys.time () in
let min0, prom0, maj0 = Gc.counters () in
initialize_until result (count - 1);
let min1, prom1, maj1 = Gc.counters () in
let t1 = Sys.time () in
Printf.eprintf "owee: minor:%.0f prom:%.0f maj:%.0f t:%.02f\n%!"
(min1 -. min0) (prom1 -. prom0) (maj1 -. maj0) (t1 -. t0);
result
*)