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
Xavier Leroy 2020-07-13 16:53:43 +02:00
parent b0118a97db
commit a6f03cc10f
3 changed files with 65 additions and 26 deletions

View File

@ -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'

View File

@ -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 *)

View File

@ -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