Skip to content

Commit 6477bd0

Browse files
committed
vhd-tool: Add read_headers_interval command
This command returns a more efficient representation of allocated clusters (when compared to read_headers), utilizing a sparse interval format instead of returning every single allocated cluster. This is the more efficient option, decreasing the filesize and memory usage in vhd-tool, but it's currently under a feature flag, so it's added as a new command instead of replacing read_headers immediately. Cram test for read_headers is still passing, so this refactoring has preserved the legacy format. Signed-off-by: Andrii Sultanov <andriy.sultanov@vates.tech>
1 parent 43514bd commit 6477bd0

5 files changed

Lines changed: 79 additions & 23 deletions

File tree

ocaml/libs/vhd/vhd_format/f.ml

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,24 +2903,10 @@ functor
29032903

29042904
let raw ?from (vhd : fd Vhd.t) = raw_common ?from vhd
29052905

2906-
let vhd_blocks_to_json (t : fd Vhd.t) =
2906+
let vhd_blocks_to_json_aux (t : fd Vhd.t) blocks =
29072907
let block_size_sectors_shift =
29082908
t.Vhd.header.Header.block_size_sectors_shift
29092909
in
2910-
let max_table_entries = Vhd.used_max_table_entries t in
2911-
2912-
let include_block = include_block None t in
2913-
2914-
let blocks =
2915-
Seq.init max_table_entries Fun.id
2916-
|> Seq.filter_map (fun i ->
2917-
if include_block i then
2918-
Some (`Int i)
2919-
else
2920-
None
2921-
)
2922-
|> List.of_seq
2923-
in
29242910
let json =
29252911
`Assoc
29262912
[
@@ -2934,6 +2920,52 @@ functor
29342920
let json_string = Yojson.to_string json in
29352921
print_string json_string ; return ()
29362922

2923+
let vhd_blocks_to_json (t : fd Vhd.t) =
2924+
let max_table_entries = Vhd.used_max_table_entries t in
2925+
let blocks =
2926+
Seq.init max_table_entries Fun.id
2927+
|> Seq.filter_map (fun i ->
2928+
if include_block None t i then
2929+
Some (`Int i)
2930+
else
2931+
None
2932+
)
2933+
|> List.of_seq
2934+
in
2935+
vhd_blocks_to_json_aux t blocks
2936+
2937+
let vhd_blocks_to_json_interval (t : fd Vhd.t) =
2938+
let max_table_entries = Vhd.used_max_table_entries t in
2939+
let blocks, last_block =
2940+
Seq.init max_table_entries Fun.id
2941+
|> Seq.fold_left
2942+
(fun (acc, left_block) i ->
2943+
if include_block None t i then
2944+
match left_block with
2945+
| Some _ ->
2946+
(acc, left_block)
2947+
| None ->
2948+
(acc, Some i)
2949+
else
2950+
match left_block with
2951+
| Some x ->
2952+
(`List [`Int x; `Int (i - 1)] :: acc, None)
2953+
| None ->
2954+
(acc, None)
2955+
)
2956+
([], None)
2957+
in
2958+
(* Close off the interval we were tracking we ran off the end of the seq *)
2959+
let blocks =
2960+
match last_block with
2961+
| Some x ->
2962+
`List [`Int x; `Int (max_table_entries - 1)] :: blocks
2963+
| None ->
2964+
blocks
2965+
in
2966+
let blocks = List.rev blocks in
2967+
vhd_blocks_to_json_aux t blocks
2968+
29372969
let vhd_common ?from ?raw ?(emit_batmap = false) (t : fd Vhd.t) =
29382970
let block_size_sectors_shift =
29392971
t.Vhd.header.Header.block_size_sectors_shift
@@ -3173,6 +3205,8 @@ functor
31733205
Vhd_input.vhd_common ?from ~raw vhd
31743206

31753207
let blocks_json = Vhd_input.vhd_blocks_to_json
3208+
3209+
let blocks_json_interval = Vhd_input.vhd_blocks_to_json_interval
31763210
end
31773211

31783212
(* Create a VHD stream from data on t, using `include_block` guide us which blocks have data *)

ocaml/libs/vhd/vhd_format/f.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ module From_file : functor (F : S.FILE) -> sig
470470
[from] into [t] *)
471471

472472
val blocks_json : fd Vhd.t -> unit t
473+
474+
val blocks_json_interval : fd Vhd.t -> unit t
473475
end
474476

475477
module Raw_input : sig

ocaml/vhd-tool/cli/main.ml

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -385,19 +385,34 @@ let stream_cmd =
385385
, Cmd.info "stream" ~sdocs:_common_options ~doc ~man
386386
)
387387

388+
let vhd_source =
389+
let doc = Printf.sprintf "Path to the VHD file" in
390+
Arg.(required & pos 0 (some file) None & info [] ~doc)
391+
388392
let read_headers_cmd =
389393
let doc =
390394
{|Parse VHD headers and output allocated blocks information in JSON format \
391395
like: {"virtual_size": X, "cluster_bits": X, "data_clusters": [1,2,3]}|}
392396
in
393-
let source =
394-
let doc = Printf.sprintf "Path to the VHD file" in
395-
Arg.(required & pos 0 (some file) None & info [] ~doc)
396-
in
397-
( Term.(ret (const Impl.read_headers $ common_options_t $ source))
397+
( Term.(
398+
ret
399+
(const (Impl.read_headers ~legacy:true) $ common_options_t $ vhd_source)
400+
)
398401
, Cmd.info "read_headers" ~sdocs:_common_options ~doc
399402
)
400403

404+
let read_headers_interval_cmd =
405+
let doc =
406+
{|Parse VHD headers and output allocated blocks intervals information in JSON format \
407+
like: {"virtual_size": X, "cluster_bits": X, "data_clusters": [[1,13],[17,17],[19,272]]|}
408+
in
409+
( Term.(
410+
ret
411+
(const (Impl.read_headers ~legacy:false) $ common_options_t $ vhd_source)
412+
)
413+
, Cmd.info "read_headers_interval" ~sdocs:_common_options ~doc
414+
)
415+
401416
let cmds =
402417
[
403418
info_cmd
@@ -408,6 +423,7 @@ let cmds =
408423
; serve_cmd
409424
; stream_cmd
410425
; read_headers_cmd
426+
; read_headers_interval_cmd
411427
]
412428
|> List.map (fun (t, i) -> Cmd.v i t)
413429

ocaml/vhd-tool/src/impl.ml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,11 +1168,15 @@ let stream_t common args ?(progress = no_progress_bar) () =
11681168
args.StreamCommon.tar_filename_prefix args.StreamCommon.good_ciphersuites
11691169
args.StreamCommon.verify_cert
11701170

1171-
let read_headers common source =
1171+
let read_headers common source ~legacy =
11721172
let path = [Filename.dirname source] in
11731173
let thread =
11741174
retry common 3 (fun () -> Vhd_IO.openchain ~path source false) >>= fun t ->
1175-
Vhd_IO.close t >>= fun () -> Hybrid_input.blocks_json t
1175+
Vhd_IO.close t >>= fun () ->
1176+
if legacy then
1177+
Hybrid_input.blocks_json t
1178+
else
1179+
Hybrid_input.blocks_json_interval t
11761180
in
11771181
Lwt_main.run thread ; `Ok ()
11781182

ocaml/vhd-tool/src/impl.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ val stream :
3636
Common.t -> StreamCommon.t -> [> `Error of bool * string | `Ok of unit]
3737

3838
val read_headers :
39-
Common.t -> string -> [> `Error of bool * string | `Ok of unit]
39+
Common.t -> string -> legacy:bool -> [> `Error of bool * string | `Ok of unit]
4040

4141
val serve :
4242
Common.t

0 commit comments

Comments
 (0)