123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323(* 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]
*)externalunbox_float:(float[@local_opt])->(float#[@unboxed][@local_opt])@@portable="%unbox_float"externalint_of_int32_u:(int32#[@unboxed][@local_opt])->int@@portable="%int_of_int32#"externalunbox_int64:(int64[@local_opt])->(int64#[@unboxed][@local_opt])@@portable="%unbox_int64"externalint64_u_of_nativeint_u:(nativeint#[@unboxed])->(int64#[@unboxed][@local_opt])@@portable="%int64#_of_nativeint#"letfold_unit_us#()=sletint_of_bool_u=function|#false->0|#true->1;;externalint_of_char_u:(char#[@unboxed])->int@@portable="%int_of_int8#"[@@warning"-187"]letcode_of_char_ux=int_of_char_uxland255openBasement.Or_null_shim.ExportincludeHash_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('a:any)folder=state->'a->statelethash_fold_units()=slethash_fold_unit_u=fold_unit_ulethash_fold_int=Hash.fold_intlethash_fold_int64_u=Hash.fold_int64_ulethash_fold_int64sx=hash_fold_int64_us(unbox_int64x)lethash_fold_float_u=Hash.fold_float_ulethash_fold_floatsx=hash_fold_float_us(unbox_floatx)lethash_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_int32_usx=hash_fold_ints(int_of_int32_ux)lethash_fold_char=as_intChar.codelethash_fold_char_usx=hash_fold_ints(code_of_char_ux)lethash_fold_bool=as_int(function|true->1|false->0);;lethash_fold_bool_usx=hash_fold_ints(int_of_bool_ux)lethash_fold_nativeintsx=hash_fold_int64s(Stdlib.Int64.of_nativeintx)lethash_fold_nativeint_usx=hash_fold_int64_us(int64_u_of_nativeint_ux)lethash_fold_optionhash_fold_elems=function|None->hash_fold_ints0|Somex->hash_fold_elem(hash_fold_ints1)x;;lethash_fold_or_nullhash_fold_elems=function|Null->hash_fold_ints0|Thisx->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_nativeint_ux=Hash.get_hash_value(hash_fold_nativeint_u(Hash.reset(Hash.alloc()))x);;lethash_int64x=Hash.get_hash_value(hash_fold_int64(Hash.reset(Hash.alloc()))x)lethash_int64_ux=Hash.get_hash_value(hash_fold_int64_u(Hash.reset(Hash.alloc()))x);;lethash_int32x=Hash.get_hash_value(hash_fold_int32(Hash.reset(Hash.alloc()))x)lethash_int32_ux=Hash.get_hash_value(hash_fold_int32_u(Hash.reset(Hash.alloc()))x);;lethash_charx=Hash.get_hash_value(hash_fold_char(Hash.reset(Hash.alloc()))x)lethash_char_ux=Hash.get_hash_value(hash_fold_char_u(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_bool_ux=Hash.get_hash_value(hash_fold_bool_u(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_float_ux=Hash.get_hash_value(hash_fold_float_u(Hash.reset(Hash.alloc()))x);;lethash_unitx=Hash.get_hash_value(hash_fold_unit(Hash.reset(Hash.alloc()))x)lethash_unit_ux=Hash.get_hash_value(hash_fold_unit_u(Hash.reset(Hash.alloc()))x);;endmoduleF(Hash:Hash_intf.S):Hash_intf.Fullwithtypehash_value=Hash.hash_valueandtypestate=Hash.stateandtypeseed=Hash.seed=structincludeHashtype('a:any)folder=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:sig@@portableincludeHash_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[@unboxed])->state="Base_internalhash_fold_int64""Base_internalhash_fold_int64_unboxed"[@@noalloc]externalfold_int:state->int->state="Base_internalhash_fold_int"[@@noalloc]externalfold_float:state->(float[@unboxed])->state="Base_internalhash_fold_float""Base_internalhash_fold_float_unboxed"[@@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=Base_internalhash_types.compare_stateletstate_to_string=Base_internalhash_types.state_to_stringendendmoduleT=structincludeInternalhashtype('a:any)folder=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=structincludeFolding(Internalhash)(* Hack to make zero-alloc see that these functions are zero-alloc, even in
BUILD_PROFILE=dev. dev builds do not inline functor applications like
[Folding (Internalhash)] above.
*)lethash_fold_string=Internalhash.fold_stringlethash_fold_float=Internalhash.fold_floatlethash_fold_int=Internalhash.fold_intlethash_fold_int64=Internalhash.fold_int64endincludeFolding(* [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=Char.code(* 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[@unboxed])->int@@portable="Base_hash_double""Base_hash_double_unboxed"[@@noalloc]lethash_unit()=0endendincludeT