|
1 | 1 | open! Core
|
2 | 2 |
|
3 |
| - |
4 | 3 | let direct_file_destination ?(buffer_size = 4096 * 16) ~filename () =
|
5 | 4 | let buf = Iobuf.create ~len:buffer_size in
|
6 | 5 | let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_RDWR ] filename in
|
@@ -32,7 +31,121 @@ let direct_file_destination ?(buffer_size = 4096 * 16) ~filename () =
|
32 | 31 | (module Dest : Writer_intf.Destination)
|
33 | 32 | ;;
|
34 | 33 |
|
35 |
| -let file_destination ~filename () = direct_file_destination ~filename () |
| 34 | +(* While Zstandard has the best compression, perfetto does not yet understand the format. *) |
| 35 | +let zstd_file_destination ?(buffer_size = 64 * 1024) ~filename () = |
| 36 | + let buf = Iobuf.create ~len:buffer_size in |
| 37 | + let compression_level = 5 in |
| 38 | + (* Ensure the compression buffer is large enough for the worst case of an input of |
| 39 | + [buffer_size]. *) |
| 40 | + let compressed_buf = |
| 41 | + let len = |
| 42 | + buffer_size |
| 43 | + |> Int64.of_int |
| 44 | + |> Zstandard.compression_output_size_bound |
| 45 | + |> Int64.to_int_exn |
| 46 | + in |
| 47 | + Iobuf.create ~len |
| 48 | + in |
| 49 | + let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_CLOEXEC; O_RDWR ] filename in |
| 50 | + let written = ref 0 in |
| 51 | + let compression_context = Zstandard.Compression_context.create () in |
| 52 | + let flush () = |
| 53 | + Iobuf.rewind buf; |
| 54 | + Iobuf.advance buf !written; |
| 55 | + Iobuf.flip_lo buf; |
| 56 | + let input = |
| 57 | + Zstandard.Input.from_bigstring |
| 58 | + ~pos:(Iobuf.Expert.lo buf) |
| 59 | + ~len:(Iobuf.length buf) |
| 60 | + (Iobuf.Expert.buf buf) |
| 61 | + in |
| 62 | + let output = |
| 63 | + Zstandard.Output.in_buffer |
| 64 | + ~pos:(Iobuf.Expert.lo compressed_buf) |
| 65 | + ~len:(Iobuf.length compressed_buf) |
| 66 | + (Iobuf.Expert.buf compressed_buf) |
| 67 | + in |
| 68 | + let compressed_length = |
| 69 | + Zstandard.With_explicit_context.compress |
| 70 | + compression_context |
| 71 | + ~compression_level |
| 72 | + ~input |
| 73 | + ~output |
| 74 | + in |
| 75 | + Iobuf.advance compressed_buf compressed_length; |
| 76 | + Iobuf.flip_lo compressed_buf; |
| 77 | + Iobuf_unix.write compressed_buf file; |
| 78 | + written := 0; |
| 79 | + Iobuf.reset buf; |
| 80 | + Iobuf.reset compressed_buf |
| 81 | + in |
| 82 | + let module Dest = struct |
| 83 | + let next_buf ~ensure_capacity = |
| 84 | + flush (); |
| 85 | + if ensure_capacity > Iobuf.length buf |
| 86 | + then failwith "Not enough buffer space in [zstd_file_destination]"; |
| 87 | + buf |
| 88 | + ;; |
| 89 | + |
| 90 | + let wrote_bytes count = written := !written + count |
| 91 | + |
| 92 | + let close () = |
| 93 | + flush (); |
| 94 | + Zstandard.Compression_context.free compression_context; |
| 95 | + Core_unix.close file |
| 96 | + ;; |
| 97 | + end |
| 98 | + in |
| 99 | + (module Dest : Writer_intf.Destination) |
| 100 | +;; |
| 101 | + |
| 102 | +let gzip_file_destination ?(buffer_size = 64 * 1024) ~filename () = |
| 103 | + let buf = Iobuf.create ~len:buffer_size in |
| 104 | + let bytes = Bytes.create buffer_size in |
| 105 | + let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_CLOEXEC; O_RDWR ] filename in |
| 106 | + let out_channel = |
| 107 | + let oc = Core_unix.out_channel_of_descr file in |
| 108 | + (* Consider making the compression level an environment variable for |
| 109 | + experimentation. *) |
| 110 | + Gzip.open_out_chan ~level:6 oc |
| 111 | + in |
| 112 | + let written = ref 0 in |
| 113 | + let flush () = |
| 114 | + Iobuf.rewind buf; |
| 115 | + Iobuf.advance buf !written; |
| 116 | + Iobuf.flip_lo buf; |
| 117 | + Iobuf.Peek.To_bytes.blit |
| 118 | + ~src:(Iobuf.read_only buf) ~src_pos:0 ~dst:bytes ~dst_pos:0 ~len:!written; |
| 119 | + Gzip.output out_channel bytes 0 !written; |
| 120 | + written := 0; |
| 121 | + Iobuf.reset buf; |
| 122 | + in |
| 123 | + let module Dest = struct |
| 124 | + let next_buf ~ensure_capacity = |
| 125 | + flush (); |
| 126 | + if ensure_capacity > Iobuf.length buf |
| 127 | + then failwith "Not enough buffer space in [gzip_file_destination]"; |
| 128 | + buf |
| 129 | + ;; |
| 130 | + |
| 131 | + let wrote_bytes count = written := !written + count |
| 132 | + |
| 133 | + let close () = |
| 134 | + flush (); |
| 135 | + (* [close_out] also closes the underlying file descr. *) |
| 136 | + Gzip.close_out out_channel |
| 137 | + ;; |
| 138 | + end |
| 139 | + in |
| 140 | + (module Dest : Writer_intf.Destination) |
| 141 | +;; |
| 142 | + |
| 143 | +let file_destination ?(file_format = Writer_intf.File_format.Uncompressed) ~filename () = |
| 144 | + match file_format with |
| 145 | + | Uncompressed -> direct_file_destination ~filename () |
| 146 | + | Gzip -> gzip_file_destination ~filename () |
| 147 | + | Zstandard -> zstd_file_destination ~filename () |
| 148 | +;; |
36 | 149 |
|
37 | 150 | let iobuf_destination buf =
|
38 | 151 | (* We give out an [Iobuf] with a shared underlying [Bigstring] but different pointers
|
|
0 commit comments