Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T7046: add utilities for update of reference tree #33

Merged
merged 4 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 6 additions & 25 deletions src/config_diff.ml
Original file line number Diff line number Diff line change
Expand Up @@ -432,29 +432,10 @@ let union_of_values (n : Config_tree.t) (m : Config_tree.t) =
let set_m = ValueS.of_list (data_of m).values in
ValueS.elements (ValueS.union set_n set_m)

let union_of_children n m =
let set_n = ChildrenS.of_list (children_of n) in
let set_m = ChildrenS.of_list (children_of m) in
ChildrenS.elements (ChildrenS.union set_n set_m)

(* tree_union is currently used only for unit tests, so only values of data
are considered. Should there be a reason to expose it in the future,
consistency check and union of remaining data will need to be added.
*)
let rec tree_union s t =
let child_of_union s t c =
let s_c = Vytree.find s (name_of c) in
let t_c = Vytree.find t (name_of c) in
match s_c, t_c with
| Some child, None -> clone s t [(name_of child)]
| None, Some _ -> t
| Some u, Some v ->
if u ^~ v then
let values = union_of_values u v in
let data = {(data_of v) with Config_tree.values = values} in
Vytree.replace t (Vytree.make data (name_of v))
else
Vytree.replace t (tree_union u v)
| None, None -> raise Nonexistent_child
let tree_union s t =
let f u v =
let values = union_of_values u v in
let data = {(data_of v) with Config_tree.values = values} in
Vytree.make_full data (name_of v) (children_of v)
in
List.fold_left (fun x c -> child_of_union s x c) t (union_of_children s t)
Tree_alg.ConfigAlg.tree_union s t f
41 changes: 41 additions & 0 deletions src/generate.ml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,47 @@ let load_interface_definitions dir =
| Error msg -> Error msg end
with Bad_interface_definition msg -> Error msg

let interface_definitions_to_cache from_dir cache_path =
let ref_tree_result =
load_interface_definitions from_dir
in
let ref_tree =
match ref_tree_result with
| Ok ref -> ref
| Error msg -> raise (Load_error msg)
in
I.write_internal ref_tree cache_path

let reference_tree_cache_to_json cache_path render_file =
let ref_tree =
I.read_internal cache_path
in
let out = Reference_tree.render_json ref_tree in
let oc =
try
open_out render_file
with Sys_error msg -> raise (Write_error msg)
in
Printf.fprintf oc "%s" out;
close_out oc

let merge_reference_tree_cache cache_dir primary_name result_name =
let file_arr = Sys.readdir cache_dir in
let file_list' = Array.to_list file_arr in
let file_list =
List.filter (fun x -> x <> primary_name && x <> result_name) file_list' in
let file_path_list =
List.map (FilePath.concat cache_dir) file_list in
let primary_tree = I.read_internal (FilePath.concat cache_dir primary_name) in
let ref_trees = List.map I.read_internal file_path_list in
match ref_trees with
| [] ->
I.write_internal primary_tree (FilePath.concat cache_dir result_name)
| _ ->
let f _ v = v in
let res = List.fold_left (fun p r -> Tree_alg.RefAlg.tree_union r p f) primary_tree ref_trees in
I.write_internal res (FilePath.concat cache_dir result_name)

let reference_tree_to_json ?(internal_cache="") from_dir to_file =
let ref_tree_result =
load_interface_definitions from_dir
Expand Down
3 changes: 3 additions & 0 deletions src/generate.mli
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ exception Write_error of string

val load_interface_definitions : string -> (Reference_tree.t, string) result
val reference_tree_to_json : ?internal_cache:string -> string -> string -> unit
val interface_definitions_to_cache : string -> string -> unit
val reference_tree_cache_to_json : string -> string -> unit
val merge_reference_tree_cache : string -> string -> string -> unit
45 changes: 41 additions & 4 deletions src/internal.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
exception Read_error of string
exception Write_error of string

module type T =
sig
type t
Expand All @@ -10,20 +13,54 @@ module type FI = functor (M: T) ->
sig
val write_internal : M.t -> string -> unit
val read_internal : string -> M.t
val replace_internal : string -> string -> unit
end

