safety commit
[SXSI/xpathcomp.git] / tree.ml
diff --git a/tree.ml b/tree.ml
index 730e174..26dc770 100644 (file)
--- a/tree.ml
+++ b/tree.ml
@@ -20,6 +20,11 @@ type tree
 type 'a node = private int
 type node_kind = [`Text | `Tree ]
 
+type t = { 
+  doc : tree;            
+  ttable : (Tag.t,(Ptset.Int.t*Ptset.Int.t*Ptset.Int.t*Ptset.Int.t)) Hashtbl.t;
+}
+
 external inode : 'a node -> int = "%identity"  
 external nodei : int -> 'a node = "%identity"  
 let compare_node x y = (inode x) - (inode y)
@@ -28,7 +33,7 @@ let equal_node : 'a node -> 'a node -> bool = (==)
   
 external parse_xml_uri : string -> int -> bool -> bool -> tree = "caml_call_shredder_uri"         
 external parse_xml_string :  string -> int -> bool -> bool -> tree = "caml_call_shredder_string"
-  
+external tree_print_xml_fast3 : tree -> [`Tree ] node -> Unix.file_descr ->unit = "caml_xml_tree_print"
 external tree_save : tree -> Unix.file_descr -> unit = "caml_xml_tree_save"
 external tree_load : Unix.file_descr -> tree = "caml_xml_tree_load"
   
@@ -53,6 +58,7 @@ external text_get_cached_text : tree -> [`Text] node -> string = "caml_text_coll
     
 external tree_root : tree -> [`Tree] node = "caml_xml_tree_root" 
 external tree_subtree_size : tree -> [`Tree] node -> int = "caml_xml_tree_subtree_size"
+external tree_subtree_elements : tree -> [`Tree] node -> int = "caml_xml_tree_subtree_elements"
  
 let tree_is_nil x = equal_node x nil
 
@@ -60,10 +66,12 @@ external tree_parent : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_par
 external tree_parent_doc : tree -> [`Text ] node -> [`Tree ] node = "caml_xml_tree_parent_doc" "noalloc"
 (*external tree_prev_doc : tree -> [`Text ] node -> [`Tree ] node = "caml_xml_tree_prev_doc" "noalloc" *)
 external tree_first_child : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_first_child" "noalloc"
-external tree_first_element : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_first_element" "noalloc"
+external tree_closing : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_closing" "noalloc"
+external tree_is_open : tree -> [`Tree] node -> bool = "caml_xml_tree_is_open" "noalloc"
+external tree_first_element : t -> [`Tree] node -> [`Tree] node = "caml_xml_tree_first_element" "noalloc"
 external tree_tagged_child : tree -> [`Tree] node -> Tag.t -> [`Tree] node = "caml_xml_tree_tagged_child" "noalloc" 
 external tree_next_sibling : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_next_sibling"  "noalloc"
-external tree_next_element : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_next_element"  "noalloc"
+external tree_next_element : t -> [`Tree] node -> [`Tree] node = "caml_xml_tree_next_element"  "noalloc"
 external tree_tagged_sibling : tree -> [`Tree] node -> Tag.t -> [`Tree] node = "caml_xml_tree_tagged_sibling" "noalloc"
 
 external tree_prev_sibling : tree -> [`Tree] node -> [`Tree] node = "caml_xml_tree_prev_sibling" "noalloc"
@@ -76,10 +84,10 @@ external tree_tag_id : tree -> [`Tree ] node -> Tag.t = "caml_xml_tree_tag_id"
     
 
 let tree_is_last t n = equal_node nil (tree_next_sibling t n)
-    
-(*external tree_prev_text : tree -> [`Tree] node -> [`Text ] node = "caml_xml_tree_prev_text" "noalloc" *)
+
 
 external tree_my_text : tree -> [`Tree] node -> [`Text ] node = "caml_xml_tree_my_text" "noalloc"
+external tree_my_text_unsafe : tree -> [`Tree] node -> [`Text ] node = "caml_xml_tree_my_text_unsafe" "noalloc"
 (*external tree_next_text : tree -> [`Tree] node -> [`Text ] node = "caml_xml_tree_next_text" "noalloc" *)
 external tree_doc_ids : tree -> [`Tree ] node -> [`Text ] node * [`Text ] node = "caml_xml_tree_doc_ids" 
 
