albatross/src/vmm_compress.ml

112 lines
5.0 KiB
OCaml

(* copied n 2018-03-18 from github.com:mirage/decompress.git (bin/easy.ml)
(MIT licensed) at fa1551b19165503fc77da6da99411fa59b6a7f6a by Hannes Mehnert
*)
(* edited to reflect later API (v0.9.0) changes on 2019-07-16 *)
module Stdlib_buffer = Buffer
open Decompress
(* Keep in your mind, this is an easy example of Decompress but not efficient.
Don't copy/paste this code in a productive environment. *)
let compress ?(level = 4) data =
let input_buffer = Bytes.create 0xFFFF in
(* We need to allocate an input buffer, the size of this buffer is important.
In fact, the Lz77 algorithm can find a pattern (and compress) only on this
input buffer. So if the input buffer is small, the algorithm has no chance
to find many patterns.
If it is big, the algorithm can find a far pattern and keep this pattern
as long as it tries to compress. The optimal size seems to be [1 << 15]
bytes (a bigger buffer is not necessary because the distance can be upper
than [1 << 15]). *)
let output_buffer = Bytes.create 0xFFFF in
(* We need to allocate an output buffer, is like you can. it's depends your
capabilities of your writing. *)
let pos = ref 0 in
let res = Stdlib_buffer.create (String.length data) in
(* The buffer is not a good idea. In fact, we can have a memory problem with
that (like if the output is too big). You need to keep in your mind that
is insecure to let a buffer to grow automatically (an attacker can use
this behaviour). *)
(* This is the same interface as [caml-zip]. A refiller and a flusher. The
refiller send you the maximum byte than you can [blit] inside the input
buffer.
So, if the second argument is [Some max], it's mandatory to respect that,
otherwise, you lost something. In the other case, you can blit the maximum
that what you can.
The flusher send you the output buffer and how many byte Decompress wrote
inside. The offset for this buffer is always [0]. Then, you need to send
how many bytes are free in the output buffer (and the common is that all
is free).
One argument (optionnal) is missing, it's the [meth]. This argument is
used to limit the memory used by the state internally. In fact, Decompress
(and `zlib`) need to keep all of your input to calculate at the end the
frequencies and the dictionary. So if you want to compress a big file, may
be you will have a memory problem (because, all your file will be present
in the memory). So you can specify a method to flush the internal memory
(with SYNC, PARTIAL or FULL - see the documentation about that) at each
[n] bytes, like: ~meth:(PARTIAL, 4096) flushes the internal memory when we
compute 4096 bytes of your input.
If [meth] is specified, the refiller has a [Some] as the second parameter.
Otherwise, it is [None]. *)
Zlib_deflate.bytes input_buffer output_buffer
(fun input_buffer -> function
| Some max ->
let n = min max (min 0xFFFF (String.length data - !pos)) in
Bytes.blit_string data !pos input_buffer 0 n ;
pos := !pos + n ;
n
| None ->
let n = min 0xFFFF (String.length data - !pos) in
Bytes.blit_string data !pos input_buffer 0 n ;
pos := !pos + n ;
n )
(fun output_buffer len ->
Stdlib_buffer.add_subbytes res output_buffer 0 len ;
0xFFFF )
(Zlib_deflate.default ~witness:Buffer.bytes level)
(* We can specify the level of the compression, see the documentation to know
what we use for each level. The default is 4. *)
|> function
| Ok _ -> Stdlib_buffer.contents res
| Error e ->
Logs.err (fun m -> m "error %a while compressing" Zlib_deflate.pp_error e) ;
invalid_arg "cannot compress"
let uncompress data =
let input_buffer = Bytes.create 0xFFFF in
(* We need to allocate an input buffer. it's depends your capabilities of
your reading. *)
let output_buffer = Bytes.create 0xFFFF in
(* Same as [compress]. *)
let window = Window.create ~crc:Window.adler32 ~witness:Buffer.bytes in
(* We allocate a window. We let the user to do that to reuse the window if
it's needed. In fact, the window is a big buffer ([size = (1 << 15)]) and
allocate this buffer costs.
So in this case, we decompress only one time but if you want to decompress
some flows, you can reuse this window after a [Window.reset]. *)
let pos = ref 0 in
let res = Stdlib_buffer.create (String.length data) in
Zlib_inflate.bytes input_buffer output_buffer
(* Same logic as [compress]. *)
(fun input_buffer ->
let n = min 0xFFFF (String.length data - !pos) in
Bytes.blit_string data !pos input_buffer 0 n ;
pos := !pos + n ;
n )
(fun output_buffer len ->
Stdlib_buffer.add_subbytes res output_buffer 0 len ;
0xFFFF )
(Zlib_inflate.default ~witness:Buffer.bytes window)
|> function
| Ok _ -> Ok (Stdlib_buffer.contents res)
| Error exn ->
Logs.err (fun m -> m "error %a while uncompressing" Zlib_inflate.pp_error exn) ;
Error ()