123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406open!Importtypet=Bits0.tmoduleUnsafe=structletdatat=t.Bits0.dataendmoduleMutable=structincludeBits0externalunsafe_get:Bytes.t->int->int64="%caml_bytes_get64u"externalunsafe_set:Bytes.t->int->int64->unit="%caml_bytes_set64u"letunsafe_getbi=unsafe_getb(ilslshift_bytes_to_words)letunsafe_setbiv=unsafe_setb(ilslshift_bytes_to_words)vletis_emptya=a.width=0letof_constantt=tletto_constantt=tletto_stringt=Constant.to_binary_stringtletto_intt=Constant.to_inttletcopy~src~dst=letwords=wordssrcinfori=0towords-1dounsafe_setdst.datai(unsafe_getsrc.datai)done;;letwire_=emptylet(--)a_=aletvdd=of_constant(Constant.of_int~width:11)letgnd=of_constant(Constant.of_int~width:10)let(&:)cab=letwords=wordsainfori=0towords-1dounsafe_setc.datai(Int64.(land)(unsafe_geta.datai)(unsafe_getb.datai))done;;let(|:)cab=letwords=wordsainfori=0towords-1dounsafe_setc.datai(Int64.(lor)(unsafe_geta.datai)(unsafe_getb.datai))done;;let(^:)cab=letwords=wordsainfori=0towords-1dounsafe_setc.datai(Int64.(lxor)(unsafe_geta.datai)(unsafe_getb.datai))done;;externalmask:(int[@untagged])->Bytes.t->unit="hardcaml_bits_mask_bc""hardcaml_bits_mask"[@@noalloc]let(~:)ca=letwords=wordsainfori=0towords-1dounsafe_setc.datai(Int64.lnot(unsafe_geta.datai))done;maskc.widthc.data;;externaladd:(int[@untagged])->Bytes.t->Bytes.t->Bytes.t->unit="hardcaml_bits_add_bc""hardcaml_bits_add"[@@noalloc]let(+:)dstab=letwidth=widthdstinaddwidthdst.dataa.datab.data;maskwidthdst.data;;externalsub:(int[@untagged])->Bytes.t->Bytes.t->Bytes.t->unit="hardcaml_bits_sub_bc""hardcaml_bits_sub"[@@noalloc]let(-:)dstab=letwidth=widthdstinsubwidthdst.dataa.datab.data;maskwidthdst.data;;(* Unsigned Int64 compare. *)externalcmpu64:(Int64.t[@unboxed])->(Int64.t[@unboxed])->(int[@untagged])="hardcaml_bits_uint64_compare_bc""hardcaml_bits_uint64_compare"[@@noalloc]letreceqwordsiab=ifi=wordsthenInt64.oneelseifInt64.equal(unsafe_geta.datai)(unsafe_getb.datai)theneqwords(i+1)abelseInt64.zero;;let(==:)cab=letwords=wordsainunsafe_setc.data0(eqwords0ab);;letrecneqwordsiab=ifi=wordsthenInt64.zeroelseifInt64.equal(unsafe_geta.datai)(unsafe_getb.datai)thenneqwords(i+1)abelseInt64.one;;let(<>:)cab=letwords=wordsainunsafe_setc.data0(neqwords0ab);;letrecltiab=ifi<0thenInt64.zero(* must be equal *)else(matchcmpu64(unsafe_geta.datai)(unsafe_getb.datai)with|-1->Int64.one|0->lt(i-1)ab|_->Int64.zero);;let(<:)cab=letwords=wordsainunsafe_setc.data0(lt(words-1)ab);;let[@cold]raise_mux_of_empty_list()=raise_s[%message"Bits.mux unexpected empty list"];;(* For [mux2] this is ever so slightly slower. For mux16, it's slightly faster. *)letrecmux_findidxnl=matchlwith|[]->raise_mux_of_empty_list()|[h]->h|h::t->ifidx=nthenhelsemux_findidx(n+1)t;;letmuxdstsell=letidx=to_intselincopy~src:(mux_findidx0l)~dst;;letcatc_wordsa_widthab=leta_words=words_of_widtha_widthinletb_width,b_words=widthb,wordsbinleta_bits=a_widthlandwidth_maskinleta,b=a.data,b.datainifa_bits=0then(* aligned *)fori=0tob_words-1dounsafe_seta(a_words+i)(unsafe_getbi)doneelse(* not aligned *)fori=0tob_words-1doletx=unsafe_geta(a_words-1+i)inlety=unsafe_getbiinletx=Int64.(xlor(ylsla_bits))inunsafe_seta(a_words-1+i)x;ifa_words+i<c_wordsthen(lety=Int64.(ylsrInt.(bits_per_word-a_bits))inunsafe_seta(a_words+i)y)done;a_width+b_width;;(* This implementation allocates due to [List.rev], but is slightly faster:
{[
let rec cat_iter c_words width c t =
match t with
| [] -> ()
| h :: t ->
let width = cat c_words width c h in
cat_iter c_words width c t
let concat_fast_allocs c l =
let c_words = words c in
match List.rev l with
| [] -> ()
| h :: t ->
copy ~src:h ~dst:c;
cat_iter c_words (width h) c t
]} *)letreccat_iter_backc_wordswidth_cl=matchlwith|[]->()|h::t->letwidth=width_-widthhincat_iter_backc_wordswidthct;ignore(catc_wordswidthch:int);;letconcatcl=letc_words=wordscincat_iter_backc_words(widthc)cl;;letwordw=wlsrlog_bits_per_wordletselectcshl=letc_width=h-l+1inletc_words=wordscinlets_bits=llandwidth_maskinletlo_word=wordlinlethi_word=wordhinlets=s.datainifs_bits=0thenfori=0toc_words-1dounsafe_setc.datai(unsafe_gets(lo_word+i))doneelsefori=0toc_words-1doletj=lo_word+iinleta=unsafe_getsjinletb=ifj>=hi_wordthenInt64.zeroelseunsafe_gets(j+1)inletx=Int64.((alsrs_bits)lor(blslInt.(bits_per_word-s_bits)))inunsafe_setc.dataixdone;maskc_widthc.data;;externalumul:Bytes.t->Bytes.t->Bytes.t->(int[@untagged])->(int[@untagged])->unit="hardcaml_bits_umul_bc""hardcaml_bits_umul"[@@noalloc]externalsmul:Bytes.t->Bytes.t->Bytes.t->(int[@untagged])->(int[@untagged])->unit="hardcaml_bits_smul_bc""hardcaml_bits_smul"[@@noalloc]let(*:)dstab=umuldst.dataa.datab.data(widtha)(widthb);mask(widthdst)dst.data;;let(*+)dstab=smuldst.dataa.datab.data(widtha)(widthb);mask(widthdst)dst.data;;letnum_words=wordsletget_wordt=unsafe_gett.dataletset_wordt=unsafe_sett.dataletto_bitst=letresult=create(widtht)incopy~src:t~dst:result;result;;letcopy_bits=copymoduleComb=Comb.Make(structtypet=Bits0.tletequal=Bits0.Comparable.equalletempty=emptyletis_empty=is_emptyletwidth=widthletof_constant=of_constantletto_constant=to_constantletadd_widthswy=w+widthyletconcat_msbl=letw=List.foldl~init:0~f:add_widthsinletc=createwinconcatcl;c;;(* this is specialised to return an element from the input list, rather than
construct a new output. *)letmuxsell=letidx=to_intselinmux_findidx0l;;letselectshl=letw=h-l+1inletc=createwinselectcshl;c;;let(--)=(--)let(&:)ab=letc=create(widtha)in(&:)cab;c;;let(|:)ab=letc=create(widtha)in(|:)cab;c;;let(^:)ab=letc=create(widtha)in(^:)cab;c;;let(~:)a=letc=create(widtha)in(~:)ca;c;;let(+:)ab=letc=create(widtha)in(+:)cab;c;;let(-:)ab=letc=create(widtha)in(-:)cab;c;;let(*:)ab=letc=create(widtha+widthb)in(*:)cab;c;;let(*+)ab=letc=create(widtha+widthb)in(*+)cab;c;;let(==:)ab=letc=create1in(==:)cab;c;;let(<:)ab=letc=create1in(<:)cab;c;;letto_string=to_stringletsexp_of_t(s:t)=[%sexp(to_constants|>Constant.to_binary_string:string)]end)endinclude(Mutable.Comb:Comb.Swithtypet:=Bits0.t)includeBits0.Comparable(* Override the functor implementations, as these allocate less (to_int doesn't allocate
at all) *)letto_intx=Constant.to_intxletto_int32x=Constant.to_int32xletzerow=Bits0.createwletppfmtt=Caml.Format.fprintffmt"%s"(to_bstrt)modulePP=Pretty_printer.Register(structtypenonrect=Bits0.tletmodule_name="Hardcaml.Bits"letto_string=to_bstrend)