ocaml/stdlib/weak.ml

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