module Make : FI = functor (M: T) -> struct
let write_internal x file_name =
let yt = M.to_yojson x in
let ys = Yojson.Safe.to_string yt in
let oc = open_out file_name in
Printf.fprintf oc "%s" ys; close_out oc
let fd = Unix.openfile file_name [Unix.O_CREAT;Unix.O_WRONLY] 0o664 in
let () =
try
Unix.lockf fd Unix.F_TLOCK 0
with _ ->
Unix.close fd; raise (Write_error "write lock unavailable")
in
let oc = Unix.out_channel_of_descr fd in
let () = Unix.ftruncate fd 0 in
let () = Printf.fprintf oc "%s" ys in
let () = Unix.fsync fd in
let () = Unix.lockf fd Unix.F_ULOCK 0 in
close_out_noerr oc

let read_internal file_name =
let ic = open_in file_name in
let fd =
try
Unix.openfile file_name [Unix.O_RDONLY] 0o664
with Unix.Unix_error (e,f,p) ->
let out =
Printf.sprintf "%s %s: %s" (Unix.error_message e) f p
in raise (Read_error out)
in
let () =
try
Unix.lockf fd Unix.F_TEST 0
with _ ->
Unix.close fd; raise (Read_error "read lock unavailable")
in
let ic = Unix.in_channel_of_descr fd in
let ys = really_input_string ic (in_channel_length ic) in
let yt = Yojson.Safe.from_string ys in
let ct_res = M.of_yojson yt in
let ct = Result.value ct_res ~default:M.default in
close_in ic; ct
close_in_noerr ic; ct

let replace_internal dst src =
let tmp = src ^ ".tmp" in
try
let () = FileUtil.cp ~force:Force [src] tmp in
let () = FileUtil.rm ~force:Force [dst] in
FileUtil.mv ~force:Force tmp dst
with _ -> raise (Write_error "replace error")
end
4 changes: 4 additions & 0 deletions src/internal.mli
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
exception Read_error of string
exception Write_error of string

module type T =
sig
type t
Expand All @@ -10,6 +13,7 @@ module type FI = functor (M : T) ->
sig
val write_internal : M.t -> string -> unit
val read_internal : string -> M.t
val replace_internal : string -> string -> unit
end

module Make : FI
68 changes: 68 additions & 0 deletions src/tree_alg.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
exception Incompatible_union
exception Nonexistent_child

module type Data = sig
type t
end

module type Tree = sig
module D: Data
type t = D.t Vytree.t
end

module Tree_impl (D: Data): Tree with module D = D = struct
module D = D
type t = D.t Vytree.t
end

module Alg (D: Data) (T: Tree with module D = D) = struct
module TreeOrd = struct
type t = T.t
let compare a b =
Util.lexical_numeric_compare (Vytree.name_of_node a) (Vytree.name_of_node b)
end
module SetT = Set.Make(TreeOrd)

let union_of_children n m =
let set_n = SetT.of_list (Vytree.children_of_node n) in
let set_m = SetT.of_list (Vytree.children_of_node m) in
SetT.elements (SetT.union set_n set_m)

let find_child n c = Vytree.find n (Vytree.name_of_node c)

let insert_child n c = Vytree.insert ~position:Vytree.Lexical ~children:(Vytree.children_of_node c) n [(Vytree.name_of_node c)] (Vytree.data_of_node c)

let replace_child n c =
Vytree.replace n c

let rec tree_union s t f =
if (Vytree.name_of_node s) <> (Vytree.name_of_node t) then
raise Incompatible_union
else
let child_of_union s t c =
let s_c = find_child s c in
let t_c = find_child t c in
match s_c, t_c with
| Some child, None ->
insert_child t child
| None, Some _ -> t
| Some u, Some v ->
if (Vytree.data_of_node u <> Vytree.data_of_node v) then
replace_child t (tree_union u (f u v) f)
else
replace_child t (tree_union u v f)
| None, None -> raise Nonexistent_child
in
List.fold_left (fun x c -> child_of_union s x c) t (union_of_children s t)
end

module ConfigData: Data with type t = Config_tree.config_node_data = struct
type t = Config_tree.config_node_data
end

module RefData: Data with type t = Reference_tree.ref_node_data = struct
type t = Reference_tree.ref_node_data
end

module ConfigAlg = Alg(ConfigData)(Tree_impl(ConfigData))
module RefAlg = Alg(RefData)(Tree_impl(RefData))