123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017 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.
*)moduleC=Piqi_commonopenCopenPiqobj_commonmoduleW=Piqi_protobuf(* whether to generate piqi-any in external mode, i.e. only including fields
* defined for it by piqi.piqi (and not piqi-impl.piqi)
*)letis_external_mode=reffalse(* providing special handling for boxed objects, since they are not
* actual references and can not be uniquely identified. Moreover they can
* mask integers which are used for enumerating objects *)letreferobj=letcount=Piqloc.next_ocount()inifnot(Obj.is_int(Obj.reprobj))thenPiqloc.addrefobjcountelse()letreferencefcodex=referx;fcodex(* XXX: move to Piqi_protobuf? *)letgen_int?wire_typecodex=letwire_type=W.get_wire_type`intwire_typeinletgen_f=matchwire_typewith|`varint->Piqirun.int64_to_varint|`zigzag_varint->Piqirun.int64_to_zigzag_varint|`fixed32->Piqirun.int64_to_fixed32|`fixed64->Piqirun.int64_to_fixed64|`signed_varint->Piqirun.int64_to_signed_varint|`signed_fixed32->Piqirun.int64_to_signed_fixed32|`signed_fixed64->Piqirun.int64_to_signed_fixed64|`block->assertfalse(* XXX *)ingen_fcodexletgen_packed_int?wire_typex=letwire_type=W.get_wire_type`intwire_typeinletgen_f=matchwire_typewith|`varint->Piqirun.int64_to_packed_varint|`zigzag_varint->Piqirun.int64_to_packed_zigzag_varint|`fixed32->Piqirun.int64_to_packed_fixed32|`fixed64->Piqirun.int64_to_packed_fixed64|`signed_varint->Piqirun.int64_to_packed_signed_varint|`signed_fixed32->Piqirun.int64_to_packed_signed_fixed32|`signed_fixed64->Piqirun.int64_to_packed_signed_fixed64|`block->assertfalse(* XXX *)ingen_fxletgen_float?wire_typecodex=letwire_type=W.get_wire_type`floatwire_typeinletgen_f=matchwire_typewith|`fixed32->Piqirun.float_to_fixed32|`fixed64->Piqirun.float_to_fixed64|_->assertfalse(* XXX *)ingen_fcodexletgen_packed_float?wire_typex=letwire_type=W.get_wire_type`floatwire_typeinletgen_f=matchwire_typewith|`fixed32->Piqirun.float_to_packed_fixed32|`fixed64->Piqirun.float_to_packed_fixed64|_->assertfalse(* XXX *)ingen_fxletgen_bool=Piqirun.gen_bool_fieldletgen_packed_bool=Piqirun.bool_to_packed_varintletgen_string=Piqirun.gen_string_fieldletgen_int?wire_typecodex=referx;gen_int?wire_typecodexletgen_float?wire_typecodex=referx;gen_float?wire_typecodexletgen_bool=referencegen_boolletgen_string=referencegen_stringletcompare_field_typeab=matcha.T.Field.code,b.T.Field.codewith|Somea,Someb->Int32.to_int(Int32.subab)|_->assertfalseletcompare_fieldab=letopenFincompare_field_typea.tb.t(* preorder fields by their codes *)letorder_fields=List.sortcompare_field(*
let rec unalias (x:Piqobj.obj) =
match x with
| `alias x -> unalias x.A.obj
| x -> x
*)letrecgen_objcode(x:Piqobj.obj)=matchxwith(* built-in types *)|`intx|`uintx->gen_intcodex|`floatx->gen_floatcodex|`boolx->gen_boolcodex|`stringx->gen_stringcodex|`binaryx->gen_stringcodex|`anyx->gen_anycodex(* custom types *)|`recordx->referencegen_recordcodex|`variantx->referencegen_variantcodex|`enumx->referencegen_enumcodex|`listx->referencegen_listcodex|`aliasx->gen_aliascodexandgen_packed_obj(x:Piqobj.obj)=matchxwith(* built-in types *)|`intx|`uintx->gen_packed_intx|`floatx->gen_packed_floatx|`boolx->gen_packed_boolx|`enumx->gen_packed_enumx|`aliasx->gen_packed_aliasx|_->assertfalse(* other objects can't be packed *)(* generate obj without leading code/tag element *)andgen_binobjx=Piqirun.gen_binobjgen_objx(* generate "Piqi_piqi.any" record from Piqobj.any *)andgen_anycodex=letopenAnyinletpiqi_any=ifnot!is_external_modethen(* in internal mode, passing a reference to intermediate Any
* prepresentation registered using Piqi_objstore *)letres=T.Any.({(T.default_any())withref=Some(Piqobj.put_anyx);})inPiqloc.addrefretxreselse(* in external mode, leave only fields defined by piqi.piqi: protobuf and
* typename *)lettypename=x.typenameinletprotobuf=Piqobj.pb_of_anyxin(* if protobuf is undefined, see if we have untyped JSON or XML *)(* XXX, TODO: use unindented JSON and XML to preserve space *)letjson=ifprotobuf<>NonethenNoneelsematchPiqobj.json_of_anyxwith|None->None|Somejson_ast->lets=!Piqobj.string_of_jsonjson_astinSomesinletxml=ifprotobuf<>None||json<>NonethenNoneelsematchPiqobj.xml_of_anyxwith|None->None|Somexml_elems->lets=!Piqobj.string_of_xml(`Elem("value",xml_elems))inSomesinletpiq=ifprotobuf<>None||json<>None||xml<>NonethenNoneelsematchPiqobj.piq_of_anyxwith|None->None|Somepiq_ast->lets=!Piqobj.string_of_piqpiq_astinSomesinT.Any.({(T.default_any())withtypename=typename;protobuf=protobuf;json=json;xml=xml;piq=piq;})inT.gen__anycodepiqi_anyandgen_recordcodex=letopenRin(* TODO, XXX: doing ordering at every generation step is inefficient *)letfields=order_fieldsx.fieldinletencoded_piq_unparsed=matchx.unparsed_piq_fields_refwith|Somexwhennot!is_external_mode->letobj=Int64.of_intxin(* making Piqloc happy by adding a fake reference *)Piqloc.add_fake_locobj~label:"_unparsed_piq_fields_ref";letencoded_x=gen_int1obj~wire_type:`varintin[encoded_x]|_->[]inletencoded_fields=encoded_piq_unparsed@(gen_fieldsfields)inPiqirun.gen_recordcodeencoded_fieldsandgen_fieldsfields=(* check if there's at least one packed field
* TODO: optimize by keeping track of it statically at the record level *)ifList.existsis_packed_fieldfieldsthengroup_gen_fieldsfieldselseList.mapgen_fieldfieldsandis_packed_fieldx=x.F.t.T.Field.protobuf_packed(* generate fields but first group packed repeated fields together because they
* are represented differently on the wire *)andgroup_gen_fieldsfields=letrecauxaccul=matchlwith|[]->List.revaccu|h::t->letres,t=ifis_packed_fieldhthengen_packed_fieldshtelsegen_fieldh,tinaux(res::accu)tinaux[]fields(* group repeated packed fields that have the same type as the first one,
* generate their wire representation and return when we encounter some other
* field that doesn't belong to this repeated group *)andgen_packed_fieldsfirsttail=letopenFinletreturnaccurest=letcode=Int32.to_int(some_offirst.t.T.Field.code)inletres=Piqirun.gen_recordcode(List.revaccu)inres,restinletrecauxaccul=matchlwith|[]->returnaccul|h::_whenh.t!=first.t->returnaccul|h::t->aux((gen_packed_fieldh)::accu)tin(* putting `first` as a first element in our accumulator *)aux[gen_packed_fieldfirst]tailandgen_packed_fieldx=letopenFin(* NOTE: object for a repeated packed field can't be None *)gen_packed_obj(some_ofx.obj)andgen_fieldx=letopenFinletcode=Int32.to_int(some_ofx.t.T.Field.code)inmatchx.objwith|None->(* using true for encoding flags -- the same encoding as for options
* (see below) *)referx;Piqirun.gen_bool_fieldcodetrue|Someobj->gen_objcodeobjandgen_variantcodex=letopenVin(* generate a record with a single field which represents variant's option *)Piqirun.gen_recordcode[gen_optionx.option]andgen_optionx=letopenOinletcode=Int32.to_int(some_ofx.t.T.Option.code)inmatchx.objwith|None->(* using true for encoding options w/o value *)referx;Piqirun.gen_bool_fieldcodetrue|Someobj->gen_objcodeobjandgen_enumcodex=letopenEingen_enum_optioncodex.optionandgen_enum_optioncodex=letopenOinletvalue=some_ofx.t.T.Option.codeinPiqirun.int32_to_signed_varintcodevalueandgen_packed_enumx=letopenEingen_packed_enum_optionx.optionandgen_packed_enum_optionx=letopenOinletvalue=some_ofx.t.T.Option.codeinPiqirun.int32_to_packed_signed_varintvalueandgen_listcodex=letopenLinifnotx.t.T.Piqi_list.protobuf_packedthenPiqirun.gen_listgen_objcodex.objelsePiqirun.gen_packed_listgen_packed_objcodex.objandgen_alias?wire_typecodex=letopenAinletwire_type=resolve_wire_type?wire_typex.t.T.Alias.protobuf_wire_typeinmatchx.objwith|`intx|`uintx->gen_intcodex?wire_type|`floatx->gen_floatcodex?wire_type|`aliasx->gen_aliascodex?wire_type|obj->gen_objcodeobjandgen_packed_alias?wire_typex=letopenAinletwire_type=resolve_wire_type?wire_typex.t.T.Alias.protobuf_wire_typeinmatchx.objwith|`intx|`uintx->gen_packed_intx?wire_type|`floatx->gen_packed_floatx?wire_type|`aliasx->gen_packed_aliasx?wire_type|obj->gen_packed_objobjandresolve_wire_type?wire_typethis_wire_type=(* wire-type defined in this alias is overridden by wire-type from the
* upper-level definition *)ifwire_type<>Nonethenwire_typeelsethis_wire_typelet_=Piqobj.to_pb:=gen_binobj