123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)openBinary_error_typesletraisee=raise(Read_errore)typestate={buffer:string;mutableoffset:int;mutableremaining_bytes:int;mutableallowed_bytes:intoption;}letcheck_allowed_bytesstatesize=matchstate.allowed_byteswith|Somelenwhenlen<size->raiseSize_limit_exceeded|Somelen->Some(len-size)|None->Noneletcheck_remaining_bytesstatesize=ifstate.remaining_bytes<sizethenraiseNot_enough_data;state.remaining_bytes-sizeletread_atomsizeconvstate=letoffset=state.offsetinstate.remaining_bytes<-check_remaining_bytesstatesize;state.allowed_bytes<-check_allowed_bytesstatesize;state.offset<-state.offset+size;convstate.bufferoffset(** Reader for all the atomic types. *)moduleAtom=structletuint8=read_atomBinary_size.uint8TzEndian.get_uint8_stringletuint16=read_atomBinary_size.int16TzEndian.get_uint16_stringletint8=read_atomBinary_size.int8TzEndian.get_int8_stringletint16=read_atomBinary_size.int16TzEndian.get_int16_stringletint32=read_atomBinary_size.int32TzEndian.get_int32_stringletint64=read_atomBinary_size.int64TzEndian.get_int64_stringletfloat=read_atomBinary_size.floatTzEndian.get_double_stringletboolstate=int8state<>0letuint30=read_atomBinary_size.uint30@@funbufferofs->letv=Int32.to_int(TzEndian.get_int32_stringbufferofs)inifv<0thenraise(Invalid_int{min=0;v;max=(1lsl30)-1});vletint31=read_atomBinary_size.int31@@funbufferofs->Int32.to_int(TzEndian.get_int32_stringbufferofs)letint=function|`Int31->int31|`Int16->int16|`Int8->int8|`Uint30->uint30|`Uint16->uint16|`Uint8->uint8letranged_int~minimum~maximumstate=letread_int=matchBinary_size.range_to_size~minimum~maximumwith|`Int8->int8|`Int16->int16|`Int31->int31|`Uint8->uint8|`Uint16->uint16|`Uint30->uint30inletranged=read_intstateinletranged=ifminimum>0thenranged+minimumelserangedinifnot(minimum<=ranged&&ranged<=maximum)thenraise(Invalid_int{min=minimum;v=ranged;max=maximum});rangedletranged_float~minimum~maximumstate=letranged=floatstateinifnot(minimum<=ranged&&ranged<=maximum)thenraise(Invalid_float{min=minimum;v=ranged;max=maximum});rangedletrecread_zresvaluebit_in_valuestate=letbyte=uint8stateinletvalue=valuelor((byteland0x7F)lslbit_in_value)inletbit_in_value=bit_in_value+7inlet(bit_in_value,value)=ifbit_in_value<8then(bit_in_value,value)else(Buffer.add_charres(Char.unsafe_chr(valueland0xFF));(bit_in_value-8,valuelsr8))inifbyteland0x80=0x80thenread_zresvaluebit_in_valuestateelse(ifbit_in_value>0thenBuffer.add_charres(Char.unsafe_chrvalue);ifbyte=0x00thenraiseTrailing_zero;Z.of_bits(Buffer.contentsres))letnstate=letfirst=uint8stateinletfirst_value=firstland0x7Finiffirstland0x80=0x80thenread_z(Buffer.create100)first_value7stateelseZ.of_intfirst_valueletzstate=letfirst=uint8stateinletfirst_value=firstland0x3Finletsign=firstland0x40<>0iniffirstland0x80=0x80thenletn=read_z(Buffer.create100)first_value6stateinifsignthenZ.negnelsenelseletn=Z.of_intfirst_valueinifsignthenZ.negnelsenletstring_enumarrstate=letread_index=matchBinary_size.enum_sizearrwith|`Uint8->uint8|`Uint16->uint16|`Uint30->uint30inletindex=read_indexstateinifindex>=Array.lengtharrthenraiseNo_case_matched;arr.(index)letfixed_length_byteslength=read_atomlength@@funbufofs->Bytes.unsafe_of_string@@String.subbufofslengthletfixed_length_stringlength=read_atomlength@@funbufofs->String.subbufofslengthlettag=function`Uint8->uint8|`Uint16->uint16end(** Main recursive reading function, in continuation passing style. *)letrecread_rec:typeret.retEncoding.t->state->ret=funestate->letopenEncodinginmatche.encodingwith|Null->()|Empty->()|Constant_->()|Ignore->()|Bool->Atom.boolstate|Int8->Atom.int8state|Uint8->Atom.uint8state|Int16->Atom.int16state|Uint16->Atom.uint16state|Int31->Atom.int31state|Int32->Atom.int32state|Int64->Atom.int64state|N->Atom.nstate|Z->Atom.zstate|Float->Atom.floatstate|Bytes(`Fixedn)->Atom.fixed_length_bytesnstate|Bytes`Variable->Atom.fixed_length_bytesstate.remaining_bytesstate|String(`Fixedn)->Atom.fixed_length_stringnstate|String`Variable->Atom.fixed_length_stringstate.remaining_bytesstate|Padded(e,n)->letv=read_recestateinignore(Atom.fixed_length_stringnstate:string);v|RangedInt{minimum;maximum}->Atom.ranged_int~minimum~maximumstate|RangedFloat{minimum;maximum}->Atom.ranged_float~minimum~maximumstate|String_enum(_,arr)->Atom.string_enumarrstate|Array(max_length,e)->letmax_length=matchmax_lengthwithSomel->l|None->max_intinletl=read_listArray_too_longmax_lengthestateinArray.of_listl|List(max_length,e)->letmax_length=matchmax_lengthwithSomel->l|None->max_intinread_listList_too_longmax_lengthestate|Obj(Req{encoding=e;_})->read_recestate|Obj(Dft{encoding=e;_})->read_recestate|Obj(Opt{kind=`Dynamic;encoding=e;_})->letpresent=Atom.boolstateinifnotpresentthenNoneelseSome(read_recestate)|Obj(Opt{kind=`Variable;encoding=e;_})->ifstate.remaining_bytes=0thenNoneelseSome(read_recestate)|Objs{kind=`Fixedsz;left;right}->ignore(check_remaining_bytesstatesz:int);ignore(check_allowed_bytesstatesz:intoption);letleft=read_recleftstateinletright=read_recrightstatein(left,right)|Objs{kind=`Dynamic;left;right}->letleft=read_recleftstateinletright=read_recrightstatein(left,right)|Objs{kind=`Variable;left;right}->read_variable_pairleftrightstate|Tupe->read_recestate|Tups{kind=`Fixedsz;left;right}->ignore(check_remaining_bytesstatesz:int);ignore(check_allowed_bytesstatesz:intoption);letleft=read_recleftstateinletright=read_recrightstatein(left,right)|Tups{kind=`Dynamic;left;right}->letleft=read_recleftstateinletright=read_recrightstatein(left,right)|Tups{kind=`Variable;left;right}->read_variable_pairleftrightstate|Conv{inj;encoding;_}->inj(read_recencodingstate)|Union{tag_size;tagged_cases;_}->letctag=Atom.tagtag_sizestateinifctag>=Array.lengthtagged_casesthenraise(Unexpected_tagctag);let(Case{inj;encoding;_}ascase)=tagged_cases.(ctag)inifis_undefined_casecasethenraise(Unexpected_tagctag)elseinj(read_recencodingstate)|Dynamic_size{kind;encoding=e}->letsz=Atom.intkindstateinletremaining=check_remaining_bytesstateszinstate.remaining_bytes<-sz;ignore(check_allowed_bytesstatesz:intoption);letv=read_recestateinifstate.remaining_bytes<>0thenraiseExtra_bytes;state.remaining_bytes<-remaining;v|Check_size{limit;encoding=e}->letold_allowed_bytes=state.allowed_bytesinletlimit=matchstate.allowed_byteswith|None->limit|Somecurrent_limit->mincurrent_limitlimitinstate.allowed_bytes<-Somelimit;letv=read_recestateinletallowed_bytes=matchold_allowed_byteswith|None->None|Someold_limit->letremaining=matchstate.allowed_byteswith|None->assertfalse|Someremaining->remaininginletread=limit-remaininginSome(old_limit-read)instate.allowed_bytes<-allowed_bytes;v|Describe{encoding=e;_}->read_recestate|Splitted{encoding=e;_}->read_recestate|Mu{fix;_}->read_rec(fixe)state|Delayedf->read_rec(f())stateandread_variable_pair:typeleftright.leftEncoding.t->rightEncoding.t->state->left*right=fune1e2state->match(Encoding.classifye1,Encoding.classifye2)with|((`Dynamic|`Fixed_),`Variable)->letleft=read_rece1stateinletright=read_rece2statein(left,right)|(`Variable,`Fixedn)->ifn>state.remaining_bytesthenraiseNot_enough_data;state.remaining_bytes<-state.remaining_bytes-n;letleft=read_rece1stateinassert(state.remaining_bytes=0);state.remaining_bytes<-n;letright=read_rece2stateinassert(state.remaining_bytes=0);(left,right)|_->assertfalse(* Should be rejected by [Encoding.Kind.combine] *)andread_list:typea.read_error->int->aEncoding.t->state->alist=funerrormax_lengthestate->letrecloopmax_lengthacc=ifstate.remaining_bytes=0thenList.revaccelseifmax_length=0thenraiseerrorelseletv=read_recestateinloop(max_length-1)(v::acc)inloopmax_length[](** ******************** *)(** Various entry points *)letread_exnencodingbufferofslen=letstate={buffer;offset=ofs;remaining_bytes=len;allowed_bytes=None}inletv=read_recencodingstatein(state.offset,v)letreadencodingbufferofslen=tryOk(read_exnencodingbufferofslen)withRead_errorerr->Errorerrletread_optencodingbufferofslen=trySome(read_exnencodingbufferofslen)withRead_error_->Noneletof_string_exnencodingbuffer=letlen=String.lengthbufferinletstate={buffer;offset=0;remaining_bytes=len;allowed_bytes=None}inletv=read_recencodingstateinifstate.offset<>lenthenraiseExtra_bytes;vletof_stringencodingbuffer=tryOk(of_string_exnencodingbuffer)withRead_errorerr->Errorerrletof_string_optencodingbuffer=trySome(of_string_exnencodingbuffer)withRead_error_->Noneletof_bytes_exnencodingbuffer=of_string_exnencoding(Bytes.unsafe_to_stringbuffer)letof_bytesencodingbuffer=of_stringencoding(Bytes.unsafe_to_stringbuffer)letof_bytes_optencodingbuffer=of_string_optencoding(Bytes.unsafe_to_stringbuffer)