(* GC Stop & Copy *)

open Format
open Array (* une fois n'est pas coutume *)

let ram = 100
let memory = make ram 0
let space_size = ram / 2
let max_roots = 10
let roots = make max_roots (-1)

let is_pointer start p = start <= p && p < start + space_size

let stop_and_copy from_space to_space =
  (* printf "collection from_space=%d to_space=%d@." from_space to_space; *)
  let next = ref to_space in
  let forward p =
    if is_pointer from_space p then
      if is_pointer to_space memory.(p + 1) then
        memory.(p + 1)
      else begin
        let size = memory.(p) in
        let p' = !next in
        (* printf "  déplacement size=%d de %d vers %d@." size p p'; *)
        memory.(p') <- size;
        for i = 1 to size do memory.(p' + i) <- memory.(p + i) done;
        memory.(p + 1) <- p';
        next := !next + size + 1;
        p'
      end
    else
      p
  in
  let scan = ref to_space in
  for r = 0 to length roots - 1 do
    roots.(r) <- forward roots.(r)
  done;
  while !scan < !next do
    let size = memory.(!scan) in
    for i = 1 to size do
      memory.(!scan + i) <- forward memory.(!scan + i)
    done;
    scan := !scan + size + 1
  done;
  printf "mémoire occupée après collection = %d mots@\n" (!next - to_space);
  !next


let first_space = ref true
  (* true  = on alloue dans le premier demi-espace
             i.e. from_space = 0 / to_space = space_size
     false = on alloue dans le second demi-espace
             i.e. from_space = space_size / to_space = 0 *)
let next = ref 0
  (* premier emplacement libre *)

(* alloue un bloc de taille size i.e. avec size champs *)
let rec alloc size =
  let from_space = if !first_space then 0 else space_size in
  let limit = from_space + space_size in
  if !next + size + 1 <= limit then begin
    (* il y a assez de place *)
    let p = !next in
    memory.(p) <- size;
    next := !next + size + 1;
    p
  end else begin
    (* pas assez de place *)
    let to_space = if !first_space then space_size else 0 in
    next := stop_and_copy from_space to_space;
    if !next + size + 1 >= to_space + limit then failwith "out of memory";
    first_space := not !first_space;
    alloc size
  end

let reset () =
  fill memory 0 ram (-42);
  fill roots 0 max_roots (-1);
  first_space := true;
  next := 0;;

(****

(* test avec l'exemple du cours *)

let _ =
  fill memory 0 ram (-42);
  fill roots 0 max_roots (-1);
  roots.(0) <- 9;
  roots.(1) <- 3;
  blit [| 2; -1; 3;
          2; -2; 6;
          2; -3; -1;
          2; -2; -1;
          2; 9; 3 |] 0 memory 0 15;
  stop_and_copy 0 space_size

****)

(* réponse à la devinette :

   Les fonctions insert et sort sont récursives et une partie des données
   se trouve donc parfois stockée temporairement sur la pile, qui est ici la
   pile d'ocaml et n'est pas matérialisée en mémoire.

   Dans le cas extrême où ram=100 et n=16 l'intégralité de la liste occupe 48
   mots, pour un demi-espace de 50 mots. Quand on appelle "sort", elle parcourt
   la liste récursivement, on conservant les éléments dans les variables
   locales y se trouvant sur la pile. Arrivée au bout, elle appelle "insert"
   et, vu que roots.(0)=-1, la première chose que fait insert c'est allouer un
   bloc de taille 3 avec "cons0 x". Vu qu'il n'y a pas la place, on appelle le
   GC, qui récupère alors toute la mémoire vu que roots.(0)=-1. Mais la liste
   sera bien reconstruite en mémoire, à partir des valeurs stockées en pile.

*)


This document was generated using caml2html