123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223(*
This is the interface to the runtime support for [ppx_hash].
The [ppx_hash] syntax extension supports: [@@deriving hash] and [%hash_fold: TYPE] and
[%hash: TYPE]
For type [t] a function [hash_fold_t] of type [Hash.state -> t -> Hash.state] is
generated.
The generated [hash_fold_<T>] function is compositional, following the structure of the
type; allowing user overrides at every level. This is in contrast to ocaml's builtin
polymorphic hashing [Hashtbl.hash] which ignores user overrides.
The generator also provides a direct hash-function [hash] (named [hash_<T>] when <T> !=
"t") of type: [t -> Hash.hash_value].
The folding hash function can be accessed as [%hash_fold: TYPE]
The direct hash function can be accessed as [%hash: TYPE]
*)open!Import0moduleArray=Array0moduleChar=Char0moduleInt=Int0moduleList=List0includeHash_intf(** Builtin folding-style hash functions, abstracted over [Hash_intf.S] *)moduleFolding(Hash:Hash_intf.S):Hash_intf.Builtin_intfwithtypestate=Hash.stateandtypehash_value=Hash.hash_value=structtypestate=Hash.statetypehash_value=Hash.hash_valuetype'afolder=state->'a->statelethash_fold_units()=slethash_fold_int=Hash.fold_intlethash_fold_int64=Hash.fold_int64lethash_fold_float=Hash.fold_floatlethash_fold_string=Hash.fold_stringletas_intfsx=hash_fold_ints(fx)(* This ignores the sign bit on 32-bit architectures, but it's unlikely to lead to
frequent collisions (min_value colliding with 0 is the most likely one). *)lethash_fold_int32=as_intStdlib.Int32.to_intlethash_fold_char=as_intChar.to_intlethash_fold_bool=as_int(function|true->1|false->0);;lethash_fold_nativeintsx=hash_fold_int64s(Stdlib.Int64.of_nativeintx)lethash_fold_optionhash_fold_elems=function|None->hash_fold_ints0|Somex->hash_fold_elem(hash_fold_ints1)x;;letrechash_fold_list_bodyhash_fold_elemslist=matchlistwith|[]->s|x::xs->hash_fold_list_bodyhash_fold_elem(hash_fold_elemsx)xs;;lethash_fold_listhash_fold_elemslist=(* The [length] of the list must be incorporated into the hash-state so values of
types such as [unit list] - ([], [()], [();()],..) are hashed differently. *)(* The [length] must come before the elements to avoid a violation of the rule
enforced by Perfect_hash. *)lets=hash_fold_ints(List.lengthlist)inlets=hash_fold_list_bodyhash_fold_elemslistins;;lethash_fold_lazy_thash_fold_elemsx=hash_fold_elems(Stdlib.Lazy.forcex)lethash_fold_ref_frozenhash_fold_elemsx=hash_fold_elems!xletrechash_fold_array_frozen_ihash_fold_elemsarrayi=ifi=Array.lengtharraythenselse(lete=Array.unsafe_getarrayiinhash_fold_array_frozen_ihash_fold_elem(hash_fold_elemse)array(i+1));;lethash_fold_array_frozenhash_fold_elemsarray=hash_fold_array_frozen_i(* [length] must be incorporated for arrays, as it is for lists. See comment above *)hash_fold_elem(hash_fold_ints(Array.lengtharray))array0;;(* the duplication here is because we think
ocaml can't eliminate indirect function calls otherwise. *)lethash_nativeintx=Hash.get_hash_value(hash_fold_nativeint(Hash.reset(Hash.alloc()))x);;lethash_int64x=Hash.get_hash_value(hash_fold_int64(Hash.reset(Hash.alloc()))x)lethash_int32x=Hash.get_hash_value(hash_fold_int32(Hash.reset(Hash.alloc()))x)lethash_charx=Hash.get_hash_value(hash_fold_char(Hash.reset(Hash.alloc()))x)lethash_intx=Hash.get_hash_value(hash_fold_int(Hash.reset(Hash.alloc()))x)lethash_boolx=Hash.get_hash_value(hash_fold_bool(Hash.reset(Hash.alloc()))x)lethash_stringx=Hash.get_hash_value(hash_fold_string(Hash.reset(Hash.alloc()))x);;lethash_floatx=Hash.get_hash_value(hash_fold_float(Hash.reset(Hash.alloc()))x)lethash_unitx=Hash.get_hash_value(hash_fold_unit(Hash.reset(Hash.alloc()))x)endmoduleF(Hash:Hash_intf.S):Hash_intf.Fullwithtypehash_value=Hash.hash_valueandtypestate=Hash.stateandtypeseed=Hash.seed=structincludeHashtype'afolder=state->'a->stateletcreate?seed()=reset?seed(alloc())letof_foldhash_fold_tt=get_hash_value(hash_fold_t(create())t)moduleBuiltin=Folding(Hash)letrun?seedfolderx=Hash.get_hash_value(folder(Hash.reset?seed(Hash.alloc()))x);;endmoduleInternalhash:sigincludeHash_intf.Swithtypestate=Base_internalhash_types.state(* We give a concrete type for [state], albeit only partially exposed (see
Base_internalhash_types), so that it unifies with the same type in [Base_boot],
and to allow optimizations for the immediate type. *)andtypeseed=Base_internalhash_types.seedandtypehash_value=Base_internalhash_types.hash_valueexternalfold_int64:state->int64->state="Base_internalhash_fold_int64"[@@noalloc]externalfold_int:state->int->state="Base_internalhash_fold_int"[@@noalloc]externalfold_float:state->float->state="Base_internalhash_fold_float"[@@noalloc]externalfold_string:state->string->state="Base_internalhash_fold_string"[@@noalloc]externalget_hash_value:state->hash_value="Base_internalhash_get_hash_value"[@@noalloc]end=structletdescription="internalhash"includeBase_internalhash_typesletalloc()=create_seeded0letreset?(seed=0)_t=create_seededseedmoduleFor_tests=structletcompare_state(a:state)(b:state)=compare(a:>int)(b:>int)letstate_to_string(state:state)=Int.to_string(state:>int)endendmoduleT=structincludeInternalhashtype'afolder=state->'a->stateletcreate?seed()=reset?seed(alloc())letrun?seedfolderx=get_hash_value(folder(reset?seed(alloc()))x)letof_foldhash_fold_tt=get_hash_value(hash_fold_t(create())t)moduleBuiltin=structmoduleFolding=Folding(Internalhash)includeFolding(* [Folding] provides some default implementations for the [hash_*] functions below,
but they are inefficient for some use-cases because of the use of the [hash_fold]
functions. At this point, the [hash_value] type has been fixed to [int], so this
module can provide specialized implementations. *)lethash_char=Char0.to_int(* This hash was chosen from here: https://gist.github.com/badboy/6267743
It attempts to fulfill the primary goals of a non-cryptographic hash function:
- a bit change in the input should change ~1/2 of the output bits
- the output should be uniformly distributed across the output range
- inputs that are close to each other shouldn't lead to outputs that are close to
each other.
- all bits of the input are used in generating the output
In our case we also want it to be fast, non-allocating, and inlinable. *)let[@inlinealways]hash_int(t:int)=lett=lnott+(tlsl21)inlett=tlxor(tlsr24)inlett=t+(tlsl3)+(tlsl8)inlett=tlxor(tlsr14)inlett=t+(tlsl2)+(tlsl4)inlett=tlxor(tlsr28)int+(tlsl31);;lethash_boolx=ifxthen1else0externalhash_float:float->int="Base_hash_double"[@@noalloc]lethash_unit()=0endendincludeT