123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380(*****************************************************************************)(* *)(* 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. *)(* *)(*****************************************************************************)moduleStringMap=Map.Make(String)type'keyt='keydesc_with_path(** [desc_with_path] describes a position in the storage. It's composed
[rev_path] which is the reverse path up to the position, and [dir] the
position's [description]. [rev_path] is only useful in case of an error to
print a descriptive message. [List.rev rev_path] is a storage's path that
contains no conflict and allows the registration of a [dir]'s storage.
NB: [rev_path] indicates the position in the tree, so once the node is
added, it won't change; whereas [dir] is mutable because when more subtrees
are added this may require updating it. *)and'keydesc_with_path={rev_path:stringlist;mutabledir:'keydescription;}and'keydescription=|Empty:'keydescription|Value:{get:'key->'aoptiontzresultLwt.t;encoding:'aData_encoding.t;}->'keydescription|NamedDir:'keytStringMap.t->'keydescription|IndexedDir:{arg:'aRPC_arg.t;arg_encoding:'aData_encoding.t;list:'key->'alisttzresultLwt.t;subdir:('key*'a)t;}->'keydescriptionletrecpp:typea.Format.formatter->at->unit=funppf{dir;_}->matchdirwith|Empty->Format.fprintfppf"Empty"|Value_e->Format.fprintfppf"Value"|NamedDirmap->Format.fprintfppf"@[<v>%a@]"(Format.pp_print_listpp_item)(StringMap.bindingsmap)|IndexedDir{arg;subdir;_}->letname=Format.asprintf"<%s>"(RPC_arg.descrarg).nameinpp_itemppf(name,subdir)andpp_item:typea.Format.formatter->string*at->unit=funppf(name,desc)->Format.fprintfppf"@[<hv 2>%s@ %a@]"nameppdescletpp_rev_pathppfpath=Format.fprintfppf"[%a]"Format.(pp_print_list~pp_sep:(funppf()->pp_print_stringppf" / ")pp_print_string)(List.revpath)letrecregister_named_subcontext:typer.rt->stringlist->rt=fundescnames->match(desc.dir,names)with|_,[]->desc|Value_,_|IndexedDir_,_->Format.kasprintfinvalid_arg"Could not register a named subcontext at %a because of an existing %a."pp_rev_pathdesc.rev_pathppdesc|Empty,name::names->letsubdir={rev_path=name::desc.rev_path;dir=Empty}indesc.dir<-NamedDir(StringMap.singletonnamesubdir);register_named_subcontextsubdirnames|NamedDirmap,name::names->letsubdir=matchStringMap.findnamemapwith|Somesubdir->subdir|None->letsubdir={rev_path=name::desc.rev_path;dir=Empty}indesc.dir<-NamedDir(StringMap.addnamesubdirmap);subdirinregister_named_subcontextsubdirnamestype(_,_,_)args=|One:{rpc_arg:'aRPC_arg.t;encoding:'aData_encoding.t;compare:'a->'a->int;}->('key,'a,'key*'a)args|Pair:('key,'a,'inter_key)args*('inter_key,'b,'sub_key)args->('key,'a*'b,'sub_key)argsletrecunpack:typeabc.(a,b,c)args->c->a*b=function|One_->funx->x|Pair(l,r)->letunpack_l=unpacklinletunpack_r=unpackrinfunx->letc,d=unpack_rxinletb,a=unpack_lcin(b,(a,d))letrecpack:typeabc.(a,b,c)args->a->b->c=function|One_->funba->(b,a)|Pair(l,r)->letpack_l=packlinletpack_r=packrinfunb(a,d)->letc=pack_lbainpack_rcdletreccompare:typeabc.(a,b,c)args->b->b->int=function|One{compare;_}->compare|Pair(l,r)->(letcompare_l=comparelinletcompare_r=comparerinfun(a1,b1)(a2,b2)->matchcompare_la1a2with0->compare_rb1b2|x->x)letdestutterequall=matchlwith|[]->[]|(i,_)::l->letrecloopacci=function|[]->acc|(j,_)::l->ifequalijthenloopaccilelseloop(j::acc)jlinloop[i]illetrecregister_indexed_subcontext:typerab.rt->list:(r->alisttzresultLwt.t)->(r,a,b)args->bt=fundesc~listpath->matchpathwith|Pair(left,right)->letcompare_left=compareleftinletequal_leftxy=Compare.Int.(compare_leftxy=0)inletlist_leftr=listr>|=?funl->destutterequal_leftlinletlist_rightr=leta,k=unpackleftrinlista>|=?funl->List.mapsnd(List.filter(fun(x,_)->equal_leftxk)l)inregister_indexed_subcontext(register_indexed_subcontextdesc~list:list_leftleft)~list:list_rightright|One{rpc_arg=arg;encoding=arg_encoding;_}->(matchdesc.dirwith|Value_|NamedDir_->Format.kasprintfinvalid_arg"Could not register an indexed subcontext at %a because of an \
existing %a."pp_rev_pathdesc.rev_pathppdesc|Empty->letsubdir={rev_path=Format.sprintf"(Maybe of %s)"RPC_arg.(descrarg).name::desc.rev_path;dir=Empty;}indesc.dir<-IndexedDir{arg;arg_encoding;list;subdir};subdir|IndexedDir{arg=inner_arg;subdir;_}->(matchRPC_arg.eqarginner_argwith|None->Format.kasprintfinvalid_arg"An indexed subcontext at %a already exists but has a \
different argument: `%s` <> `%s`."pp_rev_pathdesc.rev_path(RPC_arg.descrarg).name(RPC_arg.descrinner_arg).name|SomeRPC_arg.Eq->subdir))letregister_value:typeab.at->get:(a->boptiontzresultLwt.t)->bData_encoding.t->unit=fundesc~getencoding->matchdesc.dirwith|Empty->desc.dir<-Value{get;encoding}|_->Format.kasprintfinvalid_arg"Could not register a value at %a because of an existing %a."pp_rev_pathdesc.rev_pathppdescletcreate()={rev_path=[];dir=Empty}moduletypeINDEX=sigtypetincludePath_encoding.Swithtypet:=tvalrpc_arg:tRPC_arg.tvalencoding:tData_encoding.tvalcompare:t->t->intendtype_handler=|Handler:{encoding:'aData_encoding.t;get:'key->int->'atzresultLwt.t;}->'keyhandlertype_opt_handler=|Opt_handler:{encoding:'aData_encoding.t;get:'key->int->'aoptiontzresultLwt.t;}->'keyopt_handlerletreccombine_object=function|[]->Handler{encoding=Data_encoding.unit;get=(fun__->return_unit)}|(name,Opt_handlerhandler)::fields->let(Handlerhandlers)=combine_objectfieldsinHandler{encoding=Data_encoding.merge_objsData_encoding.(obj1(optname(dynamic_sizehandler.encoding)))handlers.encoding;get=(funki->handler.getki>>=?funv1->handlers.getki>|=?funv2->(v1,v2));}typequery={depth:int}letdepth_query=letopenRPC_queryinquery(fundepth->{depth})|+field"depth"RPC_arg.uint0(funt->t.depth)|>sealletbuild_directory:typekey.keyt->keyRPC_directory.t=fundir->letrpc_dir=ref(RPC_directory.empty:keyRPC_directory.t)inletregister:typeikey.chunked:bool->(key,ikey)RPC_path.t->ikeyopt_handler->unit=fun~chunkedpath(Opt_handler{encoding;get})->letservice=RPC_service.get_service~query:depth_query~output:encodingpathinrpc_dir:=RPC_directory.opt_register~chunked!rpc_dirservice(funkq()->getk(q.depth+1))inletrecbuild_handler:typeikey.ikeyt->(key,ikey)RPC_path.t->ikeyopt_handler=fundescpath->matchdesc.dirwith|Empty->Opt_handler{encoding=Data_encoding.unit;get=(fun__->return_none)}|Value{get;encoding}->lethandler=Opt_handler{encoding;get=(funki->ifCompare.Int.(i<0)thenreturn_noneelsegetk);}inregister~chunked:truepathhandler;handler|NamedDirmap->letfields=StringMap.bindingsmapinletfields=List.map(fun(name,dir)->(name,build_handlerdirRPC_path.(path/name)))fieldsinlet(Handlerhandler)=combine_objectfieldsinlethandler=Opt_handler{encoding=handler.encoding;get=(funki->ifCompare.Int.(i<0)thenreturn_noneelsehandler.getk(i-1)>>=?funv->return_somev);}inregister~chunked:truepathhandler;handler|IndexedDir{arg;arg_encoding;list;subdir}->let(Opt_handlerhandler)=build_handlersubdirRPC_path.(path/:arg)inletencoding=letopenData_encodinginunion[case(Tag0)~title:"Leaf"(dynamic_sizearg_encoding)(functionkey,None->Somekey|_->None)(funkey->(key,None));case(Tag1)~title:"Dir"(tup2(dynamic_sizearg_encoding)(dynamic_sizehandler.encoding))(functionkey,Somevalue->Some(key,value)|_->None)(fun(key,value)->(key,Somevalue));]inletgetki=ifCompare.Int.(i<0)thenreturn_noneelseifCompare.Int.(i=0)thenreturn_some[]elselistk>>=?funkeys->List.map_es(funkey->ifCompare.Int.(i=1)thenreturn(key,None)elsehandler.get(k,key)(i-1)>|=?funvalue->(key,value))keys>>=?funvalues->return_somevaluesinlethandler=Opt_handler{encoding=Data_encoding.(list(dynamic_sizeencoding));get}inregister~chunked:truepathhandler;handlerinignore(build_handlerdirRPC_path.open_root:keyopt_handler);!rpc_dir