123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646open!CoremoduleWith_integer_index=struct(* Kernel hides away Obj-handling. *)moduleKernel:sigtype'atvallength:(_t[@local])->intvalcapacity:_t->intvalcreate:?initial_capacity:int->unit->_tvalunsafe_create_uninitialized:len:int->'atvalinit:int->f:((int->'a)[@local])->'atvalunsafe_get:'at->int->'avalunsafe_set:'at->int->'a->unitvalunsafe_blit:src:('at[@local])->src_pos:int->dst:('at[@local])->dst_pos:int->len:int->unitvalinvariant:'aInvariant.t->'atInvariant.tvalmax_index:_t->intvalgrow_capacity_once:_t->unitvalgrow_capacity_to_at_least:_t->capacity:int->unitvalunsafe_clear_pointer_at:_t->int->unitvalset_length:_t->int->unitvalcopy:'at->'atvalsort:?pos:int->?len:int->'at->compare:('a->'a->int)->unitmoduleWith_structure_details:sigtypenonrec'at='at[@@derivingsexp_of]endend=structtype'at={mutablearr:Obj.tUniform_array.t;mutablelength:int;mutablecapacity:int(** Invariant: [capacity = Uniform_array.length arr].
We maintain it here to eliminate an indirection when accessing long arrays. *)}[@@derivingfields]letlengtht=t.lengthletcheck_capacitycapacity=ifcapacity<0theninvalid_argf"Vec: negative capacity %d"capacity();;(* [initial_capacity] is mostly arbitrary, but it does make our array take one header
word + 7 data words = 8 words * 8 bytes = 64 bytes = one cacheline by default. (Of
course, there's no alignment guarantee.) *)letcreate?(initial_capacity=7)()=check_capacityinitial_capacity;{arr=Uniform_array.unsafe_create_uninitialized~len:initial_capacity;length=0;capacity=initial_capacity};;letunsafe_create_uninitialized~len:n=check_capacityn;{arr=Uniform_array.unsafe_create_uninitialized~len:n;length=n;capacity=n};;letinitn~f=check_capacityn;{arr=Uniform_array.initn~f:(funi->fi|>Obj.magic);length=n;capacity=n};;letcopyt={arr=Uniform_array.copyt.arr;length=t.length;capacity=t.capacity};;let[@inlinealways]unsafe_get(typea)(t:at)i:a=Uniform_array.unsafe_gett.arri|>Obj.magic;;let[@inlinealways]unsafe_set(typea)(t:at)i(element:a)=Uniform_array.unsafe_sett.arri(Obj.reprelement);;let[@inlinealways]unsafe_blit~src~src_pos~dst~dst_pos~len=Uniform_array.unsafe_blit~src:src.arr~src_pos~dst:dst.arr~dst_pos~len;;moduleWith_structure_details=structtypenonrec'at='atletsexp_of_t(typea)(sexp_of_a:a->Sexp.t)(t:at)=let{arr;length;capacity}=tinletelements=Uniform_array.init(Uniform_array.lengtharr)~f:(funi->letelement=Uniform_array.getarriin(* Only the first [length] elements can safely be given to [sexp_of_a]. *)ifi<lengththenelement|>Obj.magic|>sexp_of_aelse(letimm:int=Obj.magicelementinSexp.Atom(sprintf"_%d"imm)))in[%sexp{elements:Sexp.tUniform_array.t;length:int;capacity:int}];;endletinvariant(typea)(a_inv:aInvariant.t)(t:at)=Invariant.invariant[%here]t[%sexp_of:_With_structure_details.t](fun()->let{capacity;length;arr}=tinifcapacity<>Uniform_array.lengtht.arrthenraise_s[%message"capacity should equal Option_array length"(capacity:int)(Uniform_array.lengtht.arr:int)];ifcapacity<0thenraise_s[%message"negative capacity"(capacity:int)];iflength>capacitythenraise_s[%message"length shouldn't be more than capacity"(length:int)(capacity:int)];forpos=0tolength-1doa_inv(Uniform_array.getarrpos|>Obj.magic)done;forpos=lengthtocapacity-1doassert(Uniform_array.getarrpos|>Obj.is_int)done);;let[@inlinealways]max_indext=t.length-1letgrow_capacity_to_exactlyt~capacity=letarr=Uniform_array.unsafe_create_uninitialized~len:capacityinfori=0tomax_indextdoUniform_array.unsafe_setarri(Uniform_array.unsafe_gett.arri)done;t.arr<-arr;t.capacity<-capacity;;letgrowth_factor=2letgrow_capacity_oncet=grow_capacity_to_exactlyt~capacity:(Int.max1t.capacity*growth_factor);;letgrow_capacity_to_at_leastt~capacity:target_capacity=assert(growth_factor=2);ift.capacity<target_capacitythengrow_capacity_to_exactlyt~capacity:(Int.ceil_pow2(Int.max1target_capacity));;let[@inlinealways]unsafe_clear_pointer_attpos=Uniform_array.unsafe_clear_if_pointert.arrpos;;letsort(typea)?pos?lent~(compare:a->a->int)=letcompare:Obj.t->Obj.t->int=Obj.magiccomparein(* [Uniform_array] checks this but has an overestimate of our length. *)letpos,len=Ordered_collection_common.get_pos_len_exn()?pos?len~total_length:(lengtht)inUniform_array.sort~pos~lent.arr~compare;;endincludeKernelletis_sortedt~compare=(* This is a copy-paste from [Array.is_sorted]. *)leti=ref(lengtht-1)inletresult=reftrueinwhile!i>0&&!resultdoletelt_i=unsafe_gett!iinletelt_i_minus_1=unsafe_gett(!i-1)inifcompareelt_i_minus_1elt_i>0thenresult:=false;decridone;!result;;letnext_free_index=lengthlet[@cold]raise__bad_indexti~op=raise_s[%message"tried to access vec out of bounds"(t:_With_structure_details.t)(i:int)(op:string)];;let[@inlinealways]check_indexti~op=ifi<0||i>=lengthtthenraise__bad_indexti~op;;letgetti=check_indexti~op:"get";unsafe_getti;;letmaybe_getti=ifi<0||i>=lengthtthenNoneelseSome(unsafe_getti)letsettielement=check_indexti~op:"set";unsafe_settielement;;let[@inlinealways]push_back__we_know_we_have_spacetelement=letlength=lengthtinunsafe_settlengthelement;set_lengtht(length+1);;letpush_back_indextelement=letlength=lengthtiniflength=capacitytthengrow_capacity_oncet;push_back__we_know_we_have_spacetelement;length;;let[@inlinealways]push_backtelement=let(_:int)=push_back_indextelementin();;letremove_exnti=ifi<0||i>=lengthtthenraise__bad_indexti~op:"remove_exn";letnew_length=lengtht-1in(* As per the ocaml stdlib documentation, blitting with src and dst
overlapping is safe.
https://github.com/ocaml-flambda/flambda-backend/blob/main/ocaml/stdlib/array.mli#L143
*)unsafe_blit~src:t~src_pos:(i+1)~dst:t~dst_pos:i~len:(lengtht-i-1);set_lengthtnew_length;;let[@inlinealways]unsafe_peek_back_exnt=unsafe_gett(max_indext)letpeek_back_exnt=letlength=lengthtiniflength<=0thenraise__bad_indextlength~op:"peek_back";unsafe_peek_back_exnt;;letpeek_backt=iflengtht<=0thenNoneelseSome(unsafe_peek_back_exnt)let[@inlinealways]pop_back_unit_exnt=letpos=max_indextin(* Don't leak the value. *)unsafe_clear_pointer_attpos;set_lengthtpos;;letpop_back_exnt=lete=peek_back_exntinpop_back_unit_exnt;e;;letgrow_tot~len~default=iflen>lengthtthen(grow_capacity_to_at_leastt~capacity:len;fori=lengthttolen-1dounsafe_settidefaultdone;set_lengthtlen);;letshrink_tot~len=iflen<0thenraise__bad_indextlen~op:"shrink_to"elseiflen<lengthtthen(fori=lentomax_indextdounsafe_clear_pointer_attidone;set_lengthtlen);;letiterit~f=fori=0tomax_indextdofi(unsafe_getti)done;;letitert~f=fori=0tomax_indextdof(unsafe_getti)done;;letto_listt=letresult=ref[]infori=max_indextdownto0doresult:=unsafe_getti::!resultdone;!result;;letto_alistt=letresult=ref[]infori=max_indextdownto0doresult:=(i,unsafe_getti)::!resultdone;!result;;letof_listxs=lett=create~initial_capacity:(List.lengthxs)()inList.iterxs~f:(push_backt);t;;letof_arrayarr=init(Array.lengtharr)~f:(funi->Array.getarri)letfoldt~init~f=letr=refinitinfori=0tomax_indextdor:=f!r(unsafe_getti)done;!r;;includeBlit.Make1(structtypenonrec'at='atletcreate_like~len_t=(* Note that even though we [unsafe_create_uninitialized], every time this function
is called, the [Vec] is immediately blitted with valid values. *)Kernel.unsafe_create_uninitialized~len;;letlength=lengthletunsafe_blit=unsafe_blitend)(** Returns the length of the longest prefix for which [f] is true. *)lettake_while_lent~f:(f[@local])=letrecloopi=ifi>=lengtht||not(f(getti))thenielse(loop[@tailcall])(i+1)inloop0[@nontail];;lettake_whilet~f=letlen=take_while_lent~finsubt~pos:0~len;;moduleInplace=structletsubt~pos~len=Ordered_collection_common.check_pos_len_exn~pos~len~total_length:(lengtht);ifpos<>0thenblit~src:t~src_pos:pos~dst:t~dst_pos:0~len;shrink_tot~len;;lettake_whilet~f=letto_len=take_while_lent~finshrink_tot~len:to_len;;letfiltert~f=letdest=ref0infori=0tomax_indextdoletx=unsafe_gettiiniffxthen(if!dest<ithenunsafe_sett!destx;incrdest)done;letdest=!destinshrink_tot~len:dest;;letmapt~f=fori=0tomax_indextdounsafe_setti(f(unsafe_getti))done;;letmapit~f=fori=0tomax_indextdounsafe_setti(fi(unsafe_getti))done;;endletrecforall2__same_lengtht1t2~filength=ifi>=lengththentrueelsef(unsafe_gett1i)(unsafe_gett2i)&&forall2__same_lengtht1t2~f(i+1)length;;letequalequaltt'=iflengtht<>lengtht'thenfalseelseforall2__same_lengthtt'~f:equal0(lengtht);;letcleart=iflengtht>0thenshrink_tot~len:0letsexp_of_t(typea)(sexp_of_a:a->Sexp.t)t=lett=to_listtin[%sexp(t:alist)];;letis_emptyt=lengtht=0letexistst~f=leti=ref0inletn=lengthtinletresult=reffalseinwhile!i<n&¬!resultdoiff(unsafe_gett!i)thenresult:=trueelseincridone;!result;;letfor_allt~f=leti=ref0inletn=lengthtinletresult=reftrueinwhile!i<n&&!resultdoiff(unsafe_gett!i)thenincrielseresult:=falsedone;!result;;letmemta~equal=(exists[@inlinedhint])t~f:(equala)[@nontail]letcountt~f=Container.count~foldt~fletsummodule_t~f=Container.sum~foldmodule_t~f(* The code for [find] and [find_exn] would be simpler (wouldn't involve threading
through [max_index]) if we iterated backward, but we iterate forward to be consistent
with other containers. *)letrecfind't~f~max_indexi=ifi>max_indexthenNoneelse(letx=unsafe_gettiiniffxthenSomexelsefind't~f~max_index(i+1));;let[@cold]raise__not_found()=raise(Base.Not_found_s[%message"Vec.find_exn: not found"]);;letrecfind_exn't~f~max_indexi=ifi>max_indexthenraise__not_found()else(letx=unsafe_gettiiniffxthenxelsefind_exn't~f~max_index(i+1));;letfindt~f=find't~f~max_index:(max_indext)0letfind_exnt~f=find_exn't~f~max_index:(max_indext)0letrecfindi't~f~max_indexi=ifi>max_indexthenNoneelse(letx=unsafe_gettiiniffxthenSome(i,x)elsefindi't~f~max_index(i+1));;letfindit~f=findi't~f~max_index:(max_indext)0letfind_and_removet~f=matchfindit~fwith|None->None|Some(i,found)->remove_exnti;Somefound;;letrecfind_map't~f~max_indexi=ifi>max_indexthenNoneelse(matchf(unsafe_getti)with|None->find_map't~f~max_index(i+1)|some->some);;letfind_mapt~f=find_map't~f~max_index:(max_indext)0letrecfold_result't~f~acc~max_indexi=ifi>max_indexthenOkaccelse(matchfacc(unsafe_getti)with|Okacc->fold_result't~f~max_index(i+1)~acc|err->err);;letfold_resultt~init~f=fold_result't~f~acc:init~max_index:(max_indext)0letrecfold_until't~f~acc~finish~max_indexi=ifi>max_indexthenfinishaccelse(match(facc(unsafe_getti):_Continue_or_stop.t)with|Stops->s|Continueacc->fold_until't~f~max_index(i+1)~acc~finish);;letfold_untilt~init~f~finish=fold_until't~f~acc:init~finish~max_index:(max_indext)0;;letmax_eltt~compare=ifis_emptytthenNoneelse(letmax=ref(unsafe_gett0)infori=1tomax_indextdoletx=unsafe_gettiinletmax'=!maxinmax:=ifcomparemax'x<0thenxelsemax'done;Some!max);;letmin_eltt~compare=ifis_emptytthenNoneelse(letmin=ref(unsafe_gett0)infori=1tomax_indextdoletx=unsafe_gettiinletmin'=!mininmin:=ifcomparemin'x>0thenxelsemin'done;Some!min);;letto_arrayt=Array.init(lengtht)~f:(unsafe_gett)lett_of_sexpa_of_sexpt=of_list([%of_sexp:alist]t)letcomparecmpt1t2=letlen1=lengtht1inletlen2=lengtht2inletmin_len=Int.minlen1len2inletresult=ref0inleti=ref0inwhile!i<min_len&&!result=0doresult:=cmp(unsafe_gett1!i)(unsafe_gett2!i);i:=!i+1done;if!result=0thenInt.comparelen1len2else!result;;letunsafe_swaptij=lete=unsafe_gettiinunsafe_setti(unsafe_gettj);unsafe_settje;;letswaptij=check_indexti~op:"swap";check_indextj~op:"swap";unsafe_swaptij;;letswap_to_last_and_popti=check_indexti~op:"swap_to_last_and_pop";unsafe_swapti(max_indext);pop_back_exnt;;moduleStable=structmoduleV1=structtypenonrec'at='at[@@derivingcompare,sexp]includeBin_prot.Utils.Make_iterable_binable1(structtypenonrec'at='attype'ael='a[@@derivingbin_io]letcaller_identity=Bin_prot.Shape.Uuid.of_string"2ec1d047-7cf8-49bc-991b-0badd17d8359";;letmodule_name=Some"Vec"letinit~len~next=initlen~f:(fun_->next())letiter=iterletlength=lengthend)endendendincludeWith_integer_indexmoduletypeS=Vec_intf.SmoduleMake(M:Intable.S)=structincludeWith_integer_indexlet[@inlinealways]unsafe_gettindex=unsafe_gett(M.to_int_exnindex)letgettindex=gett(M.to_int_exnindex)letmaybe_gettindex=maybe_gett(M.to_int_exnindex)let[@inlinealways]unsafe_settindex=unsafe_sett(M.to_int_exnindex)letsettindex=sett(M.to_int_exnindex)letnext_free_indext=next_free_indext|>M.of_int_exnletiterit~f=iterit~f:(fun[@inline]intx->f(M.of_int_exnint)x)[@nontail]letpush_back_indextelement=push_back_indextelement|>M.of_int_exnletto_alistt=(* We could do:
{[
to_alist t |> List.map ~f:(fun (i, x) -> M.of_int_exn i, x)
]}
at the expense of an extra allocation. This is a bit more copy-pasty,
but avoids that.
*)letresult=ref[]infori=max_indextdownto0doletm=M.of_int_exniinresult:=(m,unsafe_gettm)::!resultdone;!result;;moduleInplace=structincludeInplaceletsubt~pos~len=subt~pos:(M.to_int_exnpos)~lenletmapit~f=mapit~f:(fun[@inline]intx->f(M.of_int_exnint)x)[@nontail]endletswaptindex1index2=swapt(M.to_int_exnindex1)(M.to_int_exnindex2)letswap_to_last_and_poptindex=swap_to_last_and_popt(M.to_int_exnindex)end