123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231(*
This is the interface to the runtime support for [ppx_hash].
The [ppx_hash] syntax extension supports: [@@deriving_inline hash][@@@end] 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_intCaml.Int32.to_intlethash_fold_char=as_intChar.to_intlethash_fold_bool=as_int(functiontrue->1|false->0)lethash_fold_nativeintsx=hash_fold_int64s(Caml.Int64.of_nativeintx)lethash_fold_optionhash_fold_elems=function|None->hash_fold_ints0|Somex->hash_fold_elem(hash_fold_ints1)xletrechash_fold_list_bodyhash_fold_elemslist=matchlistwith|[]->s|x::xs->hash_fold_list_bodyhash_fold_elem(hash_fold_elemsx)xslethash_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_elemslistinslethash_fold_lazy_thash_fold_elemsx=hash_fold_elems(Caml.Lazy.forcex)lethash_fold_ref_frozenhash_fold_elemsx=hash_fold_elems(!x)letrechash_fold_array_frozen_ihash_fold_elemsarrayi=ifi=Array.lengtharraythenselselete=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_t=(funt->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=privateint(* allow optimizations for immediate type *)andtypeseed=intandtypehash_value=intexternalfold_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"typestate=inttypehash_value=inttypeseed=intexternalcreate_seeded:seed->state="%identity"[@@noalloc]externalfold_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]letalloc()=create_seeded0letreset?(seed=0)_t=create_seededseedmoduleFor_tests=structletcompare_state=compareletstate_to_string=Int.to_stringendendmoduleT=structincludeInternalhashtype'afolder=state->'a->stateletcreate?seed()=reset?seed(alloc())letrun?seedfolderx=get_hash_value(folder(reset?seed(alloc()))x)letof_foldhash_fold_t=(funt->get_hash_value(hash_fold_t(create())t))moduleBuiltin=structmoduleFolding=Folding(Internalhash)include(Folding:Hash_intf.Builtin_hash_fold_intfwithtypestate:=stateandtype'afolder:='afolder)lethash_nativeint=Folding.hash_nativeintlethash_int64=Folding.hash_int64lethash_int32=Folding.hash_int32lethash_string=Folding.hash_string(* [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