123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541(*
* 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_compat.int8_unsigned_elt,Bigarray_compat.c_layout)Bigarray_compat.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. The deprecated
* functions add_len and set_len violate this.
*
* 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_compat.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_set_lent=err"Cstruct.set_len %a %d"pp_ttleterr_add_lent=err"Cstruct.add_len %a %d"pp_ttleterr_copyt=err"Cstruct.copy %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_compat.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_compat.Array1.subbuffer.bufferbuffer.offbuffer.lenletcreate_unsafelen=letbuffer=Bigarray_compat.(Array1.createcharc_layoutlen)in{buffer;len;off=0}letcheck_boundstlen=len>=0&&Bigarray_compat.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_compat.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}letset_lentlen=iflen<0||not(check_boundst(t.off+len))thenerr_set_lentlenelse{twithlen}letadd_lentlen=letlen=t.len+leniniflen<0||not(check_boundst(t.off+len))thenerr_add_lentlenelse{twithlen}externalunsafe_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]letcopysrcsrcofflen=iflen<0||srcoff<0||src.len-srcoff<lenthenerr_copysrcsrcofflenelseletb=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_stringbletblitsrcsrcoffdstdstofflen=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)lenletblit_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)dstdstofflenletblit_to_string=blit_to_bytesletcomparet1t2=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_compat.Array1.sett.buffer(t.off+i)(Char.unsafe_chrc)letset_chartic=ifi>=t.len||i<0thenerr_invalid_bounds"set_char"ti1elseBigarray_compat.Array1.sett.buffer(t.off+i)cletget_uint8ti=ifi>=t.len||i<0thenerr_invalid_bounds"get_uint8"ti1elseChar.code(Bigarray_compat.Array1.gett.buffer(t.off+i))letget_charti=ifi>=t.len||i<0thenerr_invalid_bounds"get_char"ti1elseBigarray_compat.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]endletlent=t.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=lenh+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=lensrcinunsafe_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=lendstinletfirst=lenhdiniffirst<=availthen(blithd0dst0first;aux(shiftdstfirst)(n+first)tl)else(blithd0dst0avail;letrest_hd=shifthdavailin(n+avail,rest_hd::tl))inauxdst0srcletto_bytest=letsz=lentinletb=Bytes.createszinunsafe_blit_bigstring_to_bytest.buffert.offb0sz;bletto_stringt=(* The following call is safe, since this is the only reference to the
freshly-created value built by [to_bytes t]. *)Bytes.unsafe_to_string(to_bytest)letof_data_abstractblitfunlenfun?allocator?(off=0)?lenbuf=letbuflen=matchlenwith|None->lenfunbuf|Somelen->leninmatchallocatorwith|None->letc=create_unsafebufleninblitfunbufoffc0buflen;c|Somefn->letc=fnbufleninblitfunbufoffc0buflen;set_lencbuflenletof_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_hexstr=letstring_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=0tolent-1doletcolumn=imod16inletc=Char.code(Bigarray_compat.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)(lent-off-start)inheader,bodywithInvalid_argument_->err_splittstartofftype'aiter=unit->'aoptionletiterlenfnpfnt=letbody=ref(Somet)inleti=ref0infun()->match!bodywith|Somebufwhenlenbuf=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=lencs1andl2=lencs2inletcs=create_unsafe(l1+l2)inblitcs10cs0l1;blitcs20csl1l2;csletconcat=function|[]->create_unsafe0|[cs]->cs|css->letresult=create_unsafe(sum_lengths~caller:"Cstruct.concat"css)inletauxoffcs=letn=lencsinblitcs0resultoffn;off+ninignore@@List.fold_leftaux0css;resultletrevt=letn=lentinletout=create_unsafeninfori_src=0ton-1doletbyte=get_uint8ti_srcinleti_dst=n-1-i_srcinset_uint8outi_dstbytedone;out