123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435letwidth_=8(* Helper functions *)let[@inline]get_bi=Char.code(Bytes.getbi)let[@inline]unsafe_get_bi=Char.code(Bytes.unsafe_getbi)let[@inline]unsafe_set_biv=Bytes.unsafe_setbi(Char.unsafe_chrv)let[@inline]mod_n=nland0b111let[@inline]div_n=nlsr3let[@inline]mul_n=nlsl3letzero=Char.unsafe_chr0(* 0b11111111 *)letall_ones_=Char.unsafe_chr((1lslwidth_)-1)let()=assert(all_ones_=Char.chr0b1111_1111)(* [lsb_mask_ n] is [0b111111] with [n] ones. *)let[@inline]__lsb_maskn=(1lsln)-1(*
from https://en.wikipedia.org/wiki/Hamming_weight
//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64_t x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
// not necessary for int8
// x += x >> 8; //put count of each 16 bits into their lowest 8 bits
// x += x >> 16; //put count of each 32 bits into their lowest 8 bits
// x += x >> 32; //put count of each 64 bits into their lowest 8 bits
return x & 0x7f;
}
m1 = 0x5555555555555555
m2 = 0x3333333333333333
m4 = 0x0f0f0f0f0f0f0f0f
*)let[@inline]__popcount8(b:int):int=letm1=0x55inletm2=0x33inletm4=0x0finletb=b-((blsr1)landm1)inletb=(blandm2)+((blsr2)landm2)inletb=(b+(blsr4))landm4inbland0x7f(*
invariants for [v:t]:
- [Bytes.length v.b >= div_ v.size] (enough storage)
- all bits above [size] are 0 in [v.b]
*)typet={mutableb:bytes;mutablesize:int;}letlengtht=t.sizeletempty()={b=Bytes.empty;size=0}letbytes_length_of_sizesize=ifmod_size=0thendiv_sizeelsediv_size+1letcreate~sizedefault:t=ifsize=0thenempty()else(letn=bytes_length_of_sizesizeinletb=ifdefaultthenBytes.makenall_ones_elseBytes.makenzeroin(* adjust last bits *)letr=mod_sizeinifdefault&&r<>0thenunsafe_set_b(n-1)(__lsb_maskr);{b;size})letcopybv={bvwithb=Bytes.subbv.b0(bytes_length_of_sizebv.size)}let[@inline]capacitybv=mul_(Bytes.lengthbv.b)(* call [f i width(byte[i]) (byte[i])] on each byte.
The last byte might have a width of less than 8. *)letiter_bytes_(b:t)~f:unit=forn=0todiv_b.size-1dof(mul_n)width_(unsafe_get_b.bn)done;letr=mod_b.sizeinifr<>0then(letlast=div_b.sizeinf(mul_last)r(__lsb_maskrlandunsafe_get_b.blast))(* set [byte[i]] to [f(byte[i])] *)letmap_bytes_(b:t)~f:unit=forn=0todiv_b.size-1dounsafe_set_b.bn(f(unsafe_get_b.bn))done;letr=mod_b.sizeinifr<>0then(letlast=div_b.sizeinletmask=__lsb_maskrinunsafe_set_b.blast(masklandf(masklandunsafe_get_b.blast)))letcardinalbv=ifbv.size=0then0else(letn=ref0initer_bytes_bv~f:(fun__b->n:=!n+__popcount8b);!n)letreally_resize_bv~desired~currentsize=bv.size<-size;ifdesired<>currentthen(letb=Bytes.makedesiredzeroinBytes.blitbv.b0b0(mindesiredcurrent);bv.b<-b)(* set bits above [n] to 0 *)let[@inlinenever]clear_bits_above_bvtop=letn=div_topinletj=mod_topinBytes.fillbv.b(n+1)(bytes_length_of_sizebv.size-n-1)(Char.unsafe_chr0);unsafe_set_bv.bn(unsafe_get_bv.bnland__lsb_maskj)let[@inlinenever]grow_to_at_least_real_bvsize=(* beyond capacity *)letcurrent=Bytes.lengthbv.binletdesired=bytes_length_of_sizesizeinletdesired=minSys.max_string_length(maxdesired(current+(current/2)))inassert(desired>current);really_resize_bv~desired~currentsizeletgrow_to_at_least_bvsize=ifsize<=capacitybvthen(* within capacity *)bv.size<-sizeelse(* resize. This is a separate function so it's easier to
inline the happy path. *)grow_to_at_least_real_bvsizeletshrink_bvsize=assert(size<=bv.size);ifsize<bv.sizethen(letdesired=bytes_length_of_sizesizeinletcurrent=Bytes.lengthbv.binclear_bits_above_bvsize;really_resize_bv~desired~currentsize)letresizebvsize=ifsize<0theninvalid_arg"resize: negative size";ifsize<bv.sizethen(clear_bits_above_bvsize;bv.size<-size)elseifsize>bv.sizethengrow_to_at_least_bvsizeletresize_minimize_memorybvsize=ifsize<0theninvalid_arg"resize: negative size";ifsize<bv.sizethenshrink_bvsizeelseifsize>bv.sizethengrow_to_at_least_bvsizeletis_emptybv=bv.size=0||tryfori=0tobytes_length_of_sizebv.size-1doifunsafe_get_bv.bi<>0thenraise_notraceExitdone;truewithExit->falselet[@inline]getbvi=ifi<0theninvalid_arg"get: negative index";letidx_bucket=div_iinletidx_in_byte=mod_iinifidx_bucket<Bytes.lengthbv.bthenunsafe_get_bv.bidx_bucketland(1lslidx_in_byte)<>0elsefalselet[@inline]setbvi=ifi<0theninvalid_arg"set: negative index"else(letidx_bucket=div_iinletidx_in_byte=mod_iinifi>=bv.sizethengrow_to_at_least_bv(i+1);unsafe_set_bv.bidx_bucket(unsafe_get_bv.bidx_bucketlor(1lslidx_in_byte)))letinitsizef:t=letv=create~sizefalseinfori=0tosize-1doiffithensetvidone;vlet[@inline]resetbvi=ifi<0theninvalid_arg"reset: negative index"else(letn=div_iinletj=mod_iinifi>=bv.sizethengrow_to_at_least_bv(i+1);unsafe_set_bv.bn(unsafe_get_bv.bnlandlnot(1lslj)))let[@inline]set_boolbvib=ifbthensetbvielseresetbviletflipbvi=ifi<0theninvalid_arg"reset: negative index"else(letn=div_iinletj=mod_iinifi>=bv.sizethengrow_to_at_least_bv(i+1);unsafe_set_bv.bn(unsafe_get_bv.bnlxor(1lslj)))letclearbv=Bytes.fillbv.b0(Bytes.lengthbv.b)zeroletclear_and_shrinkbv=clearbv;bv.size<-0letequal_bytes_sizeb1b2=tryfori=0tobytes_length_of_sizesize-1doifBytes.getb1i<>Bytes.getb2ithenraise_notraceExitdone;truewithExit->falseletequalxy:bool=x.size=y.size&&equal_bytes_x.sizex.by.bletiterbvf=iter_bytes_bv~f:(funoffwidth_nword_n->fori=0towidth_n-1dof(off+i)(word_nland(1lsli)<>0)done)letiter_truebvf=iterbv(funib->ifbthenfielse())letto_listbv=letl=ref[]initer_truebv(funi->l:=i::!l);!lletto_sorted_listbv=List.rev(to_listbv)(* Interpret these as indices. *)letof_listl=letsize=matchlwith|[]->0|_->List.fold_leftmax0l+1inletbv=create~sizefalseinList.iter(funi->setbvi)l;bvexceptionFoundFirstofintletfirst_exnbv=tryiter_truebv(funi->raise_notrace(FoundFirsti));raiseNot_foundwithFoundFirsti->iletfirstbv=trySome(first_exnbv)withNot_found->Noneletfilterbvp=iter_truebv(funi->ifnot(pi)thenresetbvi)letnegate_selfbv=map_bytes_bv~f:(funb->lnotb)letnegatea=letb=copyainnegate_selfb;bletunion_into_no_resize_~intobv=assert(Bytes.lengthinto.b>=bytes_length_of_sizebv.size);fori=0tobytes_length_of_sizebv.size-1dounsafe_set_into.bi(unsafe_get_into.bilorunsafe_get_bv.bi)done(* Underlying size grows for union. *)letunion_into~intobv=ifinto.size<bv.sizethengrow_to_at_least_intobv.size;union_into_no_resize_~intobv(* To avoid potentially 2 passes, figure out what we need to copy. *)letunionb1b2=ifb1.size<=b2.sizethen(letinto=copyb2inunion_into_no_resize_~intob1;into)else(letinto=copyb1inunion_into_no_resize_~intob2;into)letinter_into_no_resize_~intobv=assert(into.size<=bv.size);fori=0tobytes_length_of_sizeinto.size-1dounsafe_set_into.bi(unsafe_get_into.bilandunsafe_get_bv.bi)done(* Underlying size shrinks for inter. *)letinter_into~intobv=ifinto.size>bv.sizethenshrink_intobv.size;inter_into_no_resize_~intobvletinterb1b2=ifb1.size<=b2.sizethen(letinto=copyb1ininter_into_no_resize_~intob2;into)else(letinto=copyb2ininter_into_no_resize_~intob1;into)(* Underlying size depends on the [in_] set for diff, so we don't change
its size! *)letdiff_into~intobv=letn=min(Bytes.lengthinto.b)(Bytes.lengthbv.b)infori=0ton-1dounsafe_set_into.bi(unsafe_get_into.bilandlnot(unsafe_get_bv.bi))doneletdiffin_not_in=letinto=copyin_indiff_into~intonot_in;intoletselectbvarr=letl=ref[]in(tryiter_truebv(funi->ifi>=Array.lengtharrthenraise_notraceExitelsel:=arr.(i)::!l)withExit->());!lletselectibvarr=letl=ref[]in(tryiter_truebv(funi->ifi>=Array.lengtharrthenraise_notraceExitelsel:=(arr.(i),i)::!l)withExit->());!ltype'aiter=('a->unit)->unitletto_iterbvk=iter_truebvkletof_iterseq=letl=ref[]andmaxi=ref0inseq(funx->l:=x::!l;maxi:=max!maxix);letbv=create~size:(!maxi+1)falseinList.iter(funi->setbvi)!l;bvletppoutbv=Format.pp_print_stringout"bv {";iterbv(fun_ib->Format.pp_print_charout(ifbthen'1'else'0'));Format.pp_print_stringout"}"moduleInternal_=structlet__to_word_lbv=letl=ref[]inBytes.iter(func->l:=c::!l)bv.b;List.rev!llet__popcount8=__popcount8let__lsb_mask=__lsb_masklet__check_invariantself=letn=div_self.sizeinletj=mod_self.sizeinassert(Bytes.lengthself.b>=n);ifj>0thenassert(letc=get_self.bnincland__lsb_maskj=c);fori=n+1toBytes.lengthself.b-1doassert(get_self.bi=0)doneend