123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460(*****************************************************************************)(* *)(* Open Source License *)(* Copyright 2014 OCamlPro *)(* Copyright (c) 2020 Nomadic Labs <contact@nomadic-labs.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. *)(* *)(*****************************************************************************)openJson_reprmoduleRepr=structtypeserialized={buffer:bytes;offset:int;length:int;array_field:bool;}anddeserialized=[`Oof(string*value)list|`Aofvaluelist|`Boolofbool|`Floatoffloat|`Stringofstring|`Null]andnode=|Deserializedofdeserialized|Serializedofserialized|Bothofdeserialized*serializedandvalue={mutablenode:node;conforming:bool;(* when lazily deserializing the root *)cache:bool;(* when lazily deserializing *)}moduleLEB=EndianBytes.LittleEndian_unsafeexceptionBson_decoding_errorofstring*bytes*intletviewroot=matchroot.nodewith|Deserializeddeserialized|Both(deserialized,_)->deserialized|Serialized({buffer;offset;length;array_field}asserialized)->letoffset=refoffsetinletlength=reflengthinleterrorfmt=Format.ksprintf(funmsg->raise(Bson_decoding_error(msg,buffer,!offset)))fmtinletboxnode={node;conforming=false;cache=root.cache}inletskipn=offset:=!offset+n;length:=!length-ninletread_float()=if!length<8thenerror"not enough data, double expected (8 bytes)";letres=LEB.get_doublebuffer!offsetinskip8;resinletread_string()=if!length<4thenerror"not enough data, string size tag expected (4 bytes)";letstrlen=Int32.to_int(LEB.get_int32buffer!offset)-1inskip4;if!length<strlenthenerror"not enough data, string expected (%d bytes)"strlen;letres=Bytes.sub_stringbuffer!offsetstrleninskipstrlen;if!length<1thenerror"not enough data, string terminator expected (0x00)";ifLEB.get_int8buffer!offset<>0x00thenerror"string terminator expected (0x00)";skip1;resinletread_bool()=if!length<1thenerror"not enough data, bool expected (1 byte)";letres=matchLEB.get_int8buffer!offsetwith|0x00->false|0x01->true|byte->error"invalid bool value (0x%02X)"byteinskip1;resinletread_field_name()=letrecfind_terminatorlen=if!length=0thenerror"not enough data, field name terminator expected (0x00)";matchLEB.get_int8buffer!offsetwith|0x00->skip(-len);len|_->skip1;find_terminator(len+1)inletfieldlen=find_terminator0inletres=Bytes.sub_stringbuffer!offsetfieldleninskip(fieldlen+1);resinletdeserialized=if!length<5thenerror"not enough data for size and terminator";letsize=Int32.to_int(LEB.get_int32buffer!offset)inifsize<>!lengththenerror"size tag inconsistent with actual data";skip4;lettag=LEB.get_int8buffer!offsetiniftag=0x00thenif!length=1then`O[]elseerror"early terminator"elseif(notroot.conforming)&&tagland0xF0=0x80then(skip1;letres=matchtagland0x0Fwith|0x01->`Float(read_float())|0x02->`String(read_string())|0x08->`Bool(read_bool())|0x0A->`Null|tag->error"unknown immediate tag (0x%02X)"taginif!length<>1thenerror"not enough data, terminator expected (0x00)";ifLEB.get_int8buffer!offset<>0x00thenerror"terminator expected (0x00)";skip1;res)elseletrecloopacc=lettag=LEB.get_int8buffer!offsetiniftag=0x00thenif!length=1thenifarray_fieldthentryletrecto_arrayacci=function|[]->`A(List.revacc)|(name,bson)::rest->ifname=string_of_intithento_array(bson::acc)(i+1)restelseraiseExitinto_array[]0(List.revacc)withExit->error"invalid field names for array field"else`O(List.revacc)elseerror"early terminator"else(skip1;matchtagwith|0x01->letname=read_field_name()inloop((name,box(Deserialized(`Float(read_float()))))::acc)|0x02->letname=read_field_name()inloop((name,box(Deserialized(`String(read_string()))))::acc)|0x08->letname=read_field_name()inloop((name,box(Deserialized(`Bool(read_bool()))))::acc)|0x0A->letname=read_field_name()inloop((name,box(Deserialized`Null))::acc)|0x03|0x04->letname=read_field_name()inif!length<4thenerror"not enough data, subdocument size tag expected (4 \
bytes)";letdoclen=Int32.to_int(LEB.get_int32buffer!offset)inif!length<doclenthenerror"not enough data, subdocument expected (%d bytes)"doclen;letserialized={buffer;length=doclen;offset=!offset;array_field=tag=0x04;}inskipdoclen;loop((name,box(Serializedserialized))::acc)|tag->error"unknown tag (0x%02X)"tag)inloop[]inifroot.cachethenroot.node<-Both(deserialized,serialized)elseroot.node<-Deserializeddeserialized;deserializedletreprdeserialized={node=Deserializeddeserialized;conforming=false;cache=true}letto_bytes~cache~conformingroot=matchroot.nodewith|Serializedserialized|Both(_,serialized)->ifserialized.offset=0&&serialized.length=Bytes.lengthserialized.bufferthenserialized.bufferelseBytes.subserialized.bufferserialized.offsetserialized.length|Deserialized_->letreccompute_sizebson=matchbson.nodewith|Serialized{length}|Both(_,{length})->length|Deserializeddeserialized->(matchdeserializedwith|`Float_->4+1+8+1|`Stringstr->4+1+4+String.lengthstr+1+1|`Bool_->4+1+1+1|`Null->4+1+1|`Ofields->letacc=List.fold_left(funacc(name,bson)->letself=matchviewbsonwith|`Float_->8|`Stringstr->4+String.lengthstr+1|`Bool_->1|`Null->0|`O_|`A_->compute_sizebsoninacc+1+String.lengthname+1+self)0fieldsin4+acc+1|`Acells->let(acc,_)=List.fold_left(fun(acc,i)bson->letself=matchviewbsonwith|`Float_->8|`Stringstr->4+String.lengthstr+1|`Bool_->1|`Null->0|`O_|`A_->compute_sizebsoninletrecdigitsacci=ifi<=9then1+accelsedigits(1+acc)(i/10)in(acc+1+digits0i+1+self,i+1))(0,0)cellsin4+acc+1)inletcomputed_size=compute_sizerootinletresult=Bytes.createcomputed_sizeinletpos=ref0inlet(+=)ri=r:=!r+iinletreserve_size_stamp()=letoffset=!posinpos+=4;fun()->LEB.set_int8result!pos0x00;pos+=1;letsize=Int32.of_int(!pos-offset)inLEB.set_int32resultoffsetsizeinletrecserialize_toplevelconforming=function|(`Float_|`String_|`Bool_|`Null|`A_)whenconforming->raise(Invalid_argument"Json_repr.bson_to_bytes")|`Floatf->letupdate_size_stamp=reserve_size_stamp()inLEB.set_int8result!pos0x81;pos+=1;LEB.set_doubleresult!posf;pos+=8;update_size_stamp()|`Stringstr->letupdate_size_stamp=reserve_size_stamp()inLEB.set_int8result!pos0x82;pos+=1;letstrlen=String.lengthstrinLEB.set_int32result!posInt32.(of_int(strlen+1));pos+=4;Bytes.blit_stringstr0result!posstrlen;pos+=strlen;LEB.set_int8result!pos0x00;pos+=1;update_size_stamp()|`Boolb->letupdate_size_stamp=reserve_size_stamp()inLEB.set_int8result!pos0x88;pos+=1;LEB.set_int8result!pos(ifbthen0x01else0x00);pos+=1;update_size_stamp()|`Null->letupdate_size_stamp=reserve_size_stamp()inLEB.set_int8result!pos0x8A;pos+=1;update_size_stamp()|(`O_|`A_)asfields_or_cells->letfields=matchfields_or_cellswith|`Ofields->fields|`Acells->List.mapi(funiv->(string_of_inti,v))cellsinletupdate_size_stamp=reserve_size_stamp()inserialize_fieldsfields;update_size_stamp()andserialize_fieldsfields=List.iter(fun(name,bson)->LEB.set_int8result!pos(matchviewbsonwith|`Float_->0x01|`String_->0x02|`Bool_->0x08|`Null->0x0A|`O_->0x03|`A_->0x04);pos+=1;letstrlen=String.lengthnameinBytes.blit_stringname0result!posstrlen;pos+=strlen;LEB.set_int8result!pos0x00;pos+=1;matchviewbsonwith|`Floatf->LEB.set_doubleresult!posf;pos+=8|`Stringstr->letstrlen=String.lengthstrinLEB.set_int32result!posInt32.(of_int(strlen+1));pos+=4;Bytes.blit_stringstr0result!posstrlen;pos+=strlen;LEB.set_int8result!pos0x00;pos+=1|`Boolb->LEB.set_int8result!pos(ifbthen0x01else0x00);pos+=1|`Null->()|`O_|`A_->serializefalsebson)fieldsandserializeconformingbson=matchbson.nodewith|Serialized{buffer;offset;length}|Both(_,{buffer;offset;length})->Bytes.blitbufferoffsetresult!poslength;pos:=!pos+length|Deserializeddeserialized->letoffset=!posinserialize_toplevelconformingdeserialized;letlength=!pos-offsetinifcachethenletserialized=letarray_field=matchdeserializedwith`A_->true|_->falsein{buffer=result;offset;length;array_field}inbson.node<-Both(deserialized,serialized)inserializeconformingroot;resultletfrom_bytes~laziness~cache~conformingbuffer=letserialized={offset=0;length=Bytes.lengthbuffer;buffer;array_field=false}inletroot={node=Serializedserialized;conforming;cache}inletrectraversebson=matchviewbsonwith|`Ofields->List.iter(fun(_,bson)->traversebson)fields|`Acells->List.itertraversecells|`Float_|`String_|`Bool_|`Null->()inifnotlazinessthen(* a simple traversal will expand the structure as a side effect *)traverseroot;rootletrepr_uid:valueJson_repr.repr_uid=repr_uid()endtypebson=Repr.valueexceptionBson_decoding_error=Repr.Bson_decoding_errorletbson_to_bytes?(cache=true)?(conforming=false)bson=Repr.to_bytes~cache~conformingbsonletbytes_to_bson?(laziness=true)?(cache=true)?(conforming=false)~copybuffer=letbuffer=ifcopythenBytes.copybufferelsebufferinRepr.from_bytes~laziness~cache~conformingbuffermoduleJson_encoding=Json_encoding.Make(Repr)moduleJson_query=Json_query.Make(Repr)