123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963(*
* Copyright (c) 2012 Anil Madhavapeddy <anil@recoil.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)typebuffer=(char,Bigarray.int8_unsigned_elt,Bigarray.c_layout)Bigarray.Array1.t(* Note:
*
* We try to maintain the property that no constructed [t] can ever point out of
* its underlying buffer. This property is guarded by all of the constructing
* functions and the fact that the type is private, and used by various
* functions that would otherwise be completely unsafe.
*
* Furthermore, no operation on [t] is allowed to extend the view on the
* underlying Bigarray structure, only narrowing is allowed.
*
* All well-intended souls are kindly invited to cross-check that the code
* indeed maintains this invariant.
*)typet={buffer:buffer;off:int;len:int;}letpp_tppft=Format.fprintfppf"[%d,%d](%d)"t.offt.len(Bigarray.Array1.dimt.buffer)letstring_tppfstr=Format.fprintfppf"[%d]"(String.lengthstr)letbytes_tppfstr=Format.fprintfppf"[%d]"(Bytes.lengthstr)leterrfmt=letb=Buffer.create20in(* for thread safety. *)letppf=Format.formatter_of_bufferbinletkppf=Format.pp_print_flushppf();invalid_arg(Buffer.contentsb)inFormat.kfprintfkppffmtleterr_of_bigarrayt=err"Cstruct.of_bigarray off=%d len=%d"tleterr_subt=err"Cstruct.sub: %a off=%d len=%d"pp_ttleterr_shiftt=err"Cstruct.shift %a %d"pp_ttleterr_shiftvn=err"Cstruct.shiftv short by %d"nleterr_copy_to_stringcallert=err"Cstruct.%s %a off=%d len=%d"callerpp_ttleterr_to_hex_stringt=err"Cstruct.to_hex_string %a off=%d len=%d"pp_ttleterr_blit_srcsrcdst=err"Cstruct.blit src=%a dst=%a src-off=%d len=%d"pp_tsrcpp_tdstleterr_blit_dstsrcdst=err"Cstruct.blit src=%a dst=%a dst-off=%d len=%d"pp_tsrcpp_tdstleterr_blit_from_string_srcsrcdst=err"Cstruct.blit_from_string src=%a dst=%a src-off=%d len=%d"string_tsrcpp_tdstleterr_blit_from_string_dstsrcdst=err"Cstruct.blit_from_string src=%a dst=%a dst-off=%d len=%d"string_tsrcpp_tdstleterr_blit_from_bytes_srcsrcdst=err"Cstruct.blit_from_bytes src=%a dst=%a src-off=%d len=%d"bytes_tsrcpp_tdstleterr_blit_from_bytes_dstsrcdst=err"Cstruct.blit_from_bytes src=%a dst=%a dst-off=%d len=%d"bytes_tsrcpp_tdstleterr_blit_to_bytes_srcsrcdst=err"Cstruct.blit_to_bytes src=%a dst=%a src-off=%d len=%d"pp_tsrcbytes_tdstleterr_blit_to_bytes_dstsrcdst=err"Cstruct.blit_to_bytes src=%a dst=%a dst-off=%d len=%d"pp_tsrcbytes_tdstleterr_invalid_boundsf=err"invalid bounds in Cstruct.%s %a off=%d len=%d"fpp_t[@@inlinenever]leterr_splitt=err"Cstruct.split %a start=%d off=%d"pp_ttleterr_itert=err"Cstruct.iter %a i=%d len=%d"pp_ttletof_bigarray?(off=0)?lenbuffer=letdim=Bigarray.Array1.dimbufferinletlen=matchlenwith|None->dim-off|Somelen->leninifoff<0||len<0||off+len<0||off+len>dimthenerr_of_bigarrayofflenelse{buffer;off;len}letto_bigarraybuffer=Bigarray.Array1.subbuffer.bufferbuffer.offbuffer.lenletcreate_unsafelen=letbuffer=Bigarray.(Array1.createcharc_layoutlen)in{buffer;len;off=0}letcheck_boundstlen=len>=0&&Bigarray.Array1.dimt.buffer>=lenletempty=create_unsafe0externalcheck_alignment_bigstring:buffer->int->int->bool="caml_check_alignment_bigstring"letcheck_alignmenttalignment=ifalignment>0thencheck_alignment_bigstringt.buffert.offalignmentelseinvalid_arg"check_alignment must be positive integer"typebyte=charletbyte(i:int):byte=Char.chriletbyte_to_int(b:byte)=int_of_charbtypeuint8=inttypeuint16=inttypeuint32=int32typeuint64=int64letdebugt=letmax_len=Bigarray.Array1.dimt.bufferinift.off+t.len>max_len||t.len<0||t.off<0then(Format.printf"ERROR: t.off+t.len=%d %a\n%!"(t.off+t.len)pp_tt;assertfalse;)elseFormat.asprintf"%a"pp_ttletsubtofflen=(* from https://github.com/mirage/ocaml-cstruct/pull/245
Cstruct.sub should select what a programmer intuitively expects a
sub-cstruct to be. I imagine holding out my hands, with the left
representing the start offset and the right the end. I think of a
sub-cstruct as any span within this range. If I move my left hand only to
the right (new_start >= t.off), and my right hand only to the left
(new_end <= old_end), and they don't cross (new_start <= new_end), then I
feel sure the result will be a valid sub-cstruct. And if I violate any one
of these constraints (e.g. moving my left hand further left), then I feel
sure that the result wouldn't be something I'd consider to be a sub-cstruct.
Wrapping considerations in modular arithmetic:
Note that if x is non-negative, and x + y wraps, then x + y must be
negative. This is easy to see with modular arithmetic because if y is
negative then the two arguments will cancel to some degree the result
cannot be further from zero than one of the arguments. If y is positive
then x + y can wrap, but even max_int + max_int doesn't wrap all the way to
zero.
The three possibly-wrapping operations are:
new_start = t.off + off. t.off is non-negative so if this wraps then
new_start will be negative and will fail the new_start >= t.off test.
new_end = new_start + len. The above test ensures that new_start is
non-negative in any successful return. So if this wraps then new_end will
be negative and will fail the new_start <= new_end test.
old_end = t.off + t.len. This uses only the existing trusted values. It
could only wrap if the underlying bigarray had a negative length! *)letnew_start=t.off+offinletnew_end=new_start+leninletold_end=t.off+t.leninifnew_start>=t.off&&new_end<=old_end&&new_start<=new_endthen{twithoff=new_start;len}elseerr_subtofflenletshifttamount=letoff=t.off+amountinletlen=t.len-amountinifamount<0||amount>t.len||not(check_boundst(off+len))thenerr_shifttamountelse{twithoff;len}letrecskip_empty=function|t::tswhent.len=0->skip_emptyts|x->xletrecshiftvts=function|0->skip_emptyts|n->matchtswith|[]->err_shiftvn|t::tswhenn>=t.len->shiftvts(n-t.len)|t::ts->shifttn::tsexternalunsafe_blit_bigstring_to_bigstring:buffer->int->buffer->int->int->unit="caml_blit_bigstring_to_bigstring"[@@noalloc]externalunsafe_blit_string_to_bigstring:string->int->buffer->int->int->unit="caml_blit_string_to_bigstring"[@@noalloc]externalunsafe_blit_bytes_to_bigstring:Bytes.t->int->buffer->int->int->unit="caml_blit_string_to_bigstring"[@@noalloc]externalunsafe_blit_bigstring_to_bytes:buffer->int->Bytes.t->int->int->unit="caml_blit_bigstring_to_string"[@@noalloc]externalunsafe_compare_bigstring:buffer->int->buffer->int->int->int="caml_compare_bigstring"[@@noalloc]externalunsafe_fill_bigstring:buffer->int->int->int->unit="caml_fill_bigstring"[@@noalloc]letcopy_to_stringcallersrcsrcofflen=iflen<0||srcoff<0||src.len-srcoff<lenthenerr_copy_to_stringcallersrcsrcofflenelseletb=Bytes.createleninunsafe_blit_bigstring_to_bytessrc.buffer(src.off+srcoff)b0len;(* The following call is safe, since b is not visible elsewhere. *)Bytes.unsafe_to_stringbletcopy=copy_to_string"copy"letblitsrcsrcoffdstdstofflen=iflen<0||srcoff<0||src.len-srcoff<lenthenerr_blit_srcsrcdstsrcofflenelseifdstoff<0||dst.len-dstoff<lenthenerr_blit_dstsrcdstdstofflenelseunsafe_blit_bigstring_to_bigstringsrc.buffer(src.off+srcoff)dst.buffer(dst.off+dstoff)lenletsub_copycstrofflen:t=letcstr2=create_unsafeleninblitcstroffcstr20len;cstr2letblit_from_stringsrcsrcoffdstdstofflen=iflen<0||srcoff<0||dstoff<0||String.lengthsrc-srcoff<lenthenerr_blit_from_string_srcsrcdstsrcofflenelseifdst.len-dstoff<lenthenerr_blit_from_string_dstsrcdstdstofflenelseunsafe_blit_string_to_bigstringsrcsrcoffdst.buffer(dst.off+dstoff)lenletblit_from_bytessrcsrcoffdstdstofflen=iflen<0||srcoff<0||dstoff<0||Bytes.lengthsrc-srcoff<lenthenerr_blit_from_bytes_srcsrcdstsrcofflenelseifdst.len-dstoff<lenthenerr_blit_from_bytes_dstsrcdstdstofflenelseunsafe_blit_bytes_to_bigstringsrcsrcoffdst.buffer(dst.off+dstoff)lenletblit_to_bytessrcsrcoffdstdstofflen=iflen<0||srcoff<0||dstoff<0||src.len-srcoff<lenthenerr_blit_to_bytes_srcsrcdstsrcofflenelseifBytes.lengthdst-dstoff<lenthenerr_blit_to_bytes_dstsrcdstdstofflenelseunsafe_blit_bigstring_to_bytessrc.buffer(src.off+srcoff)dstdstofflenletcomparet1t2=letl1=t1.lenandl2=t2.leninmatchcomparel1l2with|0->(matchunsafe_compare_bigstringt1.buffert1.offt2.buffert2.offl1with|0->0|r->ifr<0then-1else1)|r->rletequalt1t2=comparet1t2=0(* Note that this is only safe as long as all [t]s are coherent. *)letmemsettx=unsafe_fill_bigstringt.buffert.offt.lenxletcreatelen=lett=create_unsafeleninmemsett0;tletset_uint8tic=ifi>=t.len||i<0thenerr_invalid_bounds"set_uint8"ti1elseBigarray.Array1.sett.buffer(t.off+i)(Char.unsafe_chrc)letset_chartic=ifi>=t.len||i<0thenerr_invalid_bounds"set_char"ti1elseBigarray.Array1.sett.buffer(t.off+i)cletget_uint8ti=ifi>=t.len||i<0thenerr_invalid_bounds"get_uint8"ti1elseChar.code(Bigarray.Array1.gett.buffer(t.off+i))letget_charti=ifi>=t.len||i<0thenerr_invalid_bounds"get_char"ti1elseBigarray.Array1.gett.buffer(t.off+i)externalba_set_int16:buffer->int->uint16->unit="%caml_bigstring_set16u"externalba_set_int32:buffer->int->uint32->unit="%caml_bigstring_set32u"externalba_set_int64:buffer->int->uint64->unit="%caml_bigstring_set64u"externalba_get_int16:buffer->int->uint16="%caml_bigstring_get16u"externalba_get_int32:buffer->int->uint32="%caml_bigstring_get32u"externalba_get_int64:buffer->int->uint64="%caml_bigstring_get64u"externalswap16:int->int="%bswap16"externalswap32:int32->int32="%bswap_int32"externalswap64:int64->int64="%bswap_int64"letset_uint16swapptic=ifi>t.len-2||i<0thenerr_invalid_bounds(p^".set_uint16")ti2elseba_set_int16t.buffer(t.off+i)(ifswapthenswap16celsec)[@@inline]letset_uint32swapptic=ifi>t.len-4||i<0thenerr_invalid_bounds(p^".set_uint32")ti4elseba_set_int32t.buffer(t.off+i)(ifswapthenswap32celsec)[@@inline]letset_uint64swapptic=ifi>t.len-8||i<0thenerr_invalid_bounds(p^".set_uint64")ti8elseba_set_int64t.buffer(t.off+i)(ifswapthenswap64celsec)[@@inline]letget_uint16swappti=ifi>t.len-2||i<0thenerr_invalid_bounds(p^".get_uint16")ti2elseletr=ba_get_int16t.buffer(t.off+i)inifswapthenswap16relser[@@inline]letget_uint32swappti=ifi>t.len-4||i<0thenerr_invalid_bounds(p^".get_uint32")ti4elseletr=ba_get_int32t.buffer(t.off+i)inifswapthenswap32relser[@@inline]letget_uint64swappti=ifi>t.len-8||i<0thenerr_invalid_bounds(p^".get_uint64")ti8elseletr=ba_get_int64t.buffer(t.off+i)inifswapthenswap64relser[@@inline]moduleBE=structletset_uint16tic=set_uint16(notSys.big_endian)"BE"tic[@@inline]letset_uint32tic=set_uint32(notSys.big_endian)"BE"tic[@@inline]letset_uint64tic=set_uint64(notSys.big_endian)"BE"tic[@@inline]letget_uint16ti=get_uint16(notSys.big_endian)"BE"ti[@@inline]letget_uint32ti=get_uint32(notSys.big_endian)"BE"ti[@@inline]letget_uint64ti=get_uint64(notSys.big_endian)"BE"ti[@@inline]endmoduleLE=structletset_uint16tic=set_uint16Sys.big_endian"LE"tic[@@inline]letset_uint32tic=set_uint32Sys.big_endian"LE"tic[@@inline]letset_uint64tic=set_uint64Sys.big_endian"LE"tic[@@inline]letget_uint16ti=get_uint16Sys.big_endian"LE"ti[@@inline]letget_uint32ti=get_uint32Sys.big_endian"LE"ti[@@inline]letget_uint64ti=get_uint64Sys.big_endian"LE"ti[@@inline]endmoduleHE=structletset_uint16tic=set_uint16false"HE"tic[@@inline]letset_uint32tic=set_uint32false"HE"tic[@@inline]letset_uint64tic=set_uint64false"HE"tic[@@inline]letget_uint16ti=get_uint16false"HE"ti[@@inline]letget_uint32ti=get_uint32false"HE"ti[@@inline]letget_uint64ti=get_uint64false"HE"ti[@@inline]endletlength{len;_}=len(** [sum_lengths ~caller acc l] is [acc] plus the sum of the lengths
of the elements of [l]. Raises [Invalid_argument caller] if
arithmetic overflows. *)letrecsum_lengths_aux~calleracc=function|[]->acc|h::t->letsum=lengthh+accinifsum<acctheninvalid_argcallerelsesum_lengths_aux~callersumtletsum_lengths~callerl=sum_lengths_aux~caller0lletlenvl=sum_lengths~caller:"Cstruct.lenv"lletcopyvts=letsz=sum_lengths~caller:"Cstruct.copyv"tsinletdst=Bytes.createszinlet_=List.fold_left(funoffsrc->letx=lengthsrcinunsafe_blit_bigstring_to_bytessrc.buffersrc.offdstoffx;off+x)0tsin(* The following call is safe, since dst is not visible elsewhere. *)Bytes.unsafe_to_stringdstletfillv~src~dst=letrecauxdstn=function|[]->n,[]|hd::tl->letavail=lengthdstinletfirst=lengthhdiniffirst<=availthen(blithd0dst0first;aux(shiftdstfirst)(n+first)tl)else(blithd0dst0avail;letrest_hd=shifthdavailin(n+avail,rest_hd::tl))inauxdst0srcletto_string?(off=0)?len:szt=letlen=matchszwithNone->lengtht-off|Somel->lincopy_to_string"to_string"tofflenletto_hex_string?(off=0)?len:szt:string=let[@inline]nibble_to_char(i:int):char=ifi<10thenChar.chr(i+Char.code'0')elseChar.chr(i-10+Char.code'a')inletlen=matchszwithNone->lengtht-off|Somel->liniflen<0||off<0||t.len-off<lenthenerr_to_hex_stringtofflenelse(letout=Bytes.create(2*len)infori=0tolen-1doletc=Char.code@@Bigarray.Array1.gett.buffer(i+t.off+off)inBytes.setout(2*i)(nibble_to_char(clsr4));Bytes.setout(2*i+1)(nibble_to_char(cland0xf));done;Bytes.unsafe_to_stringout)letto_bytes?off?lent=Bytes.unsafe_of_string(to_string?off?lent)let[@inlinealways]of_data_abstractblitfunlenfun?allocator?(off=0)?lenbuf=letbuflen=matchlenwith|None->lenfunbuf-off|Somelen->leninmatchallocatorwith|None->letc=create_unsafebufleninblitfunbufoffc0buflen;c|Somefn->letc=fnbufleninblitfunbufoffc0buflen;{cwithlen=buflen}letof_string?allocator?off?lenbuf=of_data_abstractblit_from_stringString.length?allocator?off?lenbufletof_bytes?allocator?off?lenbuf=of_data_abstractblit_from_bytesBytes.length?allocator?off?lenbufletof_hex?(off=0)?lenstr=letstr=letl=matchlenwithNone->String.lengthstr-off|Somel->linString.substrofflinletstring_fold~f~zstr=letst=refzin(String.iter(func->st:=f!stc)str;!st)inlethexdigitp=function|'a'..'f'asx->int_of_charx-87|'A'..'F'asx->int_of_charx-55|'0'..'9'asx->int_of_charx-48|x->Format.ksprintfinvalid_arg"of_hex: invalid character at pos %d: %C"pxinletwhitespace=function|' '|'\t'|'\r'|'\n'->true|_->falseinmatchstring_fold~f:(fun(cs,i,p,acc)->letp'=succpinfunction|charwhenwhitespacechar->(cs,i,p',acc)|char->matchacc,hexdigitpcharwith|(None,x)->(cs,i,p',Some(xlsl4))|(Somey,x)->set_uint8csi(xlory);(cs,succi,p',None))~z:(create_unsafe(String.lengthstrlsr1),0,0,None)strwith|_,_,_,Some_->Format.ksprintfinvalid_arg"of_hex: odd numbers of characters"|cs,i,_,_->subcs0ilethexdump_ppfmtt=letbeforefmt=function|0->()|8->Format.fprintffmt" ";|_->Format.fprintffmt" "inletafterfmt=function|15->Format.fprintffmt"@;"|_->()inFormat.pp_open_vboxfmt0;fori=0tolengtht-1doletcolumn=imod16inletc=Char.code(Bigarray.Array1.gett.buffer(t.off+i))inFormat.fprintffmt"%a%.2x%a"beforecolumncaftercolumndone;Format.pp_close_boxfmt()lethexdump=Format.printf"@\n%a@."hexdump_pplethexdump_to_bufferbuft=letf=Format.formatter_of_bufferbufinFormat.fprintff"@\n%a@."hexdump_pptletsplit?(start=0)toff=tryletheader=subtstartoffinletbody=subt(start+off)(lengtht-off-start)inheader,bodywithInvalid_argument_->err_splittstartofftype'aiter=unit->'aoptionletiterlenfnpfnt=letbody=ref(Somet)inleti=ref0infun()->match!bodywith|Somebufwhenlengthbuf=0->body:=None;None|Somebuf->beginmatchlenfnbufwith|None->body:=None;None|Someplen->incri;letp,rest=trysplitbufplenwithInvalid_argument_->err_iterbuf!ipleninbody:=Somerest;Some(pfnp)end|None->Noneletrecfoldfnextacc=matchnext()with|None->acc|Somev->foldfnext(faccv)letappendcs1cs2=letl1=lengthcs1andl2=lengthcs2inletcs=create_unsafe(l1+l2)inblitcs10cs0l1;blitcs20csl1l2;csletconcat=function|[]->create_unsafe0|[cs]->cs|css->letresult=create_unsafe(sum_lengths~caller:"Cstruct.concat"css)inletauxoffcs=letn=lengthcsinblitcs0resultoffn;off+ninignore@@List.fold_leftaux0css;resultletrevt=letn=lengthtinletout=create_unsafeninfori_src=0ton-1doletbyte=get_uint8ti_srcinleti_dst=n-1-i_srcinset_uint8outi_dstbytedone;out(* Convenience function. *)externalunsafe_blit_string_to_bigstring:string->int->buffer->int->int->unit="caml_blit_string_to_bigstring"[@@noalloc]letget{buffer;off;len;}zidx=ifzidx<0||zidx>=lentheninvalid_arg"index out of bounds";Bigarray.Array1.getbuffer(off+zidx)letget_byte{buffer;off;len;}zidx=ifzidx<0||zidx>=lentheninvalid_arg"index out of bounds";Char.code(Bigarray.Array1.getbuffer(off+zidx))letstring?(off=0)?lenstr=letstr_len=String.lengthstrinletlen=matchlenwithNone->str_len|Somelen->leninifoff<0||len<0||off+len>str_lentheninvalid_arg"index out of bounds";letbuffer=Bigarray.(Array1.createcharc_layoutstr_len)inunsafe_blit_string_to_bigstringstr0buffer0str_len;of_bigarray~off~lenbufferletbuffer?(off=0)?lenbuffer=letbuffer_len=Bigarray.Array1.dimbufferinletlen=matchlenwithNone->buffer_len-off|Somelen->leninifoff<0||len<0||off+len>buffer_lentheninvalid_arg"index out of bounds";of_bigarray~off~lenbufferletstart_pos{off;_}=offletstop_pos{off;len;_}=off+lenlethead?(rev=false)({len;_}ascs)=iflen=0thenNoneelseSome(get_charcs(ifrevthenlen-1else0))lettail?(rev=false)({buffer;off;len;}ascs)=iflen=0thencselseifrevthenof_bigarray~off~len:(len-2)bufferelseof_bigarray~off:(off+1)~len:(len-1)bufferletis_empty{len;_}=len=0letis_prefix~affix:({len=alen;_}asaffix)({len;_}ascs)=ifalen>lenthenfalseelseletmax_zidx=alen-1inletrecloopi=ifi>max_zidxthentrueelseifget_charaffixi<>get_charcsithenfalseelseloop(succi)inloop0letis_infix~affix:({len=alen;_}asaffix)({len;_}ascs)=ifalen>lenthenfalseelseletmax_zidx_a=alen-1inletmax_zidx_s=len-aleninletrecloopik=ifi>max_zidx_sthenfalseelseifk>max_zidx_athentrueelseifk>0thenifget_charaffixk=get_charcs(i+k)thenloopi(succk)elseloop(succi)0elseifget_charaffix0=get_charcsithenloopi1elseloop(succi)0inloop00letis_suffix~affix:({len=alen;_}asaffix)({len;_}ascs)=ifalen>lenthenfalseelseletmax_zidx=alen-1inletmax_zidx_a=alen-1inletmax_zidx_s=len-1inletrecloopi=ifi>max_zidxthentrueelseifget_charaffix(max_zidx_a-i)<>get_charcs(max_zidx_s-i)thenfalseelseloop(succi)inloop0letfor_allsatcs=letrecgoacci=ifi<lengthcsthengo(sat(get_charcsi)&&acc)(succi)elseaccingotrue0letexistssatcs=letrecgoacci=ifi<lengthcsthengo(sat(get_charcsi)||acc)(succi)elseaccingofalse0letstart{buffer;off;_}=of_bigarraybuffer~off~len:0letstop{buffer;off;len;}=of_bigarraybuffer~off:(off+len)~len:0letis_white=function' '|'\t'..'\r'->true|_->falselettrim?(drop=is_white)({buffer;off;len;}ascs)=iflen=0thencselseletmax_zpos=leninletmax_zidx=len-1inletrecleft_posi=ifi>max_zidxthenmax_zposelseifdrop(get_charcsi)thenleft_pos(succi)elseiinletrecright_posi=ifi<0then0elseifdrop(get_charcsi)thenright_pos(predi)elsesucciinletleft=left_pos0inifleft=max_zposthenof_bigarraybuffer~off:((off*2+len)/2)~len:0elseletright=right_posmax_zidxinifleft=0&&right=max_zposthencselseof_bigarraybuffer~off:(off+left)~len:(right-left)letfspan~min~max~sat({buffer=v;off;len;}ascs)=ifmin<0theninvalid_arg"span: negative min";ifmax<0theninvalid_arg"span: negative max";ifmin>max||max=0then(buffer~off:off~len:0v,cs)elseletmax_zidx=len-1inletmax_zidx=letk=max-1inifk>max_zidx||k<0thenmax_zidxelsekinletneed_zidx=mininletrecloopi=ifi<=max_zidx&&sat(get_charcsi)thenloop(i+1)elseifi<need_zidx||i=0thenbuffer~off:off~len:0v,cselseifi=lenthen(cs,buffer~off:(off+len)~len:0v)elsebuffer~off:off~len:iv,buffer~off:(off+i)~len:(len-i)vinloop0letrspan~min~max~sat({buffer=v;off;len;}ascs)=ifmin<0theninvalid_arg"span: negative min";ifmax<0theninvalid_arg"span: negative max";ifmin>max||max=0then(cs,buffer~off:(off+len)~len:0v)elseletmax_zidx=len-1inletmin_zidx=letk=len-maxinifk<0then0elsekinletneed_zidx=len-min-1inletrecloopi=ifi>=min_zidx&&sat(get_charcsi)thenloop(i-1)elseifi>need_zidx||i=max_zidxthen(cs,buffer~off:(off+len)~len:0v)elseifi<0then(buffer~off:off~len:0v,cs)else(buffer~off:off~len:(i+1)v,buffer~off:(off+i+1)~len:(len-(i+1))v)inloopmax_zidxletspan?(rev=false)?(min=0)?(max=max_int)?(sat=fun_->true)cs=matchrevwith|true->rspan~min~max~satcs|false->fspan~min~max~satcslettake?(rev=false)?min?max?satcs=(ifrevthensndelsefst)@@span~rev?min?max?satcsletdrop?(rev=false)?min?max?satcs=(ifrevthenfstelsesnd)@@span~rev?min?max?satcsletfcut~sep:({len=sep_len;_}assep)({buffer=v;off;len;}ascs)=ifsep_len=0theninvalid_arg"cut: empty separator";letmax_sep_zidx=sep_len-1inletmax_s_zidx=len-sep_leninletreccheck_sepik=ifk>max_sep_zidxthenSome(buffer~off:off~len:iv,buffer~off:(off+i+sep_len)~len:(len-i-sep_len)v)elseifget_charcs(i+k)=get_charsepkthencheck_sepi(k+1)elsescan(i+1)andscani=ifi>max_s_zidxthenNoneelseifget_charcsi=get_charsep0thencheck_sepi1elsescan(i+1)inscan0letrcut~sep:({len=sep_len;_}assep)({buffer=v;off;len;}ascs)=ifsep_len=0theninvalid_arg"cut: empty separator";letmax_sep_zidx=sep_len-1inletmax_s_zidx=len-1inletreccheck_sepik=ifk>max_sep_zidxthenSome(buffer~off:off~len:iv,buffer~off:(off+i+sep_len)~len:(len-i-sep_len)v)elseifget_charcs(i+k)=get_charsepkthencheck_sepi(k+1)elserscan(i-1)andrscani=ifi<0thenNoneelseifget_charcsi=get_charsep0thencheck_sepi1elserscan(i-1)inrscan(max_s_zidx-max_sep_zidx)letcut?(rev=false)~sepcs=matchrevwith|true->rcut~sepcs|false->fcut~sepcsletadd_sub~no_emptybuf~off~lenacc=iflen=0then(ifno_emptythenaccelsebuffer~off~lenbuf::acc)elsebuffer~off~lenbuf::accletfcuts~no_empty~sep:({len=sep_len;_}assep)({buffer;off;len;}ascs)=ifsep_len=0theninvalid_arg"cuts: empty separator";letmax_sep_zidx=sep_len-1inletmax_s_zidx=len-sep_leninletreccheck_sepzanchorikacc=ifk>max_sep_zidxthenletnew_start=i+sep_leninscannew_startnew_start(add_sub~no_emptybuffer~off:(off+zanchor)~len:(i-zanchor)acc)elseifget_charcs(i+k)=get_charsepkthencheck_sepzanchori(k+1)accelsescanzanchor(i+1)accandscanzanchoriacc=ifi>max_s_zidxthenifzanchor=0then(ifno_empty&&len=0then[]else[cs])elseList.rev(add_sub~no_emptybuffer~off:(off+zanchor)~len:(len-zanchor)acc)elseifget_charcsi=get_charsep0thencheck_sepzanchori1accelsescanzanchor(i+1)accinscan00[]letrcuts~no_empty~sep:({len=sep_len;_}assep)({buffer;len;_}ascs)=ifsep_len=0theninvalid_arg"cuts: empty separator";lets_len=leninletmax_sep_zidx=sep_len-1inletmax_s_zidx=len-1inletreccheck_sepzanchorikacc=ifk>max_sep_zidxthenletoff=i+sep_leninrscani(i-sep_len)(add_sub~no_emptybuffer~off~len:(zanchor-off)acc)elseifget_charcs(i+k)=get_charcskthencheck_sepzanchori(k+1)accelserscanzanchor(i-1)accandrscanzanchoriacc=ifi<0thenifzanchor=s_lenthen(ifno_empty&&s_len=0then[]else[cs])elseadd_sub~no_emptybuffer~off:0~len:zanchoraccelseifget_charcsi=get_charsep0thencheck_sepzanchori1accelserscanzanchor(i-1)accinrscans_len(max_s_zidx-max_sep_zidx)[]letcuts?(rev=false)?(empty=true)~sepcs=matchrevwith|true->rcuts~no_empty:(notempty)~sepcs|false->fcuts~no_empty:(notempty)~sepcsletfields?(empty=false)?(is_sep=is_white)({buffer;off;len;}ascs)=letno_empty=notemptyinletmax_pos=leninletrecloopiend_posacc=ifi<0thenbeginifend_pos=lenthen(ifno_empty&&len=0then[]else[cs])elseadd_sub~no_emptybuffer~off:off~len:(end_pos-(i+1))accendelsebeginifnot(is_sep(get_charcsi))thenloop(i-1)end_posaccelseloop(i-1)i(add_sub~no_emptybuffer~off:(off+i+1)~len:(end_pos-(i+1))acc)endinloop(max_pos-1)max_pos[]letffindsat({buffer=v;len;_}ascs)=letmax_idx=len-1inletrecloopi=ifi>max_idxthenNoneelseifsat(get_charcsi)thenSome(buffer~off:i~len:1v)elseloop(i+1)inloop0letrfindsat({buffer=v;len;_}ascs)=letrecloopi=ifi<0thenNoneelseifsat(get_charcsi)thenSome(buffer~off:i~len:1v)elseloop(i-1)inloop(len-1)letfind?(rev=false)satcs=matchrevwith|true->rfindsatcs|false->ffindsatcsletffind_sub~sub:({len=sub_len;_}assub)({buffer=v;off;len;}ascs)=ifsub_len>lenthenNoneelseletmax_zidx_sub=sub_len-1inletmax_zidx_s=len-sub_leninletrecloopik=ifi>max_zidx_sthenNoneelseifk>max_zidx_subthenSome(bufferv~off:(off+i)~len:sub_len)elseifk>0then(ifget_charsubk=get_charcs(i+k)thenloopi(k+1)elseloop(i+1)0)elseifget_charsub0=get_charcsithenloopi1elseloop(i+1)0inloop00letrfind_sub~sub:({len=sub_len;_}assub)({buffer=v;len;_}ascs)=ifsub_len>lenthenNoneelseletmax_zidx_sub=sub_len-1inletrecloopik=ifi<0thenNoneelseifk>max_zidx_subthenSome(bufferv~off:i~len:sub_len)elseifk>0then(ifget_charsubk=get_charcs(i+k)thenloopi(k+1)elseloop(i-1)0)elseifget_charsub0=get_charcsithenloopi1elseloop(i-1)0inloop(len-sub_len)0letfind_sub?(rev=false)~subcs=matchrevwith|true->rfind_sub~subcs|false->ffind_sub~subcsletfiltersat({len;_}ascs)=iflen=0thenemptyelseletb=createleninletmax_zidx=len-1inletrecloopbki=ifi>max_zidxthen(ifk=lenthenbelsesubb0k)elseletchr=get_charcsiinifsatchrthen(set_charbkchr;loopb(k+1)(i+1))elseloopbk(i+1)inloopb00letfilter_mapf({len;_}ascs)=iflen=0thenemptyelseletb=createleninletmax_zidx=len-1inletrecloopbki=ifi>max_zidxthen(ifk=lenthenbelsesubb0k)elsematchf(get_charcsi)with|Somechr->set_charbichr;loopb(k+1)(i+1)|None->loopbk(i+1)inloopb00letmapf({len;_}ascs)=iflen=0thenemptyelseletb=createleninfori=0tolen-1doset_charbi(f(get_charcsi))done;bletmapif({len;_}ascs)=iflen=0thenemptyelseletb=createleninfori=0tolen-1doset_charbi(fi(get_charcsi))done;b