123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135(*
* Copyright (c) 2018-2022 Tarides <contact@tarides.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)open!ImportmodulePool:sigtype('k,'v)t(** Reference-counted pool of values with corresponding keys. *)valcreate:alloc:('k->'v)->('k,'v)t(** Get an empty pool, given a function for allocating new instances from IDs. *)valtake:('k,'v)t->'k->'v(** Get an instance from the pool by its key, allocating it if necessary. *)valdrop:('k,'v)t->'k->unit(** Reduce the reference count of an element, discarding it if the reference
count drops to 0. *)end=structtype'velt={mutablerefcount:int;instance:'v}type('k,'v)t={instances:('k,'velt)Hashtbl.t;alloc:'k->'v}letcreate~alloc={instances=Hashtbl.create0;alloc}lettaketk=matchHashtbl.find_optt.instanceskwith|Someelt->elt.refcount<-succelt.refcount;elt.instance|None->letinstance=t.allockinHashtbl.addt.instancesk{instance;refcount=1};instanceletdroptk=matchHashtbl.find_optt.instanceskwith|None->failwith"Pool.drop: double free"|Some{refcount;_}whenrefcount<=0->assertfalse|Some{refcount=1;_}->Hashtbl.removet.instancesk|Someelt->elt.refcount<-predelt.refcountendmoduleMaker(K:Irmin.Hash.S)=structtypekey=K.tmoduleMake(Val:Irmin_pack.Pack_value.Swithtypehash:=K.tandtypekey:=K.t)=struct(* TODO(craigfe): We could use the keys to skip traversal of the map on
lookup. This wasn't done originally due to complications with implementing
the [clear] function, but this has since been removed. (See #1794.) *)moduleKey=Irmin.Key.Of_hash(K)moduleKMap=Map.Make(structtypet=K.tletcompare=Irmin.Type.(unstage(compareK.t))end)typehash=K.ttypekey=Key.ttypevalue=Val.ttype'at={name:string;mutablet:valueKMap.t}letindex_direct_h=Somehletindexth=Lwt.return(index_directth)letinstances=Pool.create~alloc:(funname->{name;t=KMap.empty})letvname=Lwt.return(Pool.takeinstancesname)letequal_key=Irmin.Type.(unstage(equalK.t))letcloset=[%log.debug"close"];Pool.dropinstancest.name;Lwt.return_unitletcastt=(t:>read_writet)letbatchtf=f(castt)letpp_hash=Irmin.Type.ppK.tletcheck_keykv=letk'=Val.hashvinifequal_keykk'thenOk()elseError(k,k')letfindtk=tryletv=KMap.findkt.tincheck_keykv|>Result.map(fun()->Somev)withNot_found->OkNoneletunsafe_find~check_integrity:_tk=[%log.debug"unsafe find %a"pp_hashk];findtk|>function|Okr->r|Error(k,k')->Fmt.invalid_arg"corrupted value: got %a, expecting %a"pp_hashk'pp_hashkletfindtk=[%log.debug"find %a"pp_hashk];findtk|>function|Okr->Lwt.returnr|Error(k,k')->Fmt.kstrLwt.fail_invalid_arg"corrupted value: got %a, expecting %a"pp_hashk'pp_hashkletunsafe_memtk=[%log.debug"mem %a"pp_hashk];KMap.memkt.tletmemtk=Lwt.return(unsafe_memtk)letunsafe_append~ensure_unique:_~overcommit:_tkv=[%log.debug"add -> %a"pp_hashk];t.t<-KMap.addkvt.t;kletunsafe_addtkv=Lwt.return(unsafe_append~ensure_unique:true~overcommit:truetkv)letaddtv=unsafe_addt(Val.hashv)vendend