236 lines
6.6 KiB
OCaml
236 lines
6.6 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Objective Caml *)
|
|
(* *)
|
|
(* Damien Doligez, projet Para, INRIA Rocquencourt *)
|
|
(* *)
|
|
(* Copyright 1997 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. All rights reserved. This file is distributed *)
|
|
(* under the terms of the GNU Library General Public License, with *)
|
|
(* the special exception on linking described in file ../LICENSE. *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(* $Id$ *)
|
|
|
|
|
|
(** Weak array operations *)
|
|
|
|
type 'a t;;
|
|
|
|
external create: int -> 'a t = "weak_create";;
|
|
|
|
let length x = Obj.size(Obj.repr x) - 1;;
|
|
|
|
external set : 'a t -> int -> 'a option -> unit = "weak_set";;
|
|
external get: 'a t -> int -> 'a option = "weak_get";;
|
|
external get_copy: 'a t -> int -> 'a option = "weak_get_copy";;
|
|
external check: 'a t -> int -> bool = "weak_check";;
|
|
|
|
let fill ar ofs len x =
|
|
if ofs < 0 || len < 0 || ofs + len > length ar
|
|
then raise (Invalid_argument "Weak.fill")
|
|
else begin
|
|
for i = ofs to (ofs + len - 1) do
|
|
set ar i x
|
|
done
|
|
end
|
|
;;
|
|
|
|
let blit ar1 of1 ar2 of2 len =
|
|
if of1 < 0 || of1 + len > length ar1 || of2 < 0 || of2 + len > length ar2
|
|
then raise (Invalid_argument "Weak.blit")
|
|
else begin
|
|
if of2 > of1 then begin
|
|
for i = 0 to len - 1 do
|
|
set ar2 (of2 + i) (get ar1 (of1 + i))
|
|
done
|
|
end else begin
|
|
for i = len - 1 downto 0 do
|
|
set ar2 (of2 + i) (get ar1 (of1 + i))
|
|
done
|
|
end
|
|
end
|
|
;;
|
|
|
|
|
|
(** Weak hash tables *)
|
|
|
|
module type S = sig
|
|
type data
|
|
type t
|
|
val create : int -> t
|
|
val clear : t -> unit
|
|
val merge : t -> data -> data
|
|
val add : t -> data -> unit
|
|
val remove : t -> data -> unit
|
|
val find : t -> data -> data
|
|
val find_all : t -> data -> data list
|
|
val mem : t -> data -> bool
|
|
val iter : (data -> unit) -> t -> unit
|
|
val fold : (data -> 'a -> 'a) -> t -> 'a -> 'a
|
|
val count : t -> int
|
|
val stats : t -> int * int * int * int * int * int
|
|
end;;
|
|
|
|
module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct
|
|
|
|
type 'a weak_t = 'a t;;
|
|
let weak_create = create;;
|
|
let emptybucket = weak_create 0;;
|
|
|
|
type data = H.t;;
|
|
|
|
type t = {
|
|
mutable table : data weak_t array;
|
|
mutable totsize : int; (* sum of the bucket sizes *)
|
|
mutable limit : int; (* max ratio totsize/table length *)
|
|
};;
|
|
|
|
let get_index t d = (H.hash d land max_int) mod (Array.length t.table);;
|
|
|
|
let create sz =
|
|
let sz = if sz < 7 then 7 else sz in
|
|
let sz = if sz > Sys.max_array_length then Sys.max_array_length else sz in
|
|
{
|
|
table = Array.create sz emptybucket;
|
|
totsize = 0;
|
|
limit = 3;
|
|
};;
|
|
|
|
let clear t =
|
|
for i = 0 to Array.length t.table - 1 do
|
|
t.table.(i) <- emptybucket;
|
|
done;
|
|
t.totsize <- 0;
|
|
t.limit <- 3;
|
|
;;
|
|
|
|
let fold f t init =
|
|
let rec fold_bucket i b accu =
|
|
if i >= length b then accu else
|
|
match get b i with
|
|
| Some v -> fold_bucket (i+1) b (f v accu)
|
|
| None -> fold_bucket (i+1) b accu
|
|
in
|
|
Array.fold_right (fold_bucket 0) t.table init
|
|
;;
|
|
|
|
let iter f t = fold (fun d () -> ()) t ();;
|
|
|
|
let count t =
|
|
let rec count_bucket i b accu =
|
|
if i >= length b then accu else
|
|
count_bucket (i+1) b (accu + (if check b i then 1 else 0))
|
|
in
|
|
Array.fold_right (count_bucket 0) t.table 0
|
|
;;
|
|
|
|
let next_sz n = min (3*n/2 + 3) (Sys.max_array_length - 1);;
|
|
|
|
let rec resize t =
|
|
let oldlen = Array.length t.table in
|
|
let newlen = next_sz oldlen in
|
|
if newlen > oldlen then begin
|
|
let newt = create newlen in
|
|
newt.limit <- t.limit + 100; (* prevent resizing of newt *)
|
|
fold (fun d () -> add newt d) t ();
|
|
(* assert Array.length newt.table = newlen; *)
|
|
t.table <- newt.table;
|
|
t.limit <- t.limit + 2;
|
|
end
|
|
|
|
and add_aux t d index =
|
|
let bucket = t.table.(index) in
|
|
let sz = length bucket in
|
|
let rec loop i =
|
|
if i >= sz then begin
|
|
let newsz = min (sz + 3) (Sys.max_array_length - 1) in
|
|
if newsz <= sz then failwith "Weak.Make : hash bucket cannot grow more";
|
|
let newbucket = weak_create newsz in
|
|
blit bucket 0 newbucket 0 sz;
|
|
set newbucket i (Some d);
|
|
t.table.(index) <- newbucket;
|
|
t.totsize <- t.totsize + (newsz - sz);
|
|
if t.totsize > t.limit * Array.length t.table then resize t;
|
|
end else begin
|
|
if check bucket i
|
|
then loop (i+1)
|
|
else set bucket i (Some d)
|
|
end
|
|
in
|
|
loop 0;
|
|
|
|
and add t d = add_aux t d (get_index t d)
|
|
;;
|
|
|
|
let find_or t d ifnotfound =
|
|
let index = get_index t d in
|
|
let bucket = t.table.(index) in
|
|
let sz = length bucket in
|
|
let rec loop i =
|
|
if i >= sz then ifnotfound index
|
|
else begin
|
|
match get_copy bucket i with
|
|
| Some v when H.equal v d
|
|
-> begin match get bucket i with
|
|
| Some v -> v
|
|
| None -> loop (i+1)
|
|
end
|
|
| _ -> loop (i+1)
|
|
end
|
|
in
|
|
loop 0
|
|
;;
|
|
|
|
let merge t d = find_or t d (fun index -> add_aux t d index; d);;
|
|
|
|
let find t d = find_or t d (fun index -> raise Not_found);;
|
|
|
|
let find_shadow t d iffound ifnotfound =
|
|
let index = get_index t d in
|
|
let bucket = t.table.(index) in
|
|
let sz = length bucket in
|
|
let rec loop i =
|
|
if i >= sz then ifnotfound else begin
|
|
match get_copy bucket i with
|
|
| Some v when H.equal v d -> iffound bucket i
|
|
| _ -> loop (i+1)
|
|
end
|
|
in
|
|
loop 0
|
|
;;
|
|
|
|
let remove t d = find_shadow t d (fun w i -> set w i None) ();;
|
|
|
|
let mem t d = find_shadow t d (fun w i -> true) false;;
|
|
|
|
let find_all t d =
|
|
let index = get_index t d in
|
|
let bucket = t.table.(index) in
|
|
let sz = length bucket in
|
|
let rec loop i accu =
|
|
if i >= sz then accu
|
|
else begin
|
|
match get_copy bucket i with
|
|
| Some v when H.equal v d
|
|
-> begin match get bucket i with
|
|
| Some v -> loop (i+1) (v::accu)
|
|
| None -> loop (i+1) accu
|
|
end
|
|
| _ -> loop (i+1) accu
|
|
end
|
|
in
|
|
loop 0 []
|
|
;;
|
|
|
|
let stats t =
|
|
let len = Array.length t.table in
|
|
let lens = Array.map length t.table in
|
|
Array.sort compare lens;
|
|
let totlen = Array.fold_left ( + ) 0 lens in
|
|
(len, count t, totlen, lens.(0), lens.(len/2), lens.(len-1))
|
|
;;
|
|
|
|
end;;
|