123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361(**************************************************************************)(* This file is part of the Codex semantics library *)(* (patricia-tree sub-component). *)(* *)(* Copyright (C) 2024-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* You can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(**************************************************************************)openIntsopenSignaturesletsdbmxy=y+(xlsl16)+(xlsl6)-x(** Combine two numbers into a new hash *)(** Simple node, with no hash consing. *)module[@inline]SimpleNode(Key:sigtype'atend)(Value:HETEROGENEOUS_VALUE)=structtype'akey='aKey.ttype('key,'map)value=('key,'map)Value.ttype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'mapt='mapviewletviewx=xletempty=Emptyletis_emptyx=x==Emptyletleafkeyvalue=Leaf{key;value}letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|Empty,x->x|x,Empty->x|_->Branch{prefix;branching_bit;tree0;tree1}endmoduleWeakNode(Key:sigtype'atend)(Value:HETEROGENEOUS_VALUE)(* :NODE *)=structtype'akey='aKey.ttype('key,'map)value=('key,'map)Value.ttype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'at=|TEmpty:'mapt|TBranch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapt(* Additional hidden case: leaf, which is an Ephemeron.K1, whose
tag is 251, so it can be discriminated against the other
cases. This avoids an indirection. *)letempty=TEmptyletis_emptyx=x==TEmptyletleafkeyvalue=Obj.magic(Ephemeron.K1.makekeyvalue)letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|TEmpty,x->x|x,TEmpty->x|_->TBranch{prefix;branching_bit;tree0;tree1}letview(typek)(typemap)(t:mapt)=letobj=Obj.reprtinifObj.is_blockobj&&Obj.tagobj!=0then(* Ephemeron.K1.get_(key|value) are no longer available in 5.0,
so we do that instead. *)letephe:Obj.Ephemeron.t=Obj.magicobjinletkey:kkeyoption=Obj.magic@@Obj.Ephemeron.get_keyephe0inletdata:(k,map)Value.toption=Obj.magic@@Obj.Ephemeron.get_dataepheinmatchkey,datawith|Somekey,Somevalue->Leaf{key;value}|_->Emptyelsematchtwith|TEmpty->Empty|TBranch{prefix;branching_bit;tree0;tree1}->Branch{prefix;branching_bit;tree0;tree1}end(** Add a unique id to nodes, e.g. so that they can be used as keys in maps or sets. *)moduleNodeWithId(Key:sigtype'atend)(Value:HETEROGENEOUS_VALUE):NODE_WITH_IDwithtype'keykey='keyKey.tandtype('key,'map)value=('key,'map)Value.t=structtype'akey='aKey.ttype('key,'map)value=('key,'map)Value.ttype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'mapt=|NEmpty:'mapt|NBranch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt;id:int}->'mapt|NLeaf:{key:'keykey;value:('key,'map)value;id:int}->'maptletview=function|NEmpty->Empty|NBranch{prefix;branching_bit;tree0;tree1;_}->Branch{prefix;branching_bit;tree0;tree1}|NLeaf{key;value;_}->Leaf{key;value}letto_int=function|NEmpty->0|NBranch{id;_}->id|NLeaf{id;_}->idletcount=ref0letempty=NEmptyletis_emptyx=x==NEmptyletleafkeyvalue=incrcount;NLeaf{key;value;id=(!count)}letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|NEmpty,x->x|x,NEmpty->x|_->incrcount;NBranch{prefix;branching_bit;tree0;tree1;id=(!count)}end(** NODE for sets, i.e. when there is no associated values. *)moduleSetNode(Key:sigtype'atend):NODEwithtype'keykey='keyKey.tandtype('key,'map)value=unit=structtype'akey='aKey.ttype('key,'map)value=unittype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'mapt=|NEmpty:'mapt|NBranch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapt|NLeaf:{key:'keykey}->'maptletview=function|NEmpty->Empty|NBranch{prefix;branching_bit;tree0;tree1}->Branch{prefix;branching_bit;tree0;tree1}|NLeaf{key}->Leaf{key;value=()}letempty=NEmptyletis_emptyx=x==NEmptyletleafkey_value=NLeaf{key}letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|NEmpty,x->x|x,NEmpty->x|_->NBranch{prefix;branching_bit;tree0;tree1}endmoduleWeakSetNode(Key:sigtype'atend)(* :NODE *)=structtype'akey='aKey.ttype('key,'map)value=unittype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'at=|TEmpty:'mapt|TBranch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapt(* Additional hidden case: leaf, which is a Weak array, whose tag
is 251, so it can be discriminated against the other
cases. This avoids an indirection. *)letempty=TEmptyletis_emptyx=x==TEmptyletleafkey()=Obj.magic(leta=Weak.create1inWeak.seta0(Somekey))letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|TEmpty,x->x|x,TEmpty->x|_->TBranch{prefix;branching_bit;tree0;tree1}letviewt=letobj=Obj.reprtinifObj.is_blockobj&&Obj.tagobj!=0thenletweak=Obj.magicobjinletkey=Weak.getweak0inmatchkeywith|Somekey->Leaf{key;value=()}|_->Emptyelsematchtwith(* Identity in memory. *)|TEmpty->Empty|TBranch{prefix;branching_bit;tree0;tree1}->Branch{prefix;branching_bit;tree0;tree1}endmoduleHashconsedNode(Key:HETEROGENEOUS_KEY)(Value:HETEROGENEOUS_HASHED_VALUE)()(* : HASH_CONSED_NODE
with type 'key key = 'key Key.t
and type ('key, 'map) value = ('key, 'map) Value.t *)=structtype'akey='aKey.ttype('key,'map)value=('key,'map)Value.ttype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:('key,'map)value}->'mapviewand'mapt=|NEmpty:'mapt|NBranch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt;id:int}->'mapt|NLeaf:{key:'keykey;value:('key,'map)Value.t;id:int}->'maptletview=function|NEmpty->Empty|NBranch{prefix;branching_bit;tree0;tree1;_}->Branch{prefix;branching_bit;tree0;tree1}|NLeaf{key;value;_}->Leaf{key;value}letto_int=function|NEmpty->0|NBranch{id;_}->id|NLeaf{id;_}->idletcount=ref1(** Start at 1 as we increment in post *)typeany_map=AnyMap:'at->any_map[@@unboxed]moduleHashArg=structtypet=any_mapletequal(AnyMapa)(AnyMapb)=matcha,bwith|NEmpty,NEmpty->true|NLeaf{key=key1;value=value1;_},NLeaf{key=key2;value=value2;_}->beginmatchKey.polyeqkey1key2with|Eq->Value.polyeqvalue1value2|Diff->falseend|NBranch{prefix=prefixa;branching_bit=branching_bita;tree0=tree0a;tree1=tree1a;_},NBranch{prefix=prefixb;branching_bit=branching_bitb;tree0=tree0b;tree1=tree1b;_}->prefixa==prefixb&&branching_bita==branching_bitb&&to_inttree0a=to_inttree0b&&to_inttree1a=to_inttree1b|_->falselethash(AnyMapx)=matchxwith|NEmpty->0|NLeaf{key;value;_}->lethash=sdbm(Key.to_intkey)(Value.hashvalue)in(hashlsl1)lor1(* All leaf hashes are odd *)|NBranch{prefix;branching_bit;tree0;tree1;_}->(* All branch hashes are even *)(sdbm((prefix:>int)lor(branching_bit:>int))@@sdbm(to_inttree0)(to_inttree1))lsl1endmoduleWeakHash=Weak.Make(HashArg)letweakh=WeakHash.create120letempty=NEmptyletis_emptyx=x==NEmptylettry_find(tentative:'at)=letAnyMapx=WeakHash.mergeweakh(AnyMaptentative)inletx:'at=Obj.magicxinifx==tentativethenincrcount;xletleafkeyvalue=try_find(NLeaf{key;value;id=!count})letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|NEmpty,x->x|x,NEmpty->x|_->try_find(NBranch{prefix;branching_bit;tree0;tree1;id=(!count)})letequalxy=x==yletcomparexy=Int.compare(to_intx)(to_inty)endmoduleHashconsedSetNode(Key:HETEROGENEOUS_KEY)():HASH_CONSED_NODEwithtype'keykey='keyKey.tandtype('key,'map)value=unit=structtype'akey='aKey.ttype('key,'map)value=unittypemap=|NEmpty:map|NBranch:{prefix:intkey;branching_bit:mask;tree0:map;tree1:map;id:int}->map|NLeaf:{key:'keykey;id:int}->maptype'mapview=|Empty:'mapview|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt}->'mapview|Leaf:{key:'keykey;value:unit}->'mapviewand_t=mapletview=function|NEmpty->Empty|NBranch{prefix;branching_bit;tree0;tree1;_}->Branch{prefix;branching_bit;tree0;tree1}|NLeaf{key;_}->Leaf{key;value=()}letto_int=function|NEmpty->0|NBranch{id;_}->id|NLeaf{id;_}->idletcount=ref1(** Start at 1 as we increment in post *)moduleHashArg=structtypet=mapletequalab=matcha,bwith|NEmpty,NEmpty->true|NLeaf{key=key1;_},NLeaf{key=key2;_}->beginmatchKey.polyeqkey1key2with|Eq->true|Diff->falseend|NBranch{prefix=prefixa;branching_bit=branching_bita;tree0=tree0a;tree1=tree1a;_},NBranch{prefix=prefixb;branching_bit=branching_bitb;tree0=tree0b;tree1=tree1b;_}->prefixa==prefixb&&branching_bita==branching_bitb&&tree0a==tree0b&&tree1a==tree1b|_->falselethasha=matchawith|NEmpty->0|NLeaf{key;_}->((Key.to_intkey)lsl1)lor1(* All leaf hashes are odd *)|NBranch{prefix;branching_bit;tree0;tree1;_}->(* All branch hashes are even *)(sdbm((prefix:>int)lor(branching_bit:>int))@@sdbm(to_inttree0)(to_inttree1))lsl1endmoduleWeakHash=Weak.Make(HashArg)letweakh=WeakHash.create120letempty=NEmptyletis_emptyx=x==NEmptylettry_findtentative=letx=WeakHash.mergeweakhtentativeinifx==tentativethenincrcount;xletleafkey()=try_find(NLeaf{key;id=!count})letbranch~prefix~branching_bit~tree0~tree1=matchtree0,tree1with|NEmpty,x->x|x,NEmpty->x|_->try_find(NBranch{prefix;branching_bit;tree0;tree1;id=(!count)})letequalxy=x==yletcomparexy=Int.compare(to_intx)(to_inty)end