@@ -96,6 +104,7 @@ external tree_node_xml_id : tree -> [`Tree ] node -> int = "caml_xml_tree_node_x
 external tree_is_ancestor : tree -> [`Tree ] node -> [`Tree ] node -> bool = "caml_xml_tree_is_ancestor" "noalloc" 
 external tree_tagged_desc : tree -> [`Tree ] node -> Tag.t -> [`Tree ] node = "caml_xml_tree_tagged_desc" "noalloc"
 external tree_tagged_foll_below : tree -> [`Tree ] node -> Tag.t -> [`Tree ] node -> [`Tree ] node = "caml_xml_tree_tagged_foll_below" "noalloc"
+external tree_tagged_foll_before : tree -> [`Tree ] node -> Tag.t -> [`Tree ] node -> [`Tree ] node = "caml_xml_tree_tagged_foll_before" "noalloc"
 external tree_subtree_tags : tree -> [`Tree ] node -> Tag.t -> int = "caml_xml_tree_subtree_tags" "noalloc"
 
 
@@ -108,6 +117,7 @@ external tree_select_child : tree -> [`Tree ] node -> unordered_set -> [`Tree] n
 external tree_select_foll_sibling : tree -> [`Tree ] node -> unordered_set -> [`Tree] node = "caml_xml_tree_select_foll_sibling" "noalloc"
 external tree_select_desc : tree -> [`Tree ] node -> unordered_set -> [`Tree] node = "caml_xml_tree_select_desc" "noalloc"
 external tree_select_foll_below : tree -> [`Tree ] node -> unordered_set -> [`Tree] node -> [`Tree] node = "caml_xml_tree_select_foll_below" "noalloc"
+external tree_select_foll_before : tree -> [`Tree ] node -> unordered_set -> [`Tree] node -> [`Tree] node = "caml_xml_tree_select_foll_before" "noalloc"
 
 
 module HPtset = Hashtbl.Make(Ptset.Int)
@@ -124,11 +134,9 @@ let ptset_to_vector s =
          HPtset.add vector_htbl s v; v
 
       
-type t = { 
-  doc : tree;            
-  ttable : (Tag.t,(Ptset.Int.t*Ptset.Int.t*Ptset.Int.t*Ptset.Int.t)) Hashtbl.t;
-}
+
 let subtree_size t i = tree_subtree_size t.doc i
