Add Hashtbl.rebuild function
This provides an officially-sanctioned, guaranteed-to-work way to import a hash table that has been built with an old version of OCaml (say, before 4.00) and marshaled to persistent storage.master
parent
b0118a97db
commit
a6f03cc10f
|
@ -112,39 +112,43 @@ let copy h = { h with data = Array.map copy_bucketlist h.data }
|
|||
|
||||
let length h = h.size
|
||||
|
||||
let insert_all_buckets indexfun inplace odata ndata =
|
||||
let nsize = Array.length ndata in
|
||||
let ndata_tail = Array.make nsize Empty in
|
||||
let rec insert_bucket = function
|
||||
| Empty -> ()
|
||||
| Cons {key; data; next} as cell ->
|
||||
let cell =
|
||||
if inplace then cell
|
||||
else Cons {key; data; next = Empty}
|
||||
in
|
||||
let nidx = indexfun key in
|
||||
begin match ndata_tail.(nidx) with
|
||||
| Empty -> ndata.(nidx) <- cell;
|
||||
| Cons tail -> tail.next <- cell;
|
||||
end;
|
||||
ndata_tail.(nidx) <- cell;
|
||||
insert_bucket next
|
||||
in
|
||||
for i = 0 to Array.length odata - 1 do
|
||||
insert_bucket odata.(i)
|
||||
done;
|
||||
if inplace then
|
||||
for i = 0 to nsize - 1 do
|
||||
match ndata_tail.(i) with
|
||||
| Empty -> ()
|
||||
| Cons tail -> tail.next <- Empty
|
||||
done
|
||||
|
||||
let resize indexfun h =
|
||||
let odata = h.data in
|
||||
let osize = Array.length odata in
|
||||
let nsize = osize * 2 in
|
||||
if nsize < Sys.max_array_length then begin
|
||||
let ndata = Array.make nsize Empty in
|
||||
let ndata_tail = Array.make nsize Empty in
|
||||
let inplace = not (ongoing_traversal h) in
|
||||
h.data <- ndata; (* so that indexfun sees the new bucket count *)
|
||||
let rec insert_bucket = function
|
||||
| Empty -> ()
|
||||
| Cons {key; data; next} as cell ->
|
||||
let cell =
|
||||
if inplace then cell
|
||||
else Cons {key; data; next = Empty}
|
||||
in
|
||||
let nidx = indexfun h key in
|
||||
begin match ndata_tail.(nidx) with
|
||||
| Empty -> ndata.(nidx) <- cell;
|
||||
| Cons tail -> tail.next <- cell;
|
||||
end;
|
||||
ndata_tail.(nidx) <- cell;
|
||||
insert_bucket next
|
||||
in
|
||||
for i = 0 to osize - 1 do
|
||||
insert_bucket odata.(i)
|
||||
done;
|
||||
if inplace then
|
||||
for i = 0 to nsize - 1 do
|
||||
match ndata_tail.(i) with
|
||||
| Empty -> ()
|
||||
| Cons tail -> tail.next <- Empty
|
||||
done;
|
||||
insert_all_buckets (indexfun h) inplace odata ndata
|
||||
end
|
||||
|
||||
let iter f h =
|
||||
|
@ -611,3 +615,19 @@ let of_seq i =
|
|||
let tbl = create 16 in
|
||||
replace_seq tbl i;
|
||||
tbl
|
||||
|
||||
let rebuild ?(random = !randomized) h =
|
||||
let s = power_2_above 16 (Array.length h.data) in
|
||||
let seed =
|
||||
if random then Random.State.bits (Lazy.force prng)
|
||||
else if Obj.size (Obj.repr h) >= 4 then h.seed
|
||||
else 0 in
|
||||
let h' = {
|
||||
size = h.size;
|
||||
data = Array.make s Empty;
|
||||
seed = seed;
|
||||
initial_size = if Obj.size (Obj.repr h) >= 4 then h.initial_size else s
|
||||
} in
|
||||
insert_all_buckets (key_index h') false h.data h'.data;
|
||||
h'
|
||||
|
||||
|
|
|
@ -191,10 +191,27 @@ val randomize : unit -> unit
|
|||
@since 4.00.0 *)
|
||||
|
||||
val is_randomized : unit -> bool
|
||||
(** return if the tables are currently created in randomized mode by default
|
||||
(** Return [true] if the tables are currently created in randomized mode
|
||||
by default, [false] otherwise.
|
||||
|
||||
@since 4.03.0 *)
|
||||
|
||||
val rebuild : ?random:bool -> ('a, 'b) t -> ('a, 'b) t
|
||||
(** Return a copy of the given hashtable. Unlike {!Hashtbl.copy},
|
||||
[{!Hashtbl.rebuild} h] re-hashes all the (key, value) entries of
|
||||
the original table [h]. The returned hash table is randomized if
|
||||
[h] was randomized, or the optional [random] parameter is true, or
|
||||
if the default is to create randomized hash tables; see
|
||||
{!Hashtbl.create} for more information.
|
||||
|
||||
{!Hashtbl.rebuild} can safely be used to import a hash table built
|
||||
by an old version of the {!Hashtbl} module, then marshaled to
|
||||
persistent storage. After unmarshaling, apply {!Hashtbl.rebuild}
|
||||
to produce a hash table for the current version of the {!Hashtbl}
|
||||
module.
|
||||
|
||||
@since 4.12.0 *)
|
||||
|
||||
(** @since 4.00.0 *)
|
||||
type statistics = {
|
||||
num_bindings: int;
|
||||
|
@ -481,3 +498,4 @@ val seeded_hash_param : int -> int -> int -> 'a -> int
|
|||
an integer seed. Usage:
|
||||
[Hashtbl.seeded_hash_param meaningful total seed x].
|
||||
@since 4.00.0 *)
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ module Hashtbl : sig
|
|||
val length : ('a, 'b) t -> int
|
||||
val randomize : unit -> unit
|
||||
val is_randomized : unit -> bool
|
||||
val rebuild : ?random:bool -> ('a, 'b) t -> ('a, 'b) t
|
||||
type statistics = Hashtbl.statistics
|
||||
val stats : ('a, 'b) t -> statistics
|
||||
val to_seq : ('a,'b) t -> ('a * 'b) Seq.t
|
||||
|
|
Loading…
Reference in New Issue