123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150(*
* 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!ImportincludePack_key_intftypesafe=SAFEtypeunsafe=UNSAFEtype(_,_)unsafe_state=|Direct:{hash:'hash;offset:int63;length:int;}->('hash,safe)unsafe_state|Indexed:'hash->('hash,safe)unsafe_state|Offset:int63->('hash,unsafe)unsafe_statetype'hashstate=('hash,safe)unsafe_statetype'hasht=State:{mutablestate:('hash,_)unsafe_state}->'hashtletinspect(Statet)=matcht.statewith|Offset_->failwith"inspect unsafe Offset"|Directd->Directd|Indexedd->Indexeddletto_hash(Statet)=matcht.statewith|Directt->t.hash|Indexedh->h|Offset_->failwith"Hash unavailable"letto_offset(Statet)=matcht.statewith|Directt->Somet.offset|Offsetoffset->Someoffset|Indexed_->Noneletpromote_exn(Statet)~offset~length=matcht.statewith|Direct_->failwith"Attempted to promote a key that is already Direct"|Offset_->failwith"Attempted to promote an offset without hash"|Indexedhash->t.state<-Direct{hash;offset;length}lett:typeh.hIrmin.Type.t->htIrmin.Type.t=funhash_t->letopenIrmin.Typeinvariant"t"(fundirectindexedt->matchinspecttwith|Direct{hash;offset;length}->direct(hash,offset,length)|Indexedx1->indexedx1)|~case1"Direct"[%typ:hash*int63*int](fun(hash,offset,length)->State{state=Direct{hash;offset;length}})|~case1"Indexed"[%typ:hash](funx1->State{state=Indexedx1})|>sealvlett(typehash)(hash_t:hashIrmin.Type.t)=letmoduleHash=structtypet=hash[@@derivingirmin~equal~compare~pre_hash~encode_bin~decode_bin]letunboxed_encode_bin=Irmin.Type.(unstage(Unboxed.encode_bint))letunboxed_decode_bin=Irmin.Type.(unstage(Unboxed.decode_bint))letencoded_size=matchIrmin.Type.Size.of_valuetwith|Staticn->n|Dynamic_|Unknown->failwith"Hash must have a fixed-width binary encoding"endin(* Equality and ordering on keys respects {i structural} equality semantics,
meaning two objects (containing keys) are considered equal even if their
children are stored at different offsets (either as duplicates in the same
pack file, or inside different pack files), or with different lengths (in
the event that the encoding environments were different). *)letequalab=Hash.equal(to_hasha)(to_hashb)inletcompareab=Hash.compare(to_hasha)(to_hashb)in(* The pre-hash image of a key is just the hash of the corresponding value.
NOTE: it's particularly important that we discard the file offset when
computing hashes of structured values (e.g. inodes), so that this hashing
process is reproducible in different stores (w/ different offsets for the
values). *)letpre_hashtf=Hash.pre_hash(to_hasht)finletencode_bintf=Hash.encode_bin(to_hasht)finletunboxed_encode_bintf=Hash.unboxed_encode_bin(to_hasht)finletdecode_binbufpos_ref=State{state=Indexed(Hash.decode_binbufpos_ref)}inletunboxed_decode_binbufpos_ref=State{state=Indexed(Hash.unboxed_decode_binbufpos_ref)}inletsize_of=Irmin.Type.Size.custom_staticHash.encoded_sizeinIrmin.Type.like(thash_t)~pre_hash~equal~compare~bin:(encode_bin,decode_bin,size_of)~unboxed_bin:(unboxed_encode_bin,unboxed_decode_bin,size_of)letv_direct~hash~offset~length=State{state=Direct{hash;offset;length}}letv_indexedhash=State{state=Indexedhash}letv_offsetoffset=State{state=Offsetoffset}moduletypeS=sigtypehashincludeIrmin_pack.Pack_key.Swithtypet=hashtandtypehash:=hashendmoduleMake(Hash:Irmin.Hash.S)=structtypenonrect=Hash.tt[@@derivingirmin]typehash=Hash.t[@@derivingirmin~of_bin_string]letto_hash=to_hashletnull_offset=Int63.minus_oneletnull_length=-1letnull=letbuf=String.makeHash.hash_size'\000'inlethash=matchhash_of_bin_stringbufwithOkx->x|Error_->assertfalseinv_direct~hash~offset:null_offset~length:null_lengthletunfindable_of_hashhash=v_direct~hash~offset:null_offset~length:null_lengthendmoduletypeStore_spec=sigtype('h,_)contents_key='httype'hnode_key='httype'hcommit_key='htendmodulerecStore_spec:Store_spec=Store_spec