ocaml/stdlib/oo.ml

438 lines
11 KiB
OCaml
Raw Normal View History

(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Obj
let copy o = (magic Array.copy : (< .. > as 'a) -> 'a) o
(**** Compression options ****)
let compact_table = ref true
let copy_parent = ref true
let clean_when_copying = ref true
let retry_count = ref 3
let bucket_small_size = ref 16
(**** Parameters ****)
let step = Sys.word_size / 16
let first_label = 0
let bucket_size = 32 (* Must be 256 or less *)
(**** Version ****)
let version = ref 0
(**** Index ****)
type label = int
let label_count = ref 0
let next label =
incr label_count;
let label = label + step in
if label mod (step * bucket_size) = 0 then
label + step * (65536 - bucket_size)
else
label
let decode label =
(label / 65536 / step, (label mod (step * bucket_size)) / step)
(**** Items ****)
type item
let dummy_item = (magic () : item)
(**** Buckets ****)
type bucket = item array
let bucket_list = ref []
let empty_bucket = [| |]
let new_bucket () =
let bucket = Array.create (bucket_size + 1) dummy_item in
bucket.(bucket_size) <- (magic !version : item);
bucket_list := bucket :: !bucket_list;
bucket
let copy_bucket bucket =
let bucket = Array.copy bucket in
bucket.(bucket_size) <- (magic !version : item);
bucket_list := bucket :: !bucket_list;
bucket
let bucket_version bucket =
(magic bucket.(bucket_size) : int)
(**** Make a clean bucket ****)
let new_filled_bucket pos methods =
let bucket = new_bucket () in
List.iter
(fun (lab, met) ->
let (buck, elem) = decode lab in
if buck = pos then
bucket.(elem) <- (magic met : item))
(List.rev methods);
bucket
(**** Bucket merging ****)
let small_buckets = ref (Array.create 10 [| |])
let small_bucket_count = ref 0
let insert_bucket bucket =
let length = Array.length !small_buckets in
if !small_bucket_count >= length then begin
let new_array = Array.create (2 * length) [| |] in
Array.blit !small_buckets 0 new_array 0 length;
small_buckets := new_array
end;
!small_buckets.(!small_bucket_count) <- bucket;
incr small_bucket_count
let remove_bucket n =
!small_buckets.(n) <- !small_buckets.(!small_bucket_count - 1);
decr small_bucket_count
let bucket_used b =
let n = ref 0 in
for i = 0 to bucket_size - 1 do
if b.(i) != dummy_item then incr n
done;
!n
let small_bucket b = bucket_used b <= !bucket_small_size
exception Failed
let rec except e =
function
[] -> []
| e'::l -> if e == e' then l else e'::(except e l)
let merge_buckets b1 b2 =
for i = 0 to bucket_size - 1 do
if
(b2.(i) != dummy_item) & (b1.(i) != dummy_item) & (b2.(i) != b1.(i))
then
raise Failed
done;
for i = 0 to bucket_size - 1 do
if b2.(i) != dummy_item then
b1.(i) <- b2.(i)
done;
bucket_list := except b2 !bucket_list;
b1
let rec choose bucket i =
if (i > 0) & (!small_bucket_count > 0) then begin
let n = Random.int !small_bucket_count in
if not (small_bucket !small_buckets.(n)) then begin
remove_bucket n; choose bucket i
end else
try
merge_buckets !small_buckets.(n) bucket
with Failed ->
choose bucket (i - 1)
end else begin
insert_bucket bucket;
bucket
end
let compact b =
if
(b != empty_bucket) & (bucket_version b = !version) & (small_bucket b)
then
choose b !retry_count
else
b
let compact_buckets buckets =
for i = 0 to Array.length buckets - 1 do
buckets.(i) <- compact buckets.(i)
done
(**** Labels ****)
let last_label = ref first_label
let methods = Hashtbl.create 101
let new_label () =
let label = !last_label in
last_label := next !last_label;
label
let new_method met =
try
Hashtbl.find methods met
with Not_found ->
let label = new_label () in
Hashtbl.add methods met label;
label
(**** Sparse array ****)
module Vars = Map.Make(struct type t = string let compare = compare end)
type vars = int Vars.t
type obj_init
type table =
{ mutable buckets: bucket array;
mutable methods: (label * item) list;
mutable vars: vars;
mutable saved_vars: vars list;
mutable saved_var_lst: int option list;
mutable size: int;
mutable init: obj_init list list }
let table_count = ref 0
let initial_size = 1
let new_table () =
incr table_count;
{ buckets = [| |];
methods = [];
vars = Vars.empty;
saved_vars = [];
saved_var_lst = [];
size = initial_size;
init = [[]; []]}
let copy_table array1 array2 =
incr version;
array1.buckets <- Array.copy array2.buckets;
array1.methods <- array2.methods;
array1.vars <- array2.vars;
array1.saved_vars <- [];
array1.saved_var_lst <- array1.saved_var_lst;
array1.size <- array2.size;
array1.init <- array2.init
let resize array new_size =
let old_size = Array.length array.buckets in
if new_size > old_size then begin
let new_buck = Array.create new_size empty_bucket in
Array.blit array.buckets 0 new_buck 0 old_size;
array.buckets <- new_buck
end
let put array label element =
let (buck, elem) = decode label in
resize array (buck + 1);
let bucket = ref (array.buckets.(buck)) in
if !bucket == empty_bucket then begin
bucket := new_bucket ();
array.buckets.(buck) <- !bucket
end;
if !bucket.(elem) != element then begin
if
(bucket_version !bucket < !version)
&
(!bucket.(elem) != dummy_item)
then begin
if !clean_when_copying then
bucket := new_filled_bucket buck array.methods
else
bucket := copy_bucket !bucket;
array.buckets.(buck) <- !bucket
end;
!bucket.(elem) <- element
end
(**** Classes ****)
type t
type class_info =
{mutable obj_init: t -> t;
mutable class_init: table -> unit;
mutable table: table}
let set_initializer table init =
match table.init with
l::l'::l'' ->
let i =
List.fold_right
(fun init2 init1 -> (magic init1 : obj_init -> obj_init) init2)
l init
in
table.init <- (i::l')::l''
| _ ->
invalid_arg "Fatal error in Oo.set_initializer."
let inheritance table cl vars =
if
!copy_parent & (table.methods = []) & (table.size = initial_size)
& (table.init = [[]; []])
then begin
copy_table table cl.table;
table.init <- table.init@[[]];
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
Vars.empty
vars
end else begin
table.init <- []::table.init;
table.saved_vars <- table.vars::table.saved_vars;
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
Vars.empty
vars;
cl.class_init table;
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
(List.hd table.saved_vars)
vars;
table.saved_vars <- List.tl table.saved_vars
end
let set_method table label element =
table.methods <- (label, element) :: table.methods;
put table label element
let get_method table label =
let (buck, elem) = decode label in
table.buckets.(buck).(elem)
let new_slot table =
let index = table.size in
table.size <- index + 1;
index
let get_variable table name =
try
Vars.find name table.vars
with Not_found ->
let index = new_slot table in
table.vars <- Vars.add name index table.vars;
index
let hide_variable table name =
try
let i = Vars.find name table.vars in
table.vars <- Vars.remove name table.vars;
table.saved_var_lst <- Some i::table.saved_var_lst
with Not_found ->
table.saved_var_lst <- None::table.saved_var_lst
let rec list_remove name =
function
[] ->
invalid_arg "Fatal error in Oo.get_private_variable"
| (n, _) as a::l ->
if name = n then l
else a::list_remove name l
let get_private_variable table name =
let index =
try Vars.find name table.vars with Not_found -> new_slot table
in
table.vars <-
begin match List.hd table.saved_var_lst with
None ->
Vars.remove name table.vars
| Some i ->
Vars.add name i table.vars
end;
table.saved_var_lst <- List.tl table.saved_var_lst;
index
let method_count = ref 0
let inst_var_count = ref 0
let new_object table =
let obj = Array.create table.size (magic () : t) in
obj.(0) <- (magic table.buckets : t);
obj
let create_class class_info class_init =
let table = new_table () in
class_init table;
method_count := !method_count + List.length table.methods;
if !compact_table then
compact_buckets table.buckets;
inst_var_count := !inst_var_count + table.size - 1;
class_info.class_init <- class_init;
class_info.table <- table;
class_info.obj_init <-
(function x ->
let obj = Array.create table.size (magic () : t) in
obj.(0) <- (magic table.buckets : t);
(magic (List.hd (List.hd table.init))) obj x)
(**** Objects ****)
type object = t array
let inst_var obj lab =
let (buck, elem) = decode lab in
obj.((magic obj : int array array array).(0).(buck).(elem))
let set_inst_var obj lab value =
let (buck, elem) = decode lab in
obj.((magic obj : int array array array).(0).(buck).(elem)) <- value
let send obj lab =
let (buck, elem) = decode lab in
(magic obj : (object -> t) array array array).(0).(buck).(elem) obj
(**** Statistics ****)
type stats =
{ classes: int; labels: int; methods: int; inst_vars: int; buckets: int;
distrib : int array; small_bucket_count: int; small_bucket_max: int }
let distrib () =
let d = Array.create 32 0 in
List.iter
(function b ->
let n = bucket_used b in
d.(n - 1) <- d.(n - 1) + 1)
!bucket_list;
d
let stats () =
{ classes = !table_count; labels = !label_count;
methods = !method_count; inst_vars = !inst_var_count;
buckets = List.length !bucket_list; distrib = distrib ();
small_bucket_count = !small_bucket_count;
small_bucket_max = Array.length !small_buckets }
let sort_buck lst =
List.map snd
(Sort.list (fun (n, _) (n', _) -> n <= n')
(List.map (function b -> (bucket_used b, b)) lst))
let show_buckets () =
List.iter
(function b ->
for i = 0 to bucket_size - 1 do
print_char (if b.(i) == dummy_item then '.' else '*')
done;
print_newline ())
(sort_buck !bucket_list)