Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file bounded_history_repr.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 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. *)(* *)(*****************************************************************************)moduletypeNAME=sigvalname:stringendmoduletypeKEY=sigtypetvalcompare:t->t->intvalpp:Format.formatter->t->unitvalencoding:tData_encoding.tendmoduletypeVALUE=sigtypetvalequal:t->t->boolvalpp:Format.formatter->t->unitvalencoding:tData_encoding.tendmoduletypeS=sigtypettypekeytypevaluevalempty:capacity:int64->tvalencoding:tData_encoding.tvalpp:Format.formatter->t->unitvalfind:key->t->valueoptiontypeerror+=|Key_bound_to_different_valueof{key:key;existing_value:value;given_value:value;}valremember:key->value->t->ttzresultmoduleInternal_for_tests:sigvalempty:capacity:int64->next_index:int64->tvalkeys:t->keylistendendmoduleMake(Name:NAME)(Key:KEY)(Value:VALUE):Swithtypekey=Key.tandtypevalue=Value.t=structtypekey=Key.ttypevalue=Value.tmoduleInt64_map=Map.Make(Int64)moduleMap=Map.Make(Key)typet={events:valueMap.t;(** Values stored in the structure, indexes with the keys. *)sequence:keyInt64_map.t;(** An additional map from int64 indexes to keys, to be able
to remove old entries when the structure is full. *)capacity:int64;(** The max number of the entries in the structure. Once the maximum size
is reached, older entries are deleted to free space for new ones. *)next_index:int64;(** The index to use for the next entry to add in the structure. *)oldest_index:int64;(** The oldest index of the (oldest) entry that has been added to the
data structure. If the structure is empty, [oldest_index] is
equal to [next_index]. *)size:int64;(** Counts the number of entries that are stored in history. It
satisfies the invariant: `0 <= size <= capacity` *)}letencoding:tData_encoding.t=letopenData_encodinginletevents_encoding=Data_encoding.convMap.bindings(funl->Map.add_seq(List.to_seql)Map.empty)Data_encoding.(list(tup2Key.encodingValue.encoding))inletsequence_encoding=convInt64_map.bindings(List.fold_left(funm(k,v)->Int64_map.addkvm)Int64_map.empty)(list(tup2int64Key.encoding))inconv(fun{events;sequence;capacity;next_index;oldest_index;size}->(events,sequence,capacity,next_index,oldest_index,size))(fun(events,sequence,capacity,next_index,oldest_index,size)->{events;sequence;capacity;next_index;oldest_index;size})(obj6(req"events"events_encoding)(req"sequence"sequence_encoding)(req"capacity"int64)(req"next_index"int64)(req"oldest_index"int64)(req"size"int64))letppfmt{events;sequence;capacity;size;oldest_index;next_index}=Map.bindingsevents|>funbindings->Int64_map.bindingssequence|>funsequence_bindings->letpp_bindingfmt(hash,history_proof)=Format.fprintffmt"@[%a -> %a@;@]"Key.pphashValue.pphistory_proofinletpp_sequence_bindingfmt(counter,hash)=Format.fprintffmt"@[%s -> %a@;@]"(Int64.to_stringcounter)Key.pphashinFormat.fprintffmt"@[<hov 2>History:@;\
\ { capacity: %Ld;@;\
\ current size: %Ld;@;\
\ oldest index: %Ld;@;\
\ next_index : %Ld;@;\
\ bindings: %a;@;\
\ sequence: %a; }@]"capacitysizeoldest_indexnext_index(Format.pp_print_listpp_binding)bindings(Format.pp_print_listpp_sequence_binding)sequence_bindingsletempty~capacity=letnext_index=0Lin{events=Map.empty;sequence=Int64_map.empty;capacity;next_index;oldest_index=next_index;size=0L;}typeerror+=|Key_bound_to_different_valueof{key:key;existing_value:value;given_value:value;}let()=assert(not(String.equalName.name""));register_error_kind`Temporary~id:(Format.sprintf"Bounded_history_repr.%s.key_bound_to_different_value"Name.name)~title:(Name.name^": Key already bound to a different value.")~description:(Name.name^": Remember called with a key that is already bound to a different\n\
\ value.")Data_encoding.(obj3(req"key"Key.encoding)(req"existing_value"Value.encoding)(req"given_value"Value.encoding))(function|Key_bound_to_different_value{key;existing_value;given_value}->Some(key,existing_value,given_value)|_->None)(fun(key,existing_value,given_value)->Key_bound_to_different_value{key;existing_value;given_value})letrememberkeyvaluet=letopenTzresult_syntaxinifCompare.Int64.(t.capacity<=0L)thenreturntelsematchMap.findkeyt.eventswith|Somevalue'whennot(Value.equalvaluevalue')->error@@Key_bound_to_different_value{key;existing_value=value';given_value=value}|_->(letevents=Map.addkeyvaluet.eventsinletcurrent_index=t.next_indexinletnext_index=Int64.succcurrent_indexinlett={events;sequence=Int64_map.addcurrent_indexkeyt.sequence;capacity=t.capacity;next_index;oldest_index=t.oldest_index;size=Int64.succt.size;}in(* A negative size means that [t.capacity] is set to [Int64.max_int]
and that the structure is full, so adding a new entry makes the size
overflows. In this case, we remove an element in the else branch to
keep the size of the structure equal to [Int64.max_int] at most. *)ifCompare.Int64.(t.size>0L&&t.size<=t.capacity)thenreturntelseletl=t.oldest_indexinmatchInt64_map.findlt.sequencewith|None->(* If t.size > t.capacity > 0, there is necessarily
an entry whose index is t.oldest_index in [sequence]. *)assertfalse|Someh->letsequence=Int64_map.removelt.sequenceinletevents=Map.removeheventsinreturn{next_index=t.next_index;capacity=t.capacity;size=t.capacity;oldest_index=Int64.succt.oldest_index;sequence;events;})letfindkeyt=Map.find_optkeyt.eventsmoduleInternal_for_tests=structletempty~capacity~next_index={(empty~capacity)withnext_index;oldest_index=next_index}letkeys{sequence;oldest_index;_}=letl=Int64_map.bindingssequencein(* All entries with an index greater than oldest_index are well ordered.
There are put in the [lp] list. Entries with an index smaller than
oldest_index are also well ordered, but they should come after
elements in [lp]. This happens in theory when the index reaches
max_int and then overflows. *)letln,lp=List.partition_map(fun(n,h)->ifCompare.Int64.(n<oldest_index)thenLefthelseRighth)lin(* do a tail recursive concatenation lp @ ln *)List.rev_append(List.revlp)lnendend