96 lines
2.8 KiB
OCaml
96 lines
2.8 KiB
OCaml
|
(* Hash tables *)
|
||
|
|
||
|
(* We do dynamic hashing, and we double the size of the table when
|
||
|
buckets become too long, but without re-hashing the elements. *)
|
||
|
|
||
|
type ('a, 'b) t =
|
||
|
{ mutable max_len: int; (* max length of a bucket *)
|
||
|
mutable data: ('a, 'b) bucketlist array } (* the buckets *)
|
||
|
|
||
|
and ('a, 'b) bucketlist =
|
||
|
Empty
|
||
|
| Cons of 'a * 'b * ('a, 'b) bucketlist
|
||
|
|
||
|
let new initial_size =
|
||
|
{ max_len = 2; data = Array.new initial_size Empty }
|
||
|
|
||
|
let clear h =
|
||
|
for i = 0 to Array.length h.data - 1 do
|
||
|
h.data.(i) <- Empty
|
||
|
done
|
||
|
|
||
|
let resize h =
|
||
|
let n = Array.length h.data in
|
||
|
let newdata = Array.new (n+n) Empty in
|
||
|
Array.blit h.data 0 newdata 0 n;
|
||
|
Array.blit h.data 0 newdata n n;
|
||
|
h.data <- newdata;
|
||
|
h.max_len <- 2 * h.max_len
|
||
|
|
||
|
let rec bucket_too_long n bucket =
|
||
|
if n < 0 then true else
|
||
|
match bucket with
|
||
|
Empty -> false
|
||
|
| Cons(_,_,rest) -> bucket_too_long (pred n) rest
|
||
|
|
||
|
external hash_param : int -> int -> 'a -> int = "hash_univ_param"
|
||
|
|
||
|
let add h key info =
|
||
|
let i = (hash_param 10 100 key) mod (Array.length h.data) in
|
||
|
let bucket = Cons(key, info, h.data.(i)) in
|
||
|
h.data.(i) <- bucket;
|
||
|
if bucket_too_long h.max_len bucket then resize h
|
||
|
|
||
|
let remove h key =
|
||
|
let rec remove_bucket = function
|
||
|
Empty ->
|
||
|
Empty
|
||
|
| Cons(k, i, next) ->
|
||
|
if k = key then next else Cons(k, i, remove_bucket next) in
|
||
|
let i = (hash_param 10 100 key) mod (Array.length h.data) in
|
||
|
h.data.(i) <- remove_bucket h.data.(i)
|
||
|
|
||
|
let find h key =
|
||
|
match h.data.((hash_param 10 100 key) mod (Array.length h.data)) with
|
||
|
Empty -> raise Not_found
|
||
|
| Cons(k1, d1, rest1) ->
|
||
|
if key = k1 then d1 else
|
||
|
match rest1 with
|
||
|
Empty -> raise Not_found
|
||
|
| Cons(k2, d2, rest2) ->
|
||
|
if key = k2 then d2 else
|
||
|
match rest2 with
|
||
|
Empty -> raise Not_found
|
||
|
| Cons(k3, d3, rest3) ->
|
||
|
if key = k3 then d3 else begin
|
||
|
let rec find = function
|
||
|
Empty ->
|
||
|
raise Not_found
|
||
|
| Cons(k, d, rest) ->
|
||
|
if key = k then d else find rest
|
||
|
in find rest3
|
||
|
end
|
||
|
|
||
|
let find_all h key =
|
||
|
let rec find_in_bucket = function
|
||
|
Empty ->
|
||
|
[]
|
||
|
| Cons(k, d, rest) ->
|
||
|
if k = key then d :: find_in_bucket rest else find_in_bucket rest in
|
||
|
find_in_bucket h.data.((hash_param 10 100 key) mod (Array.length h.data))
|
||
|
|
||
|
let iter f h =
|
||
|
let len = Array.length h.data in
|
||
|
for i = 0 to Array.length h.data - 1 do
|
||
|
let rec do_bucket = function
|
||
|
Empty ->
|
||
|
()
|
||
|
| Cons(k, d, rest) ->
|
||
|
if (hash_param 10 100 k) mod len = i
|
||
|
then begin f k d; do_bucket rest end
|
||
|
else do_bucket rest in
|
||
|
do_bucket h.data.(i)
|
||
|
done
|
||
|
|
||
|
let hash x = hash_param 50 500 x
|