diff options
Diffstat (limited to 'stdlib/weak.ml')
-rw-r--r-- | stdlib/weak.ml | 189 |
1 files changed, 127 insertions, 62 deletions
diff --git a/stdlib/weak.ml b/stdlib/weak.ml index 317c3729f8..de5f85286b 100644 --- a/stdlib/weak.ml +++ b/stdlib/weak.ml @@ -26,6 +26,8 @@ external set : 'a t -> int -> 'a option -> unit = "caml_weak_set";; external get: 'a t -> int -> 'a option = "caml_weak_get";; external get_copy: 'a t -> int -> 'a option = "caml_weak_get_copy";; external check: 'a t -> int -> bool = "caml_weak_check";; +external blit: 'a t -> int -> 'a t -> int -> int -> unit = "caml_weak_blit";; +(* blit: src srcoff dst dstoff len *) let fill ar ofs len x = if ofs < 0 || len < 0 || ofs + len > length ar @@ -37,23 +39,6 @@ let fill ar ofs len x = 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 @@ -83,27 +68,35 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct type t = { mutable table : data weak_t array; - mutable totsize : int; (* sum of the bucket sizes *) - mutable limit : int; (* max ratio totsize/table length *) + mutable hashes : int array array; + mutable limit : int; (* bucket size limit *) + mutable oversize : int; (* number of oversize buckets *) + mutable rover : int; (* for internal bookkeeping *) };; - let get_index t d = (H.hash d land max_int) mod (Array.length t.table);; + let get_index t h = (h land max_int) mod (Array.length t.table);; + + let limit = 7;; + let over_limit = 2;; 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; + hashes = Array.create sz [| |]; + limit = limit; + oversize = 0; + rover = 0; };; let clear t = for i = 0 to Array.length t.table - 1 do t.table.(i) <- emptybucket; + t.hashes.(i) <- [| |]; done; - t.totsize <- 0; - t.limit <- 3; + t.limit <- limit; + t.oversize <- 0; ;; let fold f t init = @@ -126,85 +119,155 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct Array.iter (iter_bucket 0) t.table ;; - 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)) + let iter_weak f t = + let rec iter_bucket i j b = + if i >= length b then () else + match check b i with + | true -> f b t.hashes.(j) i; iter_bucket (i+1) j b + | false -> iter_bucket (i+1) j b in + Array.iteri (iter_bucket 0) t.table + ;; + + 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)) + ;; + + let count t = Array.fold_right (count_bucket 0) t.table 0 ;; - let next_sz n = min (3*n/2 + 3) (Sys.max_array_length - 1);; + let next_sz n = min (3 * n / 2 + 3) Sys.max_array_length;; + let prev_sz n = ((n - 3) * 2 + 2) / 3;; + + let test_shrink_bucket t = + let bucket = t.table.(t.rover) in + let hbucket = t.hashes.(t.rover) in + let len = length bucket in + let prev_len = prev_sz len in + let live = count_bucket 0 bucket 0 in + if live <= prev_len then begin + let rec loop i j = + if j >= prev_len then begin + if check bucket i then loop (i + 1) j + else if check bucket j then begin + blit bucket j bucket i 1; + hbucket.(i) <- hbucket.(j); + loop (i + 1) (j - 1); + end else loop i (j - 1); + end; + in + loop 0 (length bucket - 1); + if prev_len = 0 then begin + t.table.(t.rover) <- emptybucket; + t.hashes.(t.rover) <- [| |]; + end else begin + Obj.truncate (Obj.repr bucket) (prev_len + 1); + Obj.truncate (Obj.repr hbucket) prev_len; + end; + if len > t.limit && prev_len <= t.limit then t.oversize <- t.oversize - 1; + end; + t.rover <- (t.rover + 1) mod (Array.length t.table); + ;; 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; *) + let add_weak ob oh oi = + let setter nb ni _ = blit ob oi nb ni 1 in + let h = oh.(oi) in + add_aux newt setter None h (get_index newt h); + in + iter_weak add_weak t; t.table <- newt.table; - (* t.limit <- t.limit + 2; -- performance bug *) + t.hashes <- newt.hashes; + t.limit <- newt.limit; + t.oversize <- newt.oversize; + t.rover <- t.rover mod Array.length newt.table; + end else begin + t.limit <- max_int; (* maximum size already reached *) + t.oversize <- 0; end - and add_aux t d index = + and add_aux t setter d h index = let bucket = t.table.(index) in + let hashes = t.hashes.(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 newsz = min (3 * sz / 2 + 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 + let newhashes = Array.make newsz 0 in blit bucket 0 newbucket 0 sz; - set newbucket i (Some d); + Array.blit hashes 0 newhashes 0 sz; + setter newbucket sz d; + newhashes.(sz) <- h; t.table.(index) <- newbucket; - t.totsize <- t.totsize + (newsz - sz); - if t.totsize > t.limit * Array.length t.table then resize t; + t.hashes.(index) <- newhashes; + if sz <= t.limit && newsz > t.limit then begin + t.oversize <- t.oversize + 1; + for i = 0 to over_limit do test_shrink_bucket t done; + end; + if t.oversize > Array.length t.table / over_limit then resize t; + end else if check bucket i then begin + loop (i + 1) end else begin - if check bucket i - then loop (i+1) - else set bucket i (Some d) - end + setter bucket i d; + hashes.(i) <- h; + end; in loop 0; + ;; - and add t d = add_aux t d (get_index t d) + let add t d = + let h = H.hash d in + add_aux t set (Some d) h (get_index t h); ;; let find_or t d ifnotfound = - let index = get_index t d in + let h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i = - if i >= sz then ifnotfound index - else begin + if i >= sz then ifnotfound h index + else if h = hashes.(i) then 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) + | None -> loop (i + 1) end - | _ -> loop (i+1) - end + | _ -> loop (i + 1) + end else loop (i + 1) in loop 0 ;; - let merge t d = find_or t d (fun index -> add_aux t d index; d);; + let merge t d = + find_or t d (fun h index -> add_aux t set (Some d) h index; d) + ;; - let find t d = find_or t d (fun index -> raise Not_found);; + let find t d = find_or t d (fun h index -> raise Not_found);; let find_shadow t d iffound ifnotfound = - let index = get_index t d in + let h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i = - if i >= sz then ifnotfound else begin + if i >= sz then ifnotfound + else if h = hashes.(i) then begin match get_copy bucket i with | Some v when H.equal v d -> iffound bucket i - | _ -> loop (i+1) - end + | _ -> loop (i + 1) + end else loop (i + 1) in loop 0 ;; @@ -214,20 +277,22 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct 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 h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i accu = if i >= sz then accu - else begin + else if h = hashes.(i) then 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 + | Some v -> loop (i + 1) (v :: accu) + | None -> loop (i + 1) accu end - | _ -> loop (i+1) accu - end + | _ -> loop (i + 1) accu + end else loop (i + 1) accu in loop 0 [] ;; |