+let subtree_elements t i = tree_subtree_elements t.doc i
 let text_size t = text_size t.doc
 
 module MemUnion = Hashtbl.Make (struct 
@@ -178,24 +186,28 @@ let collect_tags tree =
       Hashtbl.replace h t 
        (pt_cup sc schild,pt_cup sbelow sb, pt_cup ssibling ss, pt_cup safter sa)
   in
-  let rec loop_right id acc_sibling acc_after
+  let rec loop_right id acc_after 
     if  id == nil
-    then (acc_sibling,acc_after)
+    then Ptset.Int.empty,Ptset.Int.empty,acc_after
     else
-      let sibling2,after2 = loop_right (tree_next_sibling tree id) acc_sibling acc_after in
-      let child1,below1   = loop_left (tree_first_child tree id) after2  in
-      let tag = tree_tag_id tree id in
-       update tag child1 below1 sibling2 after2;
-       (pt_add tag sibling2, (pt_add tag (pt_cup after2 below1)))
+    let sibling2,desc2,after2 = loop_right (tree_next_sibling tree id) acc_after in
+    let child1,desc1,after1   = loop_left (tree_first_child tree id) after2  in
+    let tag = tree_tag_id tree id in
+    update tag child1 desc1 sibling2 after2;
+    ( pt_add tag sibling2, 
+      pt_add tag (pt_cup desc1 desc2),
+      pt_cup after1 (pt_cup desc1 desc2) )
   and loop_left id acc_after = 
-    if id == nil 
-    then (Ptset.Int.empty,Ptset.Int.empty)
+    if  id == nil
+    then Ptset.Int.empty,Ptset.Int.empty,acc_after
     else
-      let sibling2,after2 = loop_right (tree_next_sibling tree id) Ptset.Int.empty acc_after in
-      let child1,below1 = loop_left (tree_first_child tree id) after2 in
-      let tag = tree_tag_id tree id in
-       update tag child1 below1 sibling2 after2;
-       (pt_add tag sibling2,(pt_add tag (pt_cup after2 below1)))      
+    let sibling2,desc2,after2 = loop_right (tree_next_sibling tree id) acc_after in
+    let child1,desc1,after1   = loop_left (tree_first_child tree id) after2  in
+    let tag = tree_tag_id tree id in
+    update tag child1 desc1 sibling2 after2;
+    (pt_add tag sibling2, 
+     pt_add tag (pt_cup desc1 desc2),
+     acc_after )
   in
   let _ = loop_left (tree_root tree) Ptset.Int.empty in h
                          
@@ -288,7 +300,21 @@ let is_root t = t == root
 let node_of_t t  =
   let _ = Tag.init (Obj.magic t) in
   let table = collect_tags t 
+  in (*
+  let _ = Hashtbl.iter (fun t (c,d,ns,f) ->
+                         Printf.eprintf "Tag %s has:\n" (Tag.to_string t);
+                         Printf.eprintf "Child tags: ";
+                         Ptset.Int.iter (fun t -> Printf.eprintf "%s "(Tag.to_string t)) c;
+                         Printf.eprintf "\nDescendant tags: ";
+                         Ptset.Int.iter (fun t -> Printf.eprintf "%s "(Tag.to_string t)) d;
+                         Printf.eprintf "\nNextSibling tags: ";
+                         Ptset.Int.iter (fun t -> Printf.eprintf "%s "(Tag.to_string t)) ns;
+                         Printf.eprintf "\nFollowing tags: ";
+                         Ptset.Int.iter (fun t -> Printf.eprintf "%s "(Tag.to_string t)) f;
+                         Printf.eprintf "\n\n%!";) table
   in
+                         
+     *)                          
     { doc= t; 
       ttable = table;
     }
@@ -310,7 +336,7 @@ let parse_xml_string str =  parse parse_xml_string str
 external pool : tree -> Tag.pool = "%identity"
 
 let magic_string = "SXSI_INDEX"
-let version_string = "1"
+let version_string = "2"
 
 let pos fd =
   Unix.lseek fd 0  Unix.SEEK_CUR
@@ -358,6 +384,7 @@ let load ?(sample=64) str =
   let fd = Unix.openfile str [ Unix.O_RDONLY ] 0o644 in
   let in_c = Unix.in_channel_of_descr fd in
   let _ = set_binary_mode_in in_c true in
+  let load_table () = 
     (let ms = input_line in_c in if ms <> magic_string then failwith "Invalid index file");
     (let vs = input_line in_c in if vs <> version_string then failwith "Invalid version file");
     let table : (Tag.t,(Ptset.Int.t*Ptset.Int.t*Ptset.Int.t*Ptset.Int.t)) Hashtbl.t =
@@ -375,11 +402,15 @@ let load ?(sample=64) str =
       (* The in_channel read a chunk of fd, so we might be after
         the start of the XMLTree save file. Reset to the correct
         position *)
-      ignore(Unix.lseek fd (pos_in in_c) Unix.SEEK_SET);
-      let tree = { doc = tree_load fd;
-                  ttable = ntable;}
-      in close_in in_c;
-       tree
+      ntable
+  in
+  let _ = Printf.eprintf "\nLoading tag table : " in
+  let ntable = time (load_table) () in
+  ignore(Unix.lseek fd (pos_in in_c) Unix.SEEK_SET);
+  let tree = { doc = tree_load fd;
+              ttable = ntable;}
+  in close_in in_c;
+  tree
   
 
 
@@ -398,14 +429,23 @@ let dump_node t = nts (inode t)
 
 let is_left t n = tree_is_first_child t.doc n
 
+
+
 let is_below_right t n1 n2 = 
   tree_is_ancestor t.doc (tree_parent t.doc n1) n2 
   && not (tree_is_ancestor t.doc n1 n2)
+
+let is_binary_ancestor t n1 n2 =
+  let p = tree_parent t.doc n1 in
+  let fin = tree_closing t.doc p in
+  n2 > n1 && n2 < fin
+(*  (is_below_right t n1 n2) ||
+    (tree_is_ancestor t.doc n1 n2) *)
     
 let parent t n = tree_parent t.doc n
 
 let first_child t = (); fun n -> tree_first_child t.doc n
-let first_element t = (); fun n -> tree_first_element t.doc n
+let first_element t = (); fun n -> tree_first_element t n
 
 (* these function will be called in two times: first partial application
    on the tag, then application of the tag and the tree, then application of
@@ -419,7 +459,7 @@ let select_child t = fun ts ->
     fun n -> tree_select_child t.doc n v
 
 let next_sibling t = (); fun n ->  tree_next_sibling t.doc n
-let next_element t = (); fun n ->  tree_next_element t.doc n
+let next_element t = (); fun n ->  tree_next_element t n
 
 let tagged_sibling t tag = (); fun n -> tree_tagged_sibling t.doc n tag
 
@@ -428,7 +468,7 @@ let select_sibling t = fun ts ->
     fun n -> tree_select_foll_sibling t.doc n v
 
 let next_sibling_ctx t = (); fun n _ -> tree_next_sibling t.doc n
-let next_element_ctx t = (); fun n _ ->  tree_next_element t.doc n
+let next_element_ctx t = (); fun n _ ->  tree_next_element t n
 let tagged_sibling_ctx t tag = (); fun n  _ -> tree_tagged_sibling t.doc n tag
 
 let select_sibling_ctx t = fun ts -> 
@@ -451,6 +491,10 @@ let select_foll_ctx t = fun ts ->
   let v = (ptset_to_vector ts) in ();
     fun n ctx -> tree_select_foll_below t.doc n v ctx
 
+let closing t n = tree_closing t.doc n
+let is_open t n = tree_is_open t.doc n
+let get_text_id t n = tree_my_text t.doc n
+
 let last_idx = ref 0
 let array_find a i j =
   let l = Array.length a in
@@ -466,8 +510,92 @@ let array_find a i j =
 
 
   let count t s = text_count t.doc s
-
-  let print_xml_fast outc tree t =
+  let stack = ref []
+  let init_stack () = stack := []
+  let push x = stack:= x::!stack
+  let peek () = match !stack with 
+     p::_ -> p
+    | _ -> failwith "peek"
+  let pop () = match !stack with
+     p::r -> stack:=r; p
+    | _ -> failwith "pop"
+
+  let next t = nodei ( (inode t) + 1 ) 
+  let next2 t = nodei ( (inode t) + 2 ) 
+  let next3 t = nodei ( (inode t) + 3 ) 
+    
+  let print_xml_fast2  =
+    let _ = init_stack () in
+    let h = Hashtbl.create MED_H_SIZE in    
+    let tag_str t = try Hashtbl.find h t with
+       Not_found -> let s = Tag.to_string t in
+       Hashtbl.add h t s;s
+    in
+    let h_att = Hashtbl.create MED_H_SIZE in    
+    let att_str t = try Hashtbl.find h_att t with
+       Not_found -> let s = Tag.to_string t in
+      let attname = String.sub s 3 ((String.length s) -3) in
+      Hashtbl.add h_att t attname;attname
+    in fun outc tree t ->
+      let tree = tree.doc in
+      let fin = tree_closing tree t in
+      let rec loop_tag t tag =
+       if t <= fin then
+       if tree_is_open tree t then
+       (* opening tag *)
+       if tag == Tag.pcdata then 
+       begin
+         output_string outc (text_get_cached_text tree (tree_my_text_unsafe tree t));
+         loop (next2 t) (* skip closing $ *)
+       end
+       else
+       let tagstr = tag_str tag in
+       let _ = output_char outc '<';    
+       output_string outc tagstr in
+       let t' = next t in
+       if tree_is_open tree t' then
+       let _ = push tagstr in
+       let tag' = tree_tag_id tree t' in
+       if tag' == Tag.attribute then let t'' = loop_attr (next t') 0 in 
+       output_string outc ">"; loop t'' else (output_string outc ">";loop_tag t' tag')
+       else (* closing with no content *)
+       let _ = output_string outc "/>" in
+       loop (next t')
+       else
+       begin
+       (* closing tag *)
+         output_string outc "</";
+         output_string outc (pop());
+         output_char outc '>';
+         loop (next t);
+       end
+      and loop t = loop_tag t (tree_tag_id tree t)
+      and loop_attr t n = 
+       if tree_is_open tree t then 
+       let attname = att_str (tree_tag_id tree t) in
+       output_char outc ' ';
+       output_string outc attname;
+       output_string outc "=\"";
+       let t = next t in (* open $@ *)
+       output_string outc (text_get_cached_text tree (tree_my_text_unsafe tree t));
+       output_char outc '"';
+       loop_attr (next3 t) (n+1)
+       else
+       next t (* close @ *)
+      in loop t
+
+  let print_xml_fast  =
+    let h = Hashtbl.create MED_H_SIZE in    
+    let tag_str t = try Hashtbl.find h t with
+       Not_found -> let s = Tag.to_string t in
+       Hashtbl.add h t s;s
+    in
+    let h_att = Hashtbl.create MED_H_SIZE in    
+    let att_str t = try Hashtbl.find h_att t with
+       Not_found -> let s = Tag.to_string t in
+      let attname = String.sub s 3 ((String.length s) -3) in
+      Hashtbl.add h_att t attname;attname
+    in fun outc tree t ->
     let rec loop ?(print_right=true) t = 
       if t != nil 
       then 
@@ -475,20 +603,18 @@ let array_find a i j =
          if tagid==Tag.pcdata
          then 
            begin
-             let tid =  tree_my_text tree.doc t in
-             let _ = Printf.eprintf "my_text %i returned %i\n%!" (inode t) (inode tid)
-             in
+             let tid =  tree_my_text_unsafe tree.doc t in
              output_string outc (text_get_cached_text tree.doc tid);
              if print_right
              then loop (next_sibling tree t);
            end
          else
-           let tagstr = Tag.to_string tagid in
+           let tagstr = tag_str tagid in
            let l = first_child tree t 
            and r = next_sibling tree t 
            in
              output_char outc  '<';
-             output_string outc  tagstr;
+             output_string outc tagstr;
              if l == nil then output_string outc  "/>"
              else 
                if (tag tree l) == Tag.attribute then
@@ -516,12 +642,9 @@ let array_find a i j =
     and loop_attributes a = 
       if a != nil
       then
-      let s = (Tag.to_string (tag tree a)) in
-      let attname = String.sub s 3 ((String.length s) -3) in
+      let attname = att_str (tag tree a) in
       let fsa = first_child tree a in
-      let tid =  tree_my_text tree.doc fsa in
-      let _ = Printf.eprintf "my_text %i returned %i\n%!" (inode fsa) (inode tid)
-      in
+      let tid =  tree_my_text_unsafe tree.doc fsa in
        output_char outc ' ';
        output_string outc attname;
        output_string outc "=\"";
@@ -593,3 +716,4 @@ let dump_tree fmt tree =
 ;;
 
        
+let print_xml_fast3 t = tree_print_xml_fast3 t.doc