12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2017, 2018 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)(* Runtime support for piqi/Protocol Buffers wire format encoding
*
* Encoding rules follow this specification:
*
* http://code.google.com/apis/protocolbuffers/docs/encoding.html
*)(*
* Runtime support for parsers (decoders).
*
*)exceptionErrorofint*stringletstring_of_locpos=string_of_intposletstrerrlocs=string_of_locloc^": "^sletbuf_errorlocs=(*
failwith (strerr s loc)
*)raise(Error(loc,s))leterrorobjs=letloc=-1in(* TODO, XXX: obj location db? *)buf_errorlocstypestring_slice={s:string;start_pos:int;(* position of `s` in the input stream *)len:int;mutablepos:int;}(* the below alternative tail-recursive implementation of stdlib's List.map is
* copied from Core (https://github.com/janestreet/core_kernel)
*
* note that the order of arguments was changed back to match the one of
* stdlib's
*)letlist_map_slowfl=List.rev(List.rev_mapfl)letreclist_count_mapflctr=matchlwith|[]->[]|[x1]->letf1=fx1in[f1]|[x1;x2]->letf1=fx1inletf2=fx2in[f1;f2]|[x1;x2;x3]->letf1=fx1inletf2=fx2inletf3=fx3in[f1;f2;f3]|[x1;x2;x3;x4]->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4in[f1;f2;f3;f4]|x1::x2::x3::x4::x5::tl->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4inletf5=fx5inf1::f2::f3::f4::f5::(ifctr>1000thenlist_map_slowftlelselist_count_mapftl(ctr+1))letlist_mapfl=list_count_mapfl0moduleList=structincludeListletmap=list_mapendmoduleIBuf=structtypet=|Stringofstring_slice|Channelofin_channelletof_channelx=Channelxletof_stringxstart_pos=String{s=x;len=String.lengthx;start_pos=start_pos;pos=0;}letto_stringbuf=matchbufwith|Stringx->(* XXX, TODO: try to avoid extra alloaction if the buffer holds the
* whole desired string? *)String.subx.sx.pos(x.len-x.pos)|Channelx->(* XXX: optimize using block reads? OTOH, it seems like this
* function is not supposed to be called for channels at all *)letres=Buffer.create20intrywhiletrue(* this cycle exist only on End_of_file exception *)doBuffer.add_charres(input_charx)done;""withEnd_of_file->Buffer.contentsresletposbuf=matchbufwith|Stringx->x.pos+x.start_pos|Channelx->pos_inxletsizebuf=matchbufwith|Stringx->x.len-x.pos|Channelx->(* this function should is not called for channels *)assertfalseleterrorbufs=letloc=posbufinbuf_errorlocsexceptionEnd_of_buffer(* get the next byte from the buffer and return it as an integer *)letnext_bytebuf=matchbufwith|Stringx->ifx.pos>=x.lenthenraiseEnd_of_bufferelseletres=x.s.[x.pos]inx.pos<-x.pos+1;Char.coderes|Channelx->(tryinput_bytexwithEnd_of_file->raiseEnd_of_buffer)(* get the next [length] bytes the buffer and return it as a string *)letnext_blockbuflength=matchbufwith|Stringx->ifx.pos+length>x.lenthen(* XXX: adjusting position to provide proper EOB location *)(x.pos<-x.len;raiseEnd_of_buffer)else(* NOTE: start_pos, pos and the string itself remain the same in
* the new buffer *)letres=String{xwithlen=x.pos+length}in(* skip the new buffer in the current buffer *)x.pos<-x.pos+length;res|Channelx->letstart_pos=pos_inxinlets=Bytes.createlengthin(tryStdlib.really_inputxs0lengthwithEnd_of_file->raiseEnd_of_buffer);of_string(Bytes.unsafe_to_strings)start_posletof_stringx=of_stringx0endtypet=|Varintofint|Varint64ofint64(* used if int width is not enough *)|Int32ofint32|Int64ofint64|BlockofIBuf.t|Top_blockofIBuf.t(* top-level block *)(* initializers for embedded records/variants (i.e. their contents start without
* any leading headers/delimiters/separators) *)letinit_from_channelch=Top_block(IBuf.of_channelch)letinit_from_strings=Top_block(IBuf.of_strings)leterror_variantobjcode=errorobj("unknown variant: "^string_of_intcode)leterror_missingobjcode=errorobj("missing field: "^string_of_intcode)leterror_enum_constobj=errorobj"unknown enum constant"(* TODO, XXX: issue warning on unparsed fields or change behaviour depending on
* "strict" config option ? *)letcheck_unparsed_fieldsl=()(*
List.iter (fun (code, x) -> error code "unknown field") l
*)letnext_varint_bytebuf=letx=IBuf.next_bytebufin(* msb indicating that more bytes will follow *)letmsb=xland0x80inletx=xland0x7finmsb,xletparse_varint64ibufmsbxpartial_res=letrecauximsbxres=letx=Int64.of_intxinlety=Int64.shift_leftx(i*7)inif(Int64.shift_right_logicaly(i*7))<>xthenIBuf.errorbuf"integer overflow while reading varint"elseletres=Int64.logorresyinifmsb=0thenVarint64res(* no more octets => return *)elseletmsb,x=next_varint_bytebufinaux(i+1)msbxres(* continue reading octets *)inauximsbx(Int64.of_intpartial_res)(* TODO: optimize using Sys.word_size and manual cycle unrolling *)letparse_varint_commonbufires=letrecauxires=letmsb,x=next_varint_bytebufinlety=xlsl(i*7)in(* NOTE: by using asr rather than lsr we disallow signed integers to appear
* in Varints, they will rather be returned as Varint64 *)ifyasr(i*7)<>xthen(* switch to Varint64 in case of overflow *)parse_varint64ibufmsbxreselseletres=resloryinifmsb=0thenVarintres(* no more octets => return *)elseaux(i+1)res(* continue reading octets *)intryauxireswithIBuf.End_of_buffer->IBuf.errorbuf"unexpected end of buffer while reading varint"letparse_varintbuf=parse_varint_commonbuf00lettry_parse_varintbuf=(* try to read the first byte and don't handle End_of_buffer exception *)letmsb,x=next_varint_bytebufinifmsb=0thenVarintx(* no more octets => return *)elseparse_varint_commonbuf1x(* TODO, XXX: check signed overflow *)(* TODO: optimize for little-endian architecture *)letparse_fixed32buf=tryletres=ref0linfori=0to3doletx=IBuf.next_bytebufinletx=Int32.of_intxinletx=Int32.shift_leftx(i*8)inres:=Int32.logor!resxdone;!reswithIBuf.End_of_buffer->IBuf.errorbuf"unexpected end of buffer while reading fixed32"letparse_fixed64buf=tryletres=ref0Linfori=0to7doletx=IBuf.next_bytebufinletx=Int64.of_intxinletx=Int64.shift_leftx(i*8)inres:=Int64.logor!resxdone;!reswithIBuf.End_of_buffer->IBuf.errorbuf"unexpected end of buffer while reading fixed64"lettry_parse_fixed32buf=(* try to read the first byte and don't handle End_of_buffer exception *)letb1=IBuf.next_bytebufinletres=ref(Int32.of_intb1)intryfori=1to3doletx=IBuf.next_bytebufinletx=Int32.of_intxinletx=Int32.shift_leftx(i*8)inres:=Int32.logor!resxdone;!reswithIBuf.End_of_buffer->IBuf.errorbuf"unexpected end of buffer while reading fixed32"lettry_parse_fixed64buf=(* try to read the first byte and don't handle End_of_buffer exception *)letb1=IBuf.next_bytebufinletres=ref(Int64.of_intb1)intryfori=1to7doletx=IBuf.next_bytebufinletx=Int64.of_intxinletx=Int64.shift_leftx(i*8)inres:=Int64.logor!resxdone;!reswithIBuf.End_of_buffer->IBuf.errorbuf"unexpected end of buffer while reading fixed64"letparse_blockbuf=(* XXX: is there a length limit or it is implementation specific? *)matchparse_varintbufwith|Varintlengthwhenlength>=0->(tryIBuf.next_blockbuflengthwithIBuf.End_of_buffer->errorbuf"unexpected end of block")|Varint_|Varint64_->IBuf.errorbuf"block length is too long"|_->assertfalse(* TODO: optimize using Sys.word_size *)letparse_field_headerbuf=(* the range for field codes is 1 - (2^29 - 1) which mean on 32-bit
* machine ocaml's int may not hold the full value *)matchtry_parse_varintbufwith|Varintkey->letwire_type=keyland7inletfield_code=keylsr3inwire_type,field_code|Varint64keywhenInt64.logandkey0xffff_ffff_0000_0000L<>0L->IBuf.errorbuf"field code is too big"|Varint64key->letwire_type=Int64.to_int(Int64.logandkey7L)inletfield_code=Int64.to_int(Int64.shift_right_logicalkey3)inwire_type,field_code|_->assertfalseletparse_fieldbuf=tryletwire_type,field_code=parse_field_headerbufinletfield_value=matchwire_typewith|0->parse_varintbuf|1->Int64(parse_fixed64buf)|2->Block(parse_blockbuf)|5->Int32(parse_fixed32buf)|3|4->IBuf.errorbuf"groups are not supported"|_->IBuf.errorbuf("unknown wire type "^string_of_intwire_type)inSome(field_code,field_value)withIBuf.End_of_buffer->None(* parse header of a top-level value of a primitive type (i.e. generated with a
* special "-1" code) *)letparse_toplevel_headerbuf=matchparse_fieldbufwith|None->errorbuf"unexpected end of buffer when reading top-level header"|Some(field_code,field_value)->iffield_code=1thenfield_valueelseerrorbuf"invalid top-level header for a primitive type"letrecexpect_int32=function|Int32i->i|Top_blockbuf->expect_int32(parse_toplevel_headerbuf)|obj->errorobj"fixed32 expected"letrecexpect_int64=function|Int64i->i|Top_blockbuf->expect_int64(parse_toplevel_headerbuf)|obj->errorobj"fixed64 expected"(*
* Convert Zig-zag varint to normal varint
*)letreczigzag_varint_of_varint=function|Varintx->letsign=-(xland1)inletres=(xlsr1)lxorsigninVarintres|Varint64x->letsign=Int64.neg(Int64.logandx1L)inletres=Int64.logxor(Int64.shift_right_logicalx1)signinVarint64res|Top_blockbuf->zigzag_varint_of_varint(parse_toplevel_headerbuf)|obj->errorobj"varint expected"(*
* Parsing primitive types
*)letmax_uint=matchSys.word_sizewith|32->0x0000_0000_7fff_ffffL(* on 32-bit, int is 31-bit wide *)|64->0x7fff_ffff_ffff_ffffL(* on 64-bit, int is 63-bit wide *)|_->assertfalseletint64_of_uintx=(* prevent turning into a negative value *)Int64.logand(Int64.of_intx)max_uintletint64_of_uint32x=(* prevent turning into a negative value *)Int64.logand(Int64.of_int32x)0x0000_0000_ffff_ffffL(* this encoding is only for unsigned integers *)letrecint_of_varintobj=matchobjwith|Varintx->x|Varint64x->letres=Int64.to_intxinifint64_of_uintres<>xthenerrorobj"int overflow in 'int_of_varint'";res|Top_blockbuf->int_of_varint(parse_toplevel_headerbuf)|_->errorobj"varint expected"letrecint_of_signed_varintobj=matchobjwith|Varintx->x|Varint64x->letres=Int64.to_intxinifInt64.of_intres<>xthenerrorobj"int overflow in 'int_of_signed_varint'";res|Top_blockbuf->int_of_signed_varint(parse_toplevel_headerbuf)|_->errorobj"varint expected"(* this encoding is only for signed integers *)letint_of_zigzag_varintx=int_of_signed_varint(zigzag_varint_of_varintx)letint_of_fixed32x=Int32.to_int(expect_int32x)letint_of_fixed64x=Int64.to_int(expect_int64x)(* this encoding is only for unsigned integers *)letrecint64_of_varint=function|Varintx->int64_of_uintx|Varint64x->x|Top_blockbuf->int64_of_varint(parse_toplevel_headerbuf)|obj->errorobj"varint expected"letrecint64_of_signed_varint=function|Varintx->Int64.of_intx|Varint64x->x|Top_blockbuf->int64_of_signed_varint(parse_toplevel_headerbuf)|obj->errorobj"varint expected"(* this encoding is only for signed integers *)letint64_of_zigzag_varintx=int64_of_signed_varint(zigzag_varint_of_varintx)letint64_of_fixed32x=letx=expect_int32xinint64_of_uint32xletint64_of_fixed64=expect_int64letint64_of_signed_fixed32x=Int64.of_int32(expect_int32x)letint64_of_signed_fixed64=int64_of_fixed64(* this encoding is only for unsigned integers *)letrecint32_of_varintobj=matchobjwith|Varintx->(* don't bother handling separate cases for now: which type is wider --
* int32 or int *)int32_of_varint(Varint64(int64_of_uintx))|Varint64x->letres=Int64.to_int32xinifint64_of_uint32res<>xthenerrorobj"int32 overflow in 'int32_of_varint'";res|Top_blockbuf->int32_of_varint(parse_toplevel_headerbuf)|obj->errorobj"varint expected"letrecint32_of_signed_varintobj=matchobjwith|Varintx->(* don't bother handling separate cases for now: which type is wider --
* int32 or int *)int32_of_signed_varint(Varint64(Int64.of_intx))|Varint64x->letres=Int64.to_int32xinifInt64.of_int32res<>xthenerrorobj"int32 overflow in 'int32_of_signed_varint'";res|Top_blockbuf->int32_of_signed_varint(parse_toplevel_headerbuf)|obj->errorobj"varint expected"(* this encoding is only for signed integers *)letint32_of_zigzag_varintx=int32_of_signed_varint(zigzag_varint_of_varintx)letint32_of_fixed32=expect_int32letint32_of_signed_fixed32=int32_of_fixed32letfloat_of_int32x=Int32.float_of_bitsx(* XXX *)letfloat_of_int64x=Int64.float_of_bitsx(* XXX *)letfloat_of_fixed64buf=float_of_int64(expect_int64buf)letfloat_of_fixed32buf=float_of_int32(expect_int32buf)letbool_of_varintobj=matchint_of_varintobjwith|0->false|1->true|_->errorobj"invalid boolean constant"letparse_bool_field=bool_of_varintletrecparse_binary_fieldobj=matchobjwith|Blockbuf->IBuf.to_stringbuf|Top_blockbuf->parse_binary_field(parse_toplevel_headerbuf)|obj->errorobj"block expected"letvalidate_strings=s(* XXX: validate utf8-encoded string *)letparse_string_fieldobj=validate_string(parse_binary_fieldobj)letstring_of_block=parse_string_fieldletword_of_block=parse_string_field(* word is encoded as string *)lettext_of_block=parse_string_field(* text is encoded as string *)(*
* Parsing packed fields (packed encoding is used only for primitive
* numeric types)
*)letint_of_packed_varintbuf=int_of_varint(try_parse_varintbuf)letint_of_packed_signed_varintbuf=int_of_signed_varint(try_parse_varintbuf)letint_of_packed_zigzag_varintbuf=int_of_zigzag_varint(try_parse_varintbuf)letint_of_packed_fixed32buf=Int32.to_int(try_parse_fixed32buf)letint_of_packed_fixed64buf=Int64.to_int(try_parse_fixed64buf)letint64_of_packed_varintbuf=int64_of_varint(try_parse_varintbuf)letint64_of_packed_signed_varintbuf=int64_of_signed_varint(try_parse_varintbuf)letint64_of_packed_zigzag_varintbuf=int64_of_zigzag_varint(try_parse_varintbuf)letint64_of_packed_fixed64buf=try_parse_fixed64bufletint64_of_packed_fixed32buf=letx=try_parse_fixed32bufinint64_of_uint32xletint64_of_packed_signed_fixed64=int64_of_packed_fixed64letint64_of_packed_signed_fixed32buf=Int64.of_int32(try_parse_fixed32buf)letint32_of_packed_varintbuf=int32_of_varint(try_parse_varintbuf)letint32_of_packed_signed_varintbuf=int32_of_signed_varint(try_parse_varintbuf)letint32_of_packed_zigzag_varintbuf=int32_of_zigzag_varint(try_parse_varintbuf)letint32_of_packed_fixed32buf=try_parse_fixed32bufletint32_of_packed_signed_fixed32=int32_of_packed_fixed32letfloat_of_packed_fixed32buf=float_of_int32(try_parse_fixed32buf)letfloat_of_packed_fixed64buf=float_of_int64(try_parse_fixed64buf)letbool_of_packed_varintbuf=bool_of_varint(try_parse_varintbuf)(*
* Parsing complex user-defined types
*)letparse_record_bufbuf=letrecparse_unorderedaccu=matchparse_fieldbufwith|Somefield->parse_unordered(field::accu)|None->letres=List.revaccuin(* stable-sort the obtained fields by codes: it is safe to use
* subtraction, because field codes are 29-bit integers *)List.stable_sort(fun(a,_)(b,_)->a-b)resinletrecparse_orderedaccu=matchparse_fieldbufwith|Some((code,_value)asfield)->(* check if the fields appear in order *)(matchaccuwith|(prev_code,_)::_whenprev_code>code->(* the field is out of order *)parse_unordered(field::accu)|_->parse_ordered(field::accu))|None->List.revaccuinparse_ordered[]letparse_recordobj=matchobjwith|Blockbuf|Top_blockbuf->parse_record_bufbuf|obj->errorobj"block expected"letparse_variantobj=matchparse_recordobjwith|[x]->x|[]->errorobj"empty variant"|_->errorobj"variant contains more than one option"(* find all fields with the given code in the list of fields sorted by codes *)letfind_fieldscodel=letrecauxaccuunknown_accu=function|(code',obj)::twhencode'=code->aux(obj::accu)unknown_accut|((code',_)ash)::twhencode'<code->(* skipping the field which code is less than the requested one *)auxaccu(h::unknown_accu)t|rem->List.revaccu,List.rev_appendunknown_accureminaux[][]l(* find the last instance of a field given its code in the list of fields sorted
* by codes *)letfind_fieldcodel=letrectry_find_next_fieldprev_value=function|(code',value)::twhencode'=code->(* field is found again *)try_find_next_fieldvaluet|rem->(* previous field was the last one *)Someprev_value,reminletrecfind_first_fieldunknown_accu=function|(code',value)::twhencode'=code->(* field is found *)(* check if this is the last instance of it, if not, continue iterating
* through the list *)letres,rem=try_find_next_fieldvaluetinres,List.rev_appendunknown_accurem|((code',_)ash)::twhencode'<code->(* skipping the field which code is less than the requested one *)find_first_field(h::unknown_accu)t|rem->(* not found *)None,reminmatchfind_first_field[]lwith|None,rem->(* not found => returning the original list *)None,l|res->(* found => returning found value + everything else *)resletparse_binobjparse_funbinobj=letbuf=init_from_stringbinobjinparse_funbufletparse_defaultbinobj=letbuf=init_from_stringbinobjinbuf(* XXX, NOTE: using default with required or optional-default fields *)letparse_required_fieldcodeparse_value?defaultl=letres,rem=find_fieldcodelinmatchreswith|None->(matchdefaultwith|Somex->parse_value(parse_defaultx),l|None->error_missinglcode)|Somex->parse_valuex,remletparse_optional_fieldcodeparse_value?defaultl=letres,rem=find_fieldcodelinmatchreswith|None->(matchdefaultwith|Somex->Some(parse_value(parse_defaultx)),l|None->None,l)|Somex->Some(parse_valuex),remletparse_repeated_fieldcodeparse_valuel=letres,rem=find_fieldscodelinList.mapparse_valueres,rem(* similar to List.map but store results in a newly created output array *)letmap_l2afl=letlen=List.lengthlin(* create and initialize the results array *)leta=Array.makelen(Obj.magic1)inletrecauxi=function|[]->()|h::t->a.(i)<-fh;aux(i+1)tinaux0l;aletparse_repeated_array_fieldcodeparse_valuel=letres,rem=find_fieldscodelinmap_l2aparse_valueres,remletparse_packed_fieldsparse_packed_valuebuf=letrecauxaccu=try(* try parsing another packed element *)letvalue=parse_packed_valuebufinaux(value::accu)withIBuf.End_of_buffer->(* no more packed elements *)(* NOTE: accu is returned in reversed order and will reversed to a normal
* order at a later stage in rev_flatmap *)accuinaux[]letparse_packed_fieldparse_packed_valueparse_valueobj=matchobjwith|Blockbuf->parse_packed_fieldsparse_packed_valuebuf|_->[parse_valueobj]letparse_packed_array_fieldelem_sizeparse_packed_valuebuf=letsize=IBuf.sizebufinletelem_count=size/elem_sizein(* make sure the array contains whole elements w/o any trailing fractions *)ifsizemodelem_size<>0thenIBuf.errorbuf"invalid packed fixed-width field";(* create a new array for results *)leta=Array.makeelem_count(Obj.magic1)in(* parse packed elements and store resuts in the array *)fori=0toelem_count-1doa.(i)<-parse_packed_valuebufdone;(* return the resulting array *)a(* the same as List.flatten (List.map (fun x -> List.rev (f x)) l), but more
* efficient and tail recursive *)letrev_flatmapfl=letl=List.rev_mapflinList.fold_left(funaccux->List.rev_appendxaccu)[]lletparse_packed_repeated_fieldcodeparse_packed_valueparse_valuel=letfields,rem=find_fieldscodelinletres=rev_flatmap(parse_packed_fieldparse_packed_valueparse_value)fieldsinres,remletparse_packed_repeated_array_fieldcodeparse_packed_valueparse_valuel=letres,rem=parse_packed_repeated_fieldcodeparse_packed_valueparse_valuelinArray.of_listres,remletparse_packed_repeated_array_fixed_fieldelem_sizecodeparse_packed_valueparse_valuel=letfields,rem=find_fieldscodelinmatchfieldswith|[Blockbuf]->letres=parse_packed_array_fieldelem_sizeparse_packed_valuebufinres,rem|_->(* this is the case when there are several repeated entries with the
* same code each containing packed repeated values -- need to handle
* this case, but not optimizing for it *)parse_packed_repeated_array_fieldcodeparse_packed_valueparse_valuelletparse_packed_repeated_array32_fieldcodeparse_packed_valueparse_valuel=parse_packed_repeated_array_fixed_field4codeparse_packed_valueparse_valuelletparse_packed_repeated_array64_fieldcodeparse_packed_valueparse_valuel=parse_packed_repeated_array_fixed_field8codeparse_packed_valueparse_valuelletparse_list_elemparse_value(code,x)=(* NOTE: expecting "1" as list element code *)ifcode=1thenparse_valuexelseerrorx"invalid list element code"letparse_listparse_valueobj=letl=parse_recordobjinList.map(parse_list_elemparse_value)lletparse_arrayparse_valueobj=letl=parse_recordobjinmap_l2a(parse_list_elemparse_value)lletparse_packed_list_1parse_packed_valueparse_valuefields=rev_flatmap(parse_list_elem(parse_packed_fieldparse_packed_valueparse_value))fieldsletparse_packed_listparse_packed_valueparse_valueobj=letfields=parse_recordobjinparse_packed_list_1parse_packed_valueparse_valuefieldsletparse_packed_arrayparse_packed_valueparse_valueobj=letres=parse_packed_listparse_packed_valueparse_valueobjinArray.of_listresletparse_packed_array_fixedelem_sizeparse_packed_valueparse_valueobj=letl=parse_recordobjinmatchlwith|[1,Blockbuf]->parse_packed_array_fieldelem_sizeparse_packed_valuebuf|_->(* this is the case when there are several list entries each containing
* packed repeated values -- need to handle this case, but not
* optimizing for it *)letres=parse_packed_list_1parse_packed_valueparse_valuelinArray.of_listresletparse_packed_array32parse_packed_valueparse_valueobj=parse_packed_array_fixed4parse_packed_valueparse_valueobjletparse_packed_array64parse_packed_valueparse_valueobj=parse_packed_array_fixed8parse_packed_valueparse_valueobj(*
* Runtime support for generators (encoders)
*)moduleOBuf=struct(* auxiliary iolist type and related primitives *)typet=Iosofstring|Ioloftlist|Iol_sizeofint*(tlist)(* iolist with known size *)|Iobofchar|IBufofIBuf.tletiosx=Iosxletioll=Iollletiobb=Iobb(* iolist buf output *)letto_buffer0bufl=letrecaux=function|Ioss->Buffer.add_stringbufs|Ioll|Iol_size(_,l)->List.iterauxl|Iobb->Buffer.add_charbufb|IBuf(IBuf.Stringx)->Buffer.add_substringbufx.sx.pos(x.len-x.pos)|IBuf(IBuf.Channelx)->assertfalseinauxl(* iolist output size *)letrecsize=function|Ioss->String.lengths|Ioll->List.fold_left(funaccux->accu+(sizex))0l|Iol_size(size,_)->size|Iob_->1|IBufx->IBuf.sizexletiol_sizel=letn=size(Ioll)inIol_size(n,l)letiol_known_sizenl=Iol_size(n,l)letto_stringl=letbuf=Buffer.create(sizel)into_buffer0bufl;Buffer.contentsbufletto_bufferl=letbuf=Buffer.create80into_buffer0bufl;bufletto_channelchcode=letbuf=to_buffercodeinBuffer.output_bufferchbufendopenOBufletto_string=OBuf.to_stringletto_buffer=OBuf.to_bufferletto_channel=OBuf.to_channelletiobi=(* IO char represented as Ios '_' *)iob(Char.chri)(*
* Generating varint values and fields
*)letgen_varint64_valuex=letrecauxx=letb=Int64.to_int(Int64.logandx0x7FL)in(* base 128 *)letrem=Int64.shift_right_logicalx7in(* Printf.printf "x: %LX, byte: %X, rem: %LX\n" x b rem; *)ifrem=0Lthen[iobb]elsebegin(* set msb indicating that more bytes will follow *)letb=blor0x80in(iobb)::(auxrem)endiniol(auxx)letgen_unsigned_varint_valuex=letrecauxx=letb=xland0x7Fin(* base 128 *)letrem=xlsr7inifrem=0then[iobb]elsebegin(* set msb indicating that more bytes will follow *)letb=blor0x80in(iobb)::(auxrem)endiniol(auxx)letgen_signed_varint_valuex=(* negative varints are encoded as bit-complement 64-bit varints, always
* producing 10-bytes long value *)ifx<0thengen_varint64_value(Int64.of_intx)elsegen_unsigned_varint_valuexletgen_unsigned_varint32_valuex=letrecauxx=letb=Int32.to_int(Int32.logandx0x7Fl)in(* base 128 *)letrem=Int32.shift_right_logicalx7inifrem=0lthen[iobb]elsebegin(* set msb indicating that more bytes will follow *)letb=blor0x80in(iobb)::(auxrem)endiniol(auxx)letgen_signed_varint32_valuex=(* negative varints are encoded as bit-complement 64-bit varints, always
* producing 10-bytes long value *)ifInt32.comparex0l<0(* x < 0? *)thengen_varint64_value(Int64.of_int32x)elsegen_unsigned_varint32_valuexletgen_keyktypecode=(* make sure that the field code is in the valid range *)assert(code<1lsl29&&code>=1);ifcodeland(1lsl28)<>0&&Sys.word_size==32then(* prevent an overflow of 31-bit OCaml integer on 32-bit platform *)letktype=Int32.of_intktypeinletcode=Int32.of_intcodeinletx=Int32.logorktype(Int32.shift_leftcode3)ingen_unsigned_varint32_valuexelsegen_unsigned_varint_value(ktypelor(codelsl3))(* gen key for primitive types *)letgen_primitive_keyktypecode=(* -1 is a special code meaning that values of primitive types must be
* generated with a field header with code 1: (abs (-1)) == 1
*
* This way, "-1" is treated the same as "1", leading to a uniform interface
* with generators for length-delimited types.
*
* For types which values are encoded as length-delimited blocks (i.e.
* records, variants, lists), -1 means suppress generation of a surrounding
* field header that includes the key and the length of data (see generators
* for these types below) *)gen_keyktype(abscode)letgen_signed_varint_fieldcodex=iol[gen_primitive_key0code;gen_signed_varint_valuex;]letgen_varint_fieldcodex=iol[gen_primitive_key0code;gen_unsigned_varint_valuex;]letgen_signed_varint32_fieldcodex=iol[gen_primitive_key0code;gen_signed_varint32_valuex;]letgen_varint32_fieldcodex=iol[gen_primitive_key0code;gen_unsigned_varint32_valuex;]letgen_varint64_fieldcodex=iol[gen_primitive_key0code;gen_varint64_valuex;](*
* Generating fixed32 and fixed64 values and fields
*)letgen_fixed32_valuex=(* little-endian *)lets=Bytes.create4inletx=refxinfori=0to3doletb=Char.chr(Int32.to_int(Int32.logand!x0xFFl))inBytes.setsib;x:=Int32.shift_right_logical!x8done;ios(Bytes.unsafe_to_strings)letgen_fixed64_valuex=(* little-endian *)lets=Bytes.create8inletx=refxinfori=0to7doletb=Char.chr(Int64.to_int(Int64.logand!x0xFFL))inBytes.setsib;x:=Int64.shift_right_logical!x8done;ios(Bytes.unsafe_to_strings)letgen_fixed32_fieldcodex=iol[gen_primitive_key5code;gen_fixed32_valuex;]letgen_fixed64_fieldcodex=iol[gen_primitive_key1code;gen_fixed64_valuex;](*
* Zig-zag encoding for int, int32 and int64
*)letzigzag_of_intx=(* encode signed integer using ZigZag encoding;
* NOTE: using arithmetic right shift *)(xlsl1)lxor(xasr62)(* XXX: can use lesser value than 62 on 32 bit? *)letzigzag_of_int32x=(* encode signed integer using ZigZag encoding;
* NOTE: using arithmetic right shift *)Int32.logxor(Int32.shift_leftx1)(Int32.shift_rightx31)letzigzag_of_int64x=(* encode signed integer using ZigZag encoding;
* NOTE: using arithmetic right shift *)Int64.logxor(Int64.shift_leftx1)(Int64.shift_rightx63)(*
* Public Piqi runtime functions for generating primitive types
*)letint_to_varintcodex=gen_varint_fieldcodexletint_to_signed_varintcodex=gen_signed_varint_fieldcodexletint_to_zigzag_varintcodex=gen_varint_fieldcode(zigzag_of_intx)letint64_to_varintcodex=gen_varint64_fieldcodexletint64_to_signed_varint=int64_to_varintletint64_to_zigzag_varintcodex=int64_to_varintcode(zigzag_of_int64x)letint64_to_fixed64codex=gen_fixed64_fieldcodexletint64_to_fixed32codex=gen_fixed32_fieldcode(Int64.to_int32x)letint64_to_signed_fixed64=int64_to_fixed64letint64_to_signed_fixed32=int64_to_fixed32letint32_to_varintcodex=gen_varint32_fieldcodexletint32_to_signed_varintcodex=gen_signed_varint32_fieldcodexletint32_to_zigzag_varintcodex=gen_varint32_fieldcode(zigzag_of_int32x)letint32_to_fixed32codex=gen_fixed32_fieldcodexletint32_to_signed_fixed32=int32_to_fixed32letint32_of_floatx=Int32.bits_of_floatx(* XXX *)letint64_of_floatx=Int64.bits_of_floatx(* XXX *)letfloat_to_fixed32codex=gen_fixed32_fieldcode(int32_of_floatx)letfloat_to_fixed64codex=gen_fixed64_fieldcode(int64_of_floatx)letint_of_bool=function|true->1|false->0letbool_to_varintcodex=gen_varint_fieldcode(int_of_boolx)letgen_bool_field=bool_to_varintletgen_string_fieldcodes=letcontents=iossiniol[gen_primitive_key2code;gen_unsigned_varint_value(String.lengths);contents;]letstring_to_block=gen_string_fieldletbinary_to_block=gen_string_field(* binaries use the same encoding as strings *)letword_to_block=gen_string_field(* word is encoded as string *)lettext_to_block=gen_string_field(* text is encoded as string *)(* the inverse of parse_field *)letgen_parsed_field(code,value)=matchvaluewith|Varintx->gen_varint_fieldcodex|Varint64x->gen_varint64_fieldcodex|Int32x->gen_fixed32_fieldcodex|Int64x->gen_fixed64_fieldcodex|Blockx->iol[gen_primitive_key2code;gen_unsigned_varint_value(IBuf.sizex);IBufx]|Top_blockx->(* impossible clause *)assertfalseletgen_parsed_field_listl=List.mapgen_parsed_fieldl(*
* Generating packed fields (packed encoding is used only for primitive
* numeric types)
*)letint_to_packed_varintx=gen_unsigned_varint_valuexletint_to_packed_signed_varintx=gen_signed_varint_valuexletint_to_packed_zigzag_varintx=gen_unsigned_varint_value(zigzag_of_intx)letint64_to_packed_varintx=gen_varint64_valuexletint64_to_packed_signed_varintx=gen_varint64_valuexletint64_to_packed_zigzag_varintx=gen_varint64_value(zigzag_of_int64x)letint64_to_packed_fixed64x=gen_fixed64_valuexletint64_to_packed_fixed32x=gen_fixed32_value(Int64.to_int32x)letint64_to_packed_signed_fixed64=int64_to_packed_fixed64letint64_to_packed_signed_fixed32=int64_to_packed_fixed32letint32_to_packed_varintx=gen_unsigned_varint32_valuexletint32_to_packed_signed_varintx=gen_signed_varint32_valuexletint32_to_packed_zigzag_varintx=gen_unsigned_varint32_value(zigzag_of_int32x)letint32_to_packed_fixed32x=gen_fixed32_valuexletint32_to_packed_signed_fixed32=int32_to_packed_fixed32letfloat_to_packed_fixed32x=gen_fixed32_value(int32_of_floatx)letfloat_to_packed_fixed64x=gen_fixed64_value(int64_of_floatx)letbool_to_packed_varintx=gen_unsigned_varint_value(int_of_boolx)(*
* Generating complex user-defined types
*)letgen_required_fieldcodefx=fcodexletgen_optional_fieldcodef=function|Somex->fcodex|None->iol[]letgen_repeated_fieldcodefl=iol(List.map(fcode)l)(* similar to Array.map but produces list instead of array *)letmap_a2lfa=letrecauxiaccu=ifi<0thenaccuelseletres=fa.(i)inaux(i-1)(res::accu)inaux((Array.lengtha)-1)[]letgen_repeated_array_fieldcodefl=iol(map_a2l(fcode)l)letgen_packed_repeated_field_commoncodecontents=letsize=OBuf.sizecontentsinifsize=0thencontents(* don't generate anything for empty repeated packed field *)elseiol[gen_key2code;gen_unsigned_varint_valuesize;contents;]letgen_packed_repeated_fieldcodefl=letcontents=iol_size(List.mapfl)ingen_packed_repeated_field_commoncodecontentsletgen_packed_repeated_array_fieldcodefl=letcontents=iol_size(map_a2lfl)ingen_packed_repeated_field_commoncodecontentsletgen_packed_repeated_array32_fieldcodefl=letsize=4*Array.lengthlinletcontents=iol_known_sizesize(map_a2lfl)ingen_packed_repeated_field_commoncodecontentsletgen_packed_repeated_array64_fieldcodefl=letsize=8*Array.lengthlinletcontents=iol_known_sizesize(map_a2lfl)ingen_packed_repeated_field_commoncodecontentsletgen_recordcodecontents=letcontents=iol_sizecontentsin(* special code meaning that key and length sould not be generated *)ifcode=-1thencontentselseiol[gen_key2code;(* the length of fields data *)gen_unsigned_varint_value(OBuf.sizecontents);contents;](* generate binary representation of <type>_list .proto structure *)letgen_listfcodel=(* NOTE: using "1" as list element code *)letcontents=List.map(f1)lingen_recordcodecontentsletgen_arrayfcodel=(* NOTE: using "1" as list element code *)letcontents=map_a2l(f1)lingen_recordcodecontentsletgen_packed_listfcodel=(* NOTE: using "1" as list element code *)letfield=gen_packed_repeated_field1flingen_recordcode[field]letgen_packed_arrayfcodel=letfield=gen_packed_repeated_array_field1flingen_recordcode[field]letgen_packed_array32fcodel=letfield=gen_packed_repeated_array32_field1flingen_recordcode[field]letgen_packed_array64fcodel=letfield=gen_packed_repeated_array64_field1flingen_recordcode[field]letgen_binobjgen_objx=letobuf=gen_obj(-1)xin(* return the result encoded as a binary string *)OBuf.to_stringobuf(* generate length-delimited block of data. The inverse operation to
* parse_block() below *)letgen_blockiodata=iol[gen_unsigned_varint_value(OBuf.sizeiodata);iodata;](* XXX, TODO: return Some or None on End_of_buffer *)letparse_blockbuf=Top_block(parse_blockbuf)