Created
February 16, 2017 14:52
-
-
Save dinosaure/6ce1b6b2c4c14e1e4e0f970c35bcc429 to your computer and use it in GitHub Desktop.
Zlib in OCaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* adler32.c -- compute the Adler-32 checksum of a data stream | |
Copyright (C) 1995-2011 Mark Adler | |
This software is provided 'as-is', without any express or implied | |
warranty. In no event will the authors be held liable for any damages | |
arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not | |
claim that you wrote the original software. If you use this software | |
in a product, an acknowledgment in the product documentation would be | |
appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be | |
misrepresented as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
Jean-loup Gailly Mark Adler | |
jloup@gzip.org madler@alumni.caltech.edu | |
The data format used by the zlib library is described by RFCs (Request for | |
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 | |
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). | |
*/ | |
#include <caml/mlvalues.h> | |
#include <caml/bigarray.h> | |
#include <caml/memory.h> | |
#include <caml/alloc.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#define BASE 65521 /* largest prime smaller than 65536 */ | |
#define NMAX 5552 | |
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ | |
#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} | |
#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); | |
#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); | |
#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); | |
#define DO16(buf) DO8(buf,0); DO8(buf,8); | |
/* use NO_DIVIDE if your processor does not do division in hardware -- | |
try it both ways to see which is faster */ | |
#ifdef NO_DIVIDE | |
/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 | |
(thank you to John Reiser for pointing this out) */ | |
# define CHOP(a) \ | |
do { \ | |
unsigned long tmp = a >> 16; \ | |
a &= 0xffffUL; \ | |
a += (tmp << 4) - tmp; \ | |
} while (0) | |
# define MOD28(a) \ | |
do { \ | |
CHOP(a); \ | |
if (a >= BASE) a -= BASE; \ | |
} while (0) | |
# define MOD(a) \ | |
do { \ | |
CHOP(a); \ | |
MOD28(a); \ | |
} while (0) | |
# define MOD63(a) \ | |
do { /* this assumes a is not negative */ \ | |
z_off64_t tmp = a >> 32; \ | |
a &= 0xffffffffL; \ | |
a += (tmp << 8) - (tmp << 5) + tmp; \ | |
tmp = a >> 16; \ | |
a &= 0xffffL; \ | |
a += (tmp << 4) - tmp; \ | |
tmp = a >> 16; \ | |
a &= 0xffffL; \ | |
a += (tmp << 4) - tmp; \ | |
if (a >= BASE) a -= BASE; \ | |
} while (0) | |
#else | |
# define MOD(a) a %= BASE | |
# define MOD28(a) a %= BASE | |
# define MOD63(a) a %= BASE | |
#endif | |
unsigned long | |
adler32(adler, buf, len) | |
unsigned long adler; | |
const unsigned char *buf; | |
unsigned int len; | |
{ | |
unsigned long sum2; | |
unsigned n; | |
/* split Adler-32 into component sums */ | |
sum2 = (adler >> 16) & 0xffff; | |
adler &= 0xffff; | |
/* in case user likes doing a byte at a time, keep it fast */ | |
if (len == 1) { | |
adler += buf[0]; | |
if (adler >= BASE) | |
adler -= BASE; | |
sum2 += adler; | |
if (sum2 >= BASE) | |
sum2 -= BASE; | |
return adler | (sum2 << 16); | |
} | |
/* initial Adler-32 value (deferred check for len == 1 speed) */ | |
if (buf == NULL) | |
return 1L; | |
/* in case short lengths are provided, keep it somewhat fast */ | |
if (len < 16) { | |
while (len--) { | |
adler += *buf++; | |
sum2 += adler; | |
} | |
if (adler >= BASE) | |
adler -= BASE; | |
MOD28(sum2); /* only added so many BASE's */ | |
return adler | (sum2 << 16); | |
} | |
/* do length NMAX blocks -- requires just one modulo operation */ | |
while (len >= NMAX) { | |
len -= NMAX; | |
n = NMAX / 16; /* NMAX is divisible by 16 */ | |
do { | |
DO16(buf); /* 16 sums unrolled */ | |
buf += 16; | |
} while (--n); | |
MOD(adler); | |
MOD(sum2); | |
} | |
/* do remaining bytes (less than NMAX, still just one modulo) */ | |
if (len) { /* avoid modulos if none remaining */ | |
while (len >= 16) { | |
len -= 16; | |
DO16(buf); | |
buf += 16; | |
} | |
while (len--) { | |
adler += *buf++; | |
sum2 += adler; | |
} | |
MOD(adler); | |
MOD(sum2); | |
} | |
/* return recombined sums */ | |
return adler | (sum2 << 16); | |
} | |
CAMLprim value bytes_adler32(value val_adler32, value val_buf, value val_ofs, value val_len) | |
{ | |
return caml_copy_int32(adler32(Int32_val(val_adler32), &Byte(String_val(val_buf), Long_val(val_ofs)), Long_val(val_len))); | |
} | |
CAMLprim value bigstring_adler32(value val_adler32, value val_buf, value val_ofs, value val_len) | |
{ | |
return caml_copy_int32(adler32(Int32_val(val_adler32), (char *)Caml_ba_array_val(val_buf)->data + Long_val(val_ofs), Long_val(val_len))); | |
} | |
unsigned long | |
adler32_combine_(adler1, adler2, len2) | |
unsigned long adler1; | |
unsigned long adler2; | |
int64_t len2; | |
{ | |
unsigned long sum1; | |
unsigned long sum2; | |
unsigned rem; | |
/* for negative len, return invalid adler32 as a clue for debugging */ | |
if (len2 < 0) | |
return 0xffffffffUL; | |
/* the derivation of this formula is left as an exercise for the reader */ | |
MOD63(len2); /* assumes len2 >= 0 */ | |
rem = (unsigned)len2; | |
sum1 = adler1 & 0xffff; | |
sum2 = rem * sum1; | |
MOD(sum2); | |
sum1 += (adler2 & 0xffff) + BASE - 1; | |
sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; | |
if (sum1 >= BASE) sum1 -= BASE; | |
if (sum1 >= BASE) sum1 -= BASE; | |
if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); | |
if (sum2 >= BASE) sum2 -= BASE; | |
return sum1 | (sum2 << 16); | |
} | |
unsigned long | |
adler32_combine(adler1, adler2, len2) | |
unsigned long adler1; | |
unsigned long adler2; | |
off_t len2; | |
{ | |
return adler32_combine_(adler1, adler2, len2); | |
} | |
unsigned long | |
adler32_combine64(adler1, adler2, len2) | |
unsigned long adler1; | |
unsigned long adler2; | |
int64_t len2; | |
{ | |
return adler32_combine_(adler1, adler2, len2); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Bigstring = | |
struct | |
open Bigarray | |
type t = (char, int8_unsigned_elt, c_layout) Array1.t | |
let length = Array1.dim | |
let create = Array1.create Char c_layout | |
let get = Array1.get | |
let set = Array1.set | |
let sub = Array1.sub | |
let fill = Array1.fill | |
let blit = Array1.blit | |
let copy v = | |
let v' = create (length v) in | |
Array1.blit v v'; v' | |
external get_u16 : t -> int -> int = "%caml_bigstring_get16u" | |
external get_u32 : t -> int -> Int32.t = "%caml_bigstring_get32u" | |
external get_u64 : t -> int -> Int64.t = "%caml_bigstring_get64u" | |
let to_string v = | |
let buf = Bytes.create (length v) in | |
for i = 0 to length v - 1 | |
do Bytes.set buf i (get v i) done; | |
Bytes.unsafe_to_string buf | |
(** See [bs.c]. *) | |
external blit | |
: t -> int -> t -> int -> int -> unit | |
= "bigstring_memcpy" [@@noalloc] | |
let pp fmt ba = | |
for i = 0 to length ba - 1 | |
do match get ba i with | |
| '\000' .. '\031' | '\127' -> Format.pp_print_char fmt '.' | |
| chr -> Format.pp_print_char fmt chr | |
done | |
let tpp fmt ba = | |
for i = 0 to length ba - 1 | |
do Format.pp_print_char fmt (get ba i) done | |
let rec _index bs len off chr = | |
if off >= len | |
then raise Not_found | |
else if get bs off = chr then off | |
else _index bs len (off + 1) chr | |
let index_from bs off len chr = | |
if off < 0 || off > len | |
then raise (Invalid_argument "Bigstring.index_from") | |
else _index bs len off chr | |
let empty = create 0 | |
end | |
module Bytes = | |
struct | |
include Bytes | |
external get_u16 : t -> int -> int = "%caml_string_get16u" | |
external get_u32 : t -> int -> Int32.t = "%caml_string_get32u" | |
external get_u64 : t -> int -> Int64.t = "%caml_string_get64u" | |
external blit | |
: t -> int -> t -> int -> int -> unit | |
= "bytes_memcpy" [@@noalloc] | |
(** See [bs.c]. *) | |
let pp fmt bs = | |
for i = 0 to length bs - 1 | |
do match get bs i with | |
| '\000' .. '\031' | '\127' -> Format.pp_print_char fmt '.' | |
| chr -> Format.pp_print_char fmt chr | |
done | |
let tpp fmt bs = | |
for i = 0 to length bs - 1 | |
do Format.pp_print_char fmt (get bs i) done | |
let blit src src_off dst dst_off len = | |
if len < 0 || src_off < 0 || src_off > Bytes.length src - len | |
|| dst_off < 0 || dst_off > Bytes.length dst - len | |
then raise (Invalid_argument (Format.sprintf "Bytes.blit (src: %d:%d, \ | |
dst: %d:%d, \ | |
len: %d)" | |
src_off (Bytes.length src) | |
dst_off (Bytes.length dst) | |
len)) | |
else blit src src_off dst dst_off len | |
let rec _index bs len off chr = | |
if off >= len | |
then raise Not_found | |
else if get bs off = chr then off | |
else _index bs len (off + 1) chr | |
let index_from bs off len chr = | |
if off < 0 || off > len | |
then raise (Invalid_argument "Bigstring.index_from") | |
else _index bs len off chr | |
end | |
(* mandatory for a GADT *) | |
type st = St | |
type bs = Bs | |
type 'a t = | |
| Bytes : Bytes.t -> st t | |
| Bigstring : Bigstring.t -> bs t | |
let from_bytes v = Bytes v | |
let from_bigstring v = Bigstring v | |
let from | |
: type a. proof:a t -> int -> a t | |
= fun ~proof len -> match proof with | |
| Bytes v -> Bytes (Bytes.create len) | |
| Bigstring v -> Bigstring (Bigstring.create len) | |
let length : type a. a t -> int = function | |
| Bytes v -> Bytes.length v | |
| Bigstring v -> Bigstring.length v | |
let get : type a. a t -> int -> char = fun v idx -> match v with | |
| Bytes v -> Bytes.get v idx | |
| Bigstring v -> Bigstring.get v idx | |
let set : type a. a t -> int -> char -> unit = fun v idx chr -> match v with | |
| Bytes v -> Bytes.set v idx chr | |
| Bigstring v -> Bigstring.set v idx chr | |
let get_u16 : type a. a t -> int -> int = fun v idx -> match v with | |
| Bytes v -> Bytes.get_u16 v idx | |
| Bigstring v -> Bigstring.get_u16 v idx | |
let get_u32 : type a. a t -> int -> Int32.t = fun v idx -> match v with | |
| Bytes v -> Bytes.get_u32 v idx | |
| Bigstring v -> Bigstring.get_u32 v idx | |
let get_u64 : type a. a t -> int -> Int64.t = fun v idx -> match v with | |
| Bytes v -> Bytes.get_u64 v idx | |
| Bigstring v -> Bigstring.get_u64 v idx | |
let sub : type a. a t -> int -> int -> a t = fun v off len -> match v with | |
| Bytes v -> Bytes.sub v off len |> from_bytes | |
| Bigstring v -> Bigstring.sub v off len |> from_bigstring | |
let fill | |
: type a. a t -> int -> int -> char -> unit | |
= fun v off len chr -> match v with | |
| Bytes v -> Bytes.fill v off len chr | |
| Bigstring v -> Bigstring.fill (Bigstring.sub v off len) chr | |
let blit : type a. a t -> int -> a t -> int -> int -> unit = | |
fun src src_idx dst dst_idx len -> match src, dst with | |
| Bytes src, Bytes dst -> | |
Bytes.blit src src_idx dst dst_idx len | |
| Bigstring src, Bigstring dst -> | |
Bigstring.blit src src_idx dst dst_idx len | |
let pp : type a. Format.formatter -> a t -> unit = fun fmt -> function | |
| Bytes v -> Format.fprintf fmt "%a" Bytes.pp v | |
| Bigstring v -> Format.fprintf fmt "%a" Bigstring.pp v | |
let tpp : type a. Format.formatter -> a t -> unit = fun fmt -> function | |
| Bytes v -> Format.fprintf fmt "%a" Bytes.tpp v | |
| Bigstring v -> Format.fprintf fmt "%a" Bigstring.tpp v | |
let to_string : type a. a t -> string = function | |
| Bytes v -> Bytes.unsafe_to_string v | |
| Bigstring v -> Bigstring.to_string v | |
external st_adler32 : int32 -> Bytes.t -> int -> int -> int32 = | |
"bytes_adler32" | |
external bs_adler32 : int32 -> Bigstring.t -> int -> int -> int32 = | |
"bigstring_adler32" | |
let adler32 : type a. a t -> int32 -> int -> int -> int32 = function | |
| Bytes v -> fun i off len -> st_adler32 i v off len | |
| Bigstring v -> fun i off len -> bs_adler32 i v off len | |
let empty : type a. a t -> a t = function | |
| Bytes v -> Bytes Bytes.empty | |
| Bigstring v -> Bigstring Bigstring.empty | |
let index_from : type a. a t -> int -> ?len:int -> char -> int = function | |
| Bytes v -> | |
fun off ?(len = Bytes.length v) chr -> | |
Bytes.index_from v off len chr | |
| Bigstring v -> | |
fun off ?(len = Bigstring.length v) chr -> | |
Bigstring.index_from v off len chr | |
let count_while v ?(init = 0) predicate = | |
let i = ref init in | |
let l = length v in | |
while !i < l && predicate (get v !i) do incr i done; | |
!i - init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <caml/mlvalues.h> | |
#include <caml/unixsupport.h> | |
#include <caml/bigarray.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
CAMLprim value bigstring_read(value val_fd, value val_buf, value val_ofs, value val_len) | |
{ | |
long ret; | |
ret = read(Int_val(val_fd), (char*)Caml_ba_array_val(val_buf)->data + Long_val(val_ofs), Long_val(val_len)); | |
if (ret == -1) uerror("read", Nothing); | |
return Val_long(ret); | |
} | |
CAMLprim value bigstring_write(value val_fd, value val_buf, value val_ofs, value val_len) | |
{ | |
long ret; | |
ret = write(Int_val(val_fd), (char*)Caml_ba_array_val(val_buf)->data + Long_val(val_ofs), Long_val(val_len)); | |
if (ret == -1) uerror("write", Nothing); | |
return Val_long(ret); | |
} | |
void | |
z_memcpy(char *dst, const char *src, size_t len) | |
{ | |
if (len == 0) return; | |
do { | |
*dst++ = *src++; | |
} while (--len != 0); | |
} | |
CAMLprim value bigstring_memcpy(value val_src, value val_src_off, value val_dst, value val_dst_off, value val_len) | |
{ | |
z_memcpy((char *)Caml_ba_array_val(val_dst)->data + Long_val(val_dst_off), (char *)Caml_ba_array_val(val_src)->data + Long_val(val_src_off), Long_val(val_len)); | |
return Val_unit; | |
} | |
CAMLprim value bytes_memcpy(value val_src, value val_src_off, value val_dst, value val_dst_off, value val_len) | |
{ | |
z_memcpy(&Byte(String_val(val_dst), Long_val(val_dst_off)), &Byte(String_val(val_src), Long_val(val_src_off)), Long_val(val_len)); | |
return Val_unit; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let repeat atm = | |
let atm = Char.code atm |> Int64.of_int in | |
let ( lor ) = Int64.logor in | |
let ( lsl ) = Int64.shift_left in | |
atm | |
lor (atm lsl 8) | |
lor (atm lsl 16) | |
lor (atm lsl 24) | |
lor (atm lsl 32) | |
lor (atm lsl 40) | |
lor (atm lsl 48) | |
lor (atm lsl 56) | |
(** (imperative) Heap implementation *) | |
module Heap = | |
struct | |
type t = | |
{ mutable buffer : int array | |
; mutable length : int } | |
let make size = | |
{ buffer = Array.make (size * 2) 0 | |
; length = 0 } | |
let get_parent i = ((i - 2) / 4) * 2 | |
let get_child i = 2 * i + 2 | |
exception Break | |
let push index value ({ buffer; length; } as heap) = | |
let swap i j = | |
let t = buffer.(i) in | |
buffer.(i) <- buffer.(j); | |
buffer.(j) <- t | |
in | |
buffer.(length) <- value; | |
buffer.(length + 1) <- index; | |
let current = ref length in | |
begin | |
try | |
while !current > 0 | |
do let parent = get_parent !current in | |
if buffer.(!current) > buffer.(parent) | |
then begin | |
swap !current parent; | |
swap (!current + 1) (parent + 1); | |
current := parent | |
end else raise Break | |
done | |
with Break -> () | |
end; | |
heap.length <- length + 2 | |
let pop ({ buffer; length; } as heap) = | |
let swap i j = | |
let t = buffer.(i) in | |
buffer.(i) <- buffer.(j); | |
buffer.(j) <- t | |
in | |
let value = buffer.(0) in | |
let index = buffer.(1) in | |
heap.length <- length - 2; | |
buffer.(0) <- buffer.(heap.length); | |
buffer.(1) <- buffer.(heap.length + 1); | |
let parent = ref 0 in | |
begin | |
try | |
while true | |
do let current = get_child !parent in | |
if current >= heap.length | |
then raise Break; | |
let current = | |
if current + 2 < heap.length | |
&& buffer.(current + 2) > buffer.(current) | |
then current + 2 | |
else current | |
in | |
if buffer.(current) > buffer.(!parent) | |
then begin | |
swap current !parent; | |
swap (current + 1) (!parent + 1) | |
end else raise Break; | |
parent := current | |
done | |
with Break -> () | |
end; | |
(index, value) | |
let length { length; _ } = length | |
end | |
(* Convenience function to create a canonic Huffman tree *) | |
module T = | |
struct | |
(** Compute the optimal bit lengths for a tree. | |
[p] must be sorted by increasing frequency. | |
*) | |
let reverse_package_merge p n limit = | |
let minimum_cost = Array.make limit 0 in | |
let flag = Array.make limit 0 in | |
let code_length = Array.make n limit in | |
let current_position = Array.make limit 0 in | |
let excess = ref ((1 lsl limit) - n) in | |
let half = (1 lsl (limit - 1)) in | |
minimum_cost.(limit - 1) <- n; | |
for j = 0 to limit - 1 do | |
if !excess < half | |
then flag.(j) <- 0 | |
else | |
begin | |
flag.(j) <- 1; | |
excess := !excess - half; | |
end; | |
excess := !excess lsl 1; | |
if limit - 2 - j >= 0 | |
then minimum_cost.(limit - 2 - j) <- | |
(minimum_cost.(limit - 1 - j) / 2) + n; | |
done; | |
minimum_cost.(0) <- flag.(0); | |
let value = Array.init limit | |
(function | |
| 0 -> Array.make minimum_cost.(0) 0 | |
| j -> | |
begin | |
if minimum_cost.(j) > 2 * minimum_cost.(j - 1) + flag.(j) | |
then minimum_cost.(j) <- 2 * minimum_cost.(j - 1) + flag.(j); | |
Array.make minimum_cost.(j) 0 | |
end) | |
in | |
let ty = Array.init limit (fun j -> Array.make minimum_cost.(j) 0) in | |
(* Decrease codeword lengths indicated by the first element in [ty.(j)], | |
recursively accessing other lists if that first element is a package. *) | |
let rec take_package j = | |
let x = ty.(j).(current_position.(j)) in | |
if x = n | |
then | |
begin | |
take_package (j + 1); | |
take_package (j + 1); | |
end | |
else code_length.(x) <- code_length.(x) - 1; | |
(* remove and discard the first elements of queues | |
[value.(j)] and [ty.(j)]. *) | |
current_position.(j) <- current_position.(j) + 1 | |
in | |
for t = 0 to minimum_cost.(limit - 1) - 1 do | |
value.(limit - 1).(t) <- p.(t); | |
ty.(limit - 1).(t) <- t; | |
done; | |
if flag.(limit - 1) = 1 then begin | |
code_length.(0) <- code_length.(0) - 1; | |
current_position.(limit - 1) <- current_position.(limit - 1) + 1; | |
end; | |
for j = limit - 2 downto 0 do | |
let i = ref 0 in | |
let next = ref current_position.(j + 1) in | |
for t = 0 to minimum_cost.(j) - 1 do | |
let weight = | |
if !next + 1 < minimum_cost.(j + 1) | |
then value.(j + 1).(!next) + value.(j + 1).(!next + 1) | |
else p.(!i) | |
in | |
if weight > p.(!i) | |
then begin | |
value.(j).(t) <- weight; | |
ty.(j).(t) <- n; | |
next := !next + 2; | |
end else begin | |
value.(j).(t) <- p.(!i); | |
ty.(j).(t) <- !i; | |
incr i; | |
end | |
done; | |
current_position.(j) <- 0; | |
if flag.(j) = 1 then take_package j; | |
done; | |
code_length | |
exception OK | |
let get_lengths freqs limit = | |
let length = Array.make (Array.length freqs) 0 in | |
begin | |
let heap = Heap.make (2 * 286) in | |
let max_code = ref (-1) in | |
(* Construct the initial heap, with the least frequent element in | |
heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. | |
heap[0] is not used. See implementation in Heap module. *) | |
Array.iteri | |
(fun i freq -> if freq > 0 then (max_code := i; Heap.push i freq heap)) | |
freqs; | |
try | |
(* The pkzip format requires that at least one distance code exists, | |
and that at least one bit should be sent even if there is only one | |
possible code. So to avoid special checks later on we force at least | |
two codes of non zero frequency. *) | |
while Heap.length heap / 2 < 2 do | |
Heap.push (if !max_code < 2 then !max_code + 1 else 0) 1 heap; | |
if !max_code < 2 then incr max_code; | |
done; | |
let nodes = Array.make (Heap.length heap / 2) (0, 0) in | |
let values = Array.make (Heap.length heap / 2) 0 in | |
if Array.length nodes = 1 | |
then begin | |
let index, value = Heap.pop heap in | |
length.(index) <- 1; | |
raise OK | |
end; | |
(* The elements heap[length / 2 + 1 .. length] are leaves of the tree, | |
establish sub-heaps of increasing lengths: *) | |
for i = 0 to Heap.length heap / 2 - 1 | |
do nodes.(i) <- Heap.pop heap; | |
values.(i) <- nodes.(i) |> snd; | |
done; | |
(* We can now generate the bit lengths. *) | |
let code_length = | |
reverse_package_merge | |
values | |
(Array.length values) | |
limit | |
in | |
Array.iteri | |
(fun i (index, _) -> | |
length.(index) <- code_length.(i)) | |
nodes | |
with OK -> () | |
end; | |
length | |
let get_codes_from_lengths ?(max_code_length = 16) lengths = | |
let count = Array.make (max_code_length + 1) 0 in | |
let start_code = Array.make (max_code_length + 1) 0 in | |
let codes = Array.make (Array.length lengths) 0 in | |
Array.iter | |
(fun length -> count.(length) <- count.(length) + 1) | |
lengths; | |
let code = ref 0 in | |
for i = 1 to max_code_length do | |
start_code.(i) <- !code; | |
code := !code + count.(i); | |
code := !code lsl 1; | |
done; | |
for i = 0 to Array.length lengths - 1 do | |
code := start_code.(lengths.(i)); | |
start_code.(lengths.(i)) <- start_code.(lengths.(i)) + 1; | |
for j = 0 to lengths.(i) - 1 do | |
codes.(i) <- (codes.(i) lsl 1) lor (!code land 1); | |
code := !code lsr 1; | |
done; | |
done; | |
codes | |
end | |
(** definition of [Hunk] *) | |
module Hunk = | |
struct | |
type t = | |
| Match of (int * int) | |
| Literal of char | |
end | |
(** non-blocking and functionnal implementation of Lz77 *) | |
module L = | |
struct | |
type error = .. | |
type error += Invalid_level of int | |
let pp_error fmt = function | |
| Invalid_level level -> Format.fprintf fmt "(Invalid_level %d)" level | |
exception Match of int * int | |
exception Literal of char | |
exception Break | |
type 'i t = | |
{ i_off : int | |
; i_pos : int | |
; i_len : int | |
; level : int | |
; on : Hunk.t -> unit | |
; state : 'i state } | |
and 'i state = | |
| Deflate of int | |
| Deffast of int | |
| Choose of int | |
| Exception of error | |
and 'i res = | |
| Cont of 'i t | |
| Wait of 'i t * Hunk.t Seq.t | |
| Error of 'i t * error | |
(** XXX: we don't have an [Ok] result because this algorithm does not decide | |
if you need to stop the compression or not - this is decided by the | |
user. It's illogic to force a [`End] state with this algorithm. *) | |
let pp_state fmt = function | |
| Deflate wbits -> Format.fprintf fmt "(Deflate wbits:%d)" wbits | |
| Deffast wbits -> Format.fprintf fmt "(Deffast wbits:%d)" wbits | |
| Choose wbits -> Format.fprintf fmt "(Choose wbits:%d)" wbits | |
| Exception exn -> Format.fprintf fmt "(Exception %a)" pp_error exn | |
let pp fmt { i_off; i_pos; i_len | |
; level | |
; on | |
; state } = | |
Format.fprintf fmt "{@[<hov>i_off = %d;@ \ | |
i_pos = %d;@ \ | |
i_len = %d;@ \ | |
level = %d;@ \ | |
on = #fun;@ \ | |
state = %a@]}" | |
i_off i_pos i_len | |
level | |
pp_state state | |
let await t lst = Wait (t, lst) | |
let error t exn = Error ({ t with state = Exception exn }, exn) | |
type key = Int32.t option | |
let key src idx len : key = | |
if idx < len - 3 | |
then Some (Safe.get_u32 src idx) | |
else None | |
module T = | |
struct | |
type t = (key, int list) Hashtbl.t | |
let find table x = | |
try Hashtbl.find table x | |
with Not_found -> [] | |
let add key value table = | |
let rest = find table key in | |
Hashtbl.replace table key (value :: rest) | |
end | |
let longuest_substring src x y len = | |
let rec aux acc len = | |
if x + len < y | |
&& y + len < len | |
&& Safe.get src (x + len) = Safe.get src (y + len) | |
then aux (Some (len + 1)) (len + 1) | |
else acc | |
in | |
aux None 0 | |
(* XXX: from ocaml-lz77, no optimized but this algorithm has no constraint. | |
bisoux @samoht. *) | |
let deflate ?(max_fardistance = (1 lsl 15) - 1) src t = | |
let results = Queue.create () in | |
let src_idx = ref (t.i_off + t.i_pos) in | |
let table = Hashtbl.create 1024 in | |
let last = ref 0 in | |
let flush_last () = | |
if !last <> 0 then begin | |
for i = 0 to !last - 1 | |
do t.on (Hunk.Literal (Safe.get src (t.i_off + t.i_pos + i))); | |
Queue.push | |
(Hunk.Literal (Safe.get src (t.i_off + t.i_pos + i))) | |
results; | |
done; | |
last := 0 | |
end | |
in | |
let find_match idx = | |
let max a b = | |
match a, b with | |
| Some (_, x), Some (_, y) -> if x >= y then a else b | |
| Some _, None -> a | |
| None, Some _ -> b | |
| None, None -> None | |
in | |
let key = key src idx (t.i_off + t.i_len) in | |
let candidates = T.find table key in | |
let rec aux acc = function | |
| [] -> acc | |
| x :: r -> | |
if x >= idx | |
|| idx - x >= max_fardistance | |
then acc | |
else match longuest_substring src x idx (t.i_off + t.i_len) with | |
| None -> aux acc r | |
| Some len -> aux (max acc (Some (x, len))) r | |
in | |
match aux None candidates with | |
| None -> None | |
| Some (i, len) -> Some (idx - i, len) | |
in | |
while !src_idx < t.i_off + t.i_len | |
do match find_match !src_idx with | |
| None -> | |
T.add (key src !src_idx (t.i_off + t.i_len)) !src_idx table; | |
incr last; | |
incr src_idx; | |
| Some (start, len) -> | |
for i = !src_idx to !src_idx + len - 1 | |
do T.add (key src !src_idx (t.i_off + t.i_len)) !src_idx table done; | |
flush_last (); | |
t.on (Hunk.Match (len - 3, start - 1)); | |
Queue.push (Hunk.Match (len - 3, start - 1)) results; | |
src_idx := !src_idx + len | |
done; | |
flush_last (); | |
Seq.of_queue results | |
let _hlog = [| 0; 11; 11; 11; 12; 13; 13; 13; 13; 13 |] | |
let _max_distance = 8191 | |
let _max_length = 256 | |
let _size_of_int64 = 8 | |
let _idx_boundary = 2 | |
(* Same as blosclz, fast and imperative implementation *) | |
let deffast | |
: type a. | |
?accel:int -> | |
?max_fardistance:int -> | |
(Safe.read, a) Safe.t -> a t -> Hunk.t Seq.t | |
= fun ?(accel = 1) ?(max_fardistance = (1 lsl 15) - 1) src t -> | |
let src_idx = ref (t.i_off + t.i_pos) in | |
let hash_log = Array.get _hlog t.level in | |
let hash_len = 1 lsl hash_log in | |
let hash_tab = Array.make hash_len 0 in | |
let results = Queue.create () in | |
let accel = if accel < 1 then 0 else accel - 1 in | |
t.on (Hunk.Literal (Safe.get src !src_idx)); | |
Queue.push (Hunk.Literal (Safe.get src !src_idx)) results; | |
incr src_idx; | |
t.on (Hunk.Literal (Safe.get src !src_idx)); | |
Queue.push (Hunk.Literal (Safe.get src !src_idx)) results; | |
incr src_idx; | |
let c ref idx = | |
try | |
if Safe.get src !ref = Safe.get src !idx | |
then begin incr ref; | |
incr idx; | |
true | |
end else false | |
with exn -> false | |
in | |
while !src_idx < t.i_off + t.i_len - 12 | |
do | |
let anchor = !src_idx in | |
let src_ref = ref !src_idx in | |
try | |
if Safe.get src !src_idx = Safe.get src (!src_idx - 1) | |
&& Safe.get_u16 src (!src_idx - 1) = Safe.get_u16 src (!src_idx + 1) | |
then raise (Match (0, 0)) (* (+3, +1) *); | |
let hval = | |
let v = Safe.get_u16 src !src_idx in | |
let v = (Safe.get_u16 src (!src_idx + 1) | |
lxor (v lsr (16 - hash_log))) lxor v in | |
v land ((1 lsl hash_log) - 1) | |
in | |
src_ref := (Array.get hash_tab hval); | |
let distance = anchor - !src_ref in | |
if distance land accel = 0 | |
then Array.set hash_tab hval (anchor - t.i_off); | |
if distance = 0 || distance >= max_fardistance | |
|| c src_ref src_idx = false | |
|| c src_ref src_idx = false | |
|| c src_ref src_idx = false | |
then raise (Literal (Safe.get src anchor)); | |
(* TODO: fix that! bug with | |
deflate ~(level >= 5) -> inflate | |
only with decompress, with camlzip, all is fine | |
if t.level >= 5 && distance >= _max_distance | |
then if c src_ref src_idx = false | |
|| c src_ref src_idx = false | |
then raise (Literal (Safe.get src anchor)) | |
else raise (Match (2, distance - 1)) (* (+3, +1) *); | |
*) | |
raise (Match (!src_idx - anchor - 3, distance - 1)) | |
with Match (len, 0) -> | |
begin | |
let pattern = Safe.get src (anchor + len - 1) in | |
let v1 = repeat pattern in | |
(* _ _ _ _ | |
* |_|_|_|_| | |
* | | | | src_idx | |
* | | | src_ref | |
* | | anchor | |
* | -1 | |
*) | |
src_idx := anchor + (len + 3); | |
(** XXX: in blosclz, [src_ref = anchor - 1 + 3], but in this case, | |
we accept 1 wrong byte. | |
*) | |
src_ref := anchor + (len + 3); | |
try | |
while !src_idx < (t.i_off + t.i_len) | |
- _size_of_int64 | |
- (2 * _idx_boundary) | |
&& !src_idx - 3 - anchor < _max_length - _size_of_int64 | |
do | |
let v2 = Safe.get_u64 src !src_ref in | |
if v1 <> v2 | |
then begin | |
while !src_idx < (t.i_off + t.i_len) - _idx_boundary | |
&& !src_idx - 3 - anchor < _max_length | |
do | |
if Safe.get src !src_ref <> pattern | |
then raise Break | |
else begin incr src_ref; incr src_idx; end | |
done; | |
raise Break | |
end else begin | |
src_idx := !src_idx + 8; | |
src_ref := !src_ref + 8; | |
end | |
done; | |
raise Break | |
with Break -> | |
begin | |
if !src_idx > t.i_off + t.i_len - _idx_boundary | |
then begin | |
let l = !src_idx - (t.i_off + t.i_len) - _idx_boundary in | |
src_idx := !src_idx - l; | |
src_ref := !src_ref - l; | |
end; | |
t.on (Hunk.Match (!src_idx - 3 - anchor, 0)); | |
Queue.push (Hunk.Match (!src_idx - 3 - anchor, 0)) results; | |
end | |
end | |
| Match (len, dist) -> | |
begin | |
src_idx := anchor + (len + 3); | |
src_ref := anchor - (dist + 1) + (len + 3); | |
try | |
while !src_idx < (t.i_off + t.i_len) | |
- _size_of_int64 | |
- (2 * _idx_boundary) | |
&& !src_idx - 3 - anchor < _max_length - _size_of_int64 | |
do if Safe.get_u64 src !src_idx <> Safe.get_u64 src !src_ref | |
then begin | |
while !src_idx < (t.i_off + t.i_len) - _idx_boundary | |
&& !src_idx - 3 - anchor < _max_length | |
do if c src_ref src_idx = false | |
then raise Break | |
done; | |
raise Break | |
end else begin | |
src_idx := !src_idx + 8; | |
src_ref := !src_ref + 8; | |
end; | |
done; | |
raise Break | |
with Break -> | |
begin | |
if !src_idx > t.i_off + t.i_len - _idx_boundary | |
then begin | |
let l = !src_idx - (t.i_off + t.i_len) - _idx_boundary in | |
src_idx := !src_idx - l; | |
src_ref := !src_ref - l; | |
end; | |
t.on (Hunk.Match (!src_idx - 3 - anchor, dist)); | |
Queue.push (Hunk.Match (!src_idx - 3 - anchor, dist)) results; | |
end | |
end | |
| Literal chr -> | |
begin | |
src_idx := anchor + 1; | |
t.on (Hunk.Literal chr); | |
Queue.push (Hunk.Literal chr) results; | |
end | |
done; | |
while !src_idx < t.i_off + t.i_len | |
do t.on (Hunk.Literal (Safe.get src !src_idx)); | |
Queue.push (Hunk.Literal (Safe.get src !src_idx)) results; | |
incr src_idx | |
done; | |
Seq.of_queue results | |
let eval src t = | |
let eval0 t = match t.state with | |
| Deflate window_bits -> | |
if t.i_len >= 12 | |
then Cont { t with state = Deffast window_bits } | |
else let hunks = deflate | |
~max_fardistance:((1 lsl window_bits) - 1) src t in | |
await { t with state = Choose window_bits | |
; i_pos = t.i_len } | |
hunks | |
| Deffast window_bits -> | |
if t.i_len >= 12 | |
then let hunks = deffast | |
~max_fardistance:((1 lsl window_bits) - 1) src t in | |
await { t with state = Choose window_bits | |
; i_pos = t.i_len } | |
hunks | |
else Cont { t with state = Deflate window_bits } | |
| Choose window_bits -> await t Seq.empty | |
| Exception exn -> error t exn | |
in | |
let rec loop t = | |
match eval0 t with | |
| Cont t -> loop t | |
| Wait (t, hunks) -> `Await (t, hunks) | |
| Error (t, exn) -> `Error (t, exn) | |
in | |
loop t | |
let refill off len t = | |
if (t.i_len - t.i_pos) = 0 | |
then match t.state with | |
| Choose window_bits -> | |
{ t with i_off = off | |
; i_len = len | |
; i_pos = 0 | |
; state = Deflate window_bits } | |
| _ -> { t with i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
else raise (Invalid_argument (Format.sprintf "L.refill: you lost \ | |
something (pos: %d, \ | |
len: %d)" | |
t.i_pos t.i_len)) | |
let used_in t = t.i_pos | |
let default | |
?(level = 0) | |
?(on = fun _ -> ()) | |
window_bits = | |
if level >= 0 && level <= 9 | |
then { i_off = 0 | |
; i_pos = 0 | |
; i_len = 0 | |
; level | |
; on | |
; state = Deflate window_bits } | |
else { i_off = 0 | |
; i_pos = 0 | |
; i_len = 0 | |
; level = 0 | |
; on | |
; state = Exception (Invalid_level level) } | |
end | |
(* Table from zlib *) | |
module Table = | |
struct | |
let _extra_lbits = | |
[| 0; 0; 0; 0; 0; 0; 0; 0; 1; 1; 1; 1; 2; 2; 2; 2; 3; 3; 3; 3; 4; 4; 4; 4; | |
5; 5; 5; 5; 0 |] | |
let _extra_dbits = | |
[| 0; 0; 0; 0; 1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 6; 6; 7; 7; 8; 8; 9; 9; 10; | |
10; 11; 11; 12; 12; 13; 13 |] | |
let _base_length = | |
[| 0; 1; 2; 3; 4; 5; 6; 7; 8; 10; 12; 14; 16; 20; 24; 28; 32; | |
40; 48; 56; 64; 80; 96; 112; 128; 160; 192; 224; 255 |] | |
let _base_dist = | |
[| 0; 1; 2; 3; 4; 6; 8; 12; 16; 24; 32; 48; | |
64; 96; 128; 192; 256; 384; 512; 768; 1024; 1536; 2048; 3072; | |
4096; 6144; 8192; 12288; 16384; 24576 |] | |
let _distance = | |
let t = | |
[| 0; 1; 2; 3; 4; 4; 5; 5; 6; 6; 6; 6; 7; 7; 7; 7; 8; 8; | |
8; 8; 8; 8; 8; 8; 9; 9; 9; 9; 9; 9; 9; 9; 10; 10; 10; 10; | |
10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 11; 11; 11; 11; 11; 11; | |
11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 12; 12; 12; 12; 12; 12; 12; 12; | |
12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; 12; | |
12; 12; 12; 12; 12; 12; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; | |
13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; 13; | |
13; 13; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; | |
14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; | |
14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; | |
14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 14; 15; 15; 15; 15; 15; 15; | |
15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; | |
15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; | |
15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; 15; | |
15; 15; 15; 15; 0; 0; 16; 17; 18; 18; 19; 19; 20; 20; 20; 20; 21; 21; | |
21; 21; 22; 22; 22; 22; 22; 22; 22; 22; 23; 23; 23; 23; 23; 23; 23; 23; | |
24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 25; 25; | |
25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 26; 26; 26; 26; | |
26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; | |
26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 27; 27; 27; 27; 27; 27; 27; 27; | |
27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; | |
27; 27; 27; 27; 27; 27; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; | |
28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; | |
28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; | |
28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 28; 29; 29; | |
29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; | |
29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; | |
29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; 29; | |
29; 29; 29; 29; 29; 29; 29; 29 |] | |
in | |
fun code -> | |
if code < 256 | |
then Array.get t code | |
else Array.get t (256 + (code lsr 7)) | |
let _length = | |
[| 0; 1; 2; 3; 4; 5; 6; 7; 8; 8; 9; 9; 10; 10; 11; 11; 12; 12; | |
12; 12; 13; 13; 13; 13; 14; 14; 14; 14; 15; 15; 15; 15; 16; 16; 16; 16; | |
16; 16; 16; 16; 17; 17; 17; 17; 17; 17; 17; 17; 18; 18; 18; 18; 18; 18; | |
18; 18; 19; 19; 19; 19; 19; 19; 19; 19; 20; 20; 20; 20; 20; 20; 20; 20; | |
20; 20; 20; 20; 20; 20; 20; 20; 21; 21; 21; 21; 21; 21; 21; 21; 21; 21; | |
21; 21; 21; 21; 21; 21; 22; 22; 22; 22; 22; 22; 22; 22; 22; 22; 22; 22; | |
22; 22; 22; 22; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; | |
23; 23; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; | |
24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 24; 25; 25; | |
25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; | |
25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 26; 26; 26; 26; 26; 26; | |
26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; 26; | |
26; 26; 26; 26; 26; 26; 26; 26; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; | |
27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; 27; | |
27; 27; 27; 28 |] | |
let _hclen_order = | |
[| 16; 17; 18; 0; 8; 7; 9; 6; 10; 5; 11; 4; 12; 3; 13; 2; 14; 1; 15 |] | |
let _static_ltree = | |
[| ( 12, 8); (140, 8); ( 76, 8); (204, 8); ( 44, 8); | |
(172, 8); (108, 8); (236, 8); ( 28, 8); (156, 8); | |
( 92, 8); (220, 8); ( 60, 8); (188, 8); (124, 8); | |
(252, 8); ( 2, 8); (130, 8); ( 66, 8); (194, 8); | |
( 34, 8); (162, 8); ( 98, 8); (226, 8); ( 18, 8); | |
(146, 8); ( 82, 8); (210, 8); ( 50, 8); (178, 8); | |
(114, 8); (242, 8); ( 10, 8); (138, 8); ( 74, 8); | |
(202, 8); ( 42, 8); (170, 8); (106, 8); (234, 8); | |
( 26, 8); (154, 8); ( 90, 8); (218, 8); ( 58, 8); | |
(186, 8); (122, 8); (250, 8); ( 6, 8); (134, 8); | |
( 70, 8); (198, 8); ( 38, 8); (166, 8); (102, 8); | |
(230, 8); ( 22, 8); (150, 8); ( 86, 8); (214, 8); | |
( 54, 8); (182, 8); (118, 8); (246, 8); ( 14, 8); | |
(142, 8); ( 78, 8); (206, 8); ( 46, 8); (174, 8); | |
(110, 8); (238, 8); ( 30, 8); (158, 8); ( 94, 8); | |
(222, 8); ( 62, 8); (190, 8); (126, 8); (254, 8); | |
( 1, 8); (129, 8); ( 65, 8); (193, 8); ( 33, 8); | |
(161, 8); ( 97, 8); (225, 8); ( 17, 8); (145, 8); | |
( 81, 8); (209, 8); ( 49, 8); (177, 8); (113, 8); | |
(241, 8); ( 9, 8); (137, 8); ( 73, 8); (201, 8); | |
( 41, 8); (169, 8); (105, 8); (233, 8); ( 25, 8); | |
(153, 8); ( 89, 8); (217, 8); ( 57, 8); (185, 8); | |
(121, 8); (249, 8); ( 5, 8); (133, 8); ( 69, 8); | |
(197, 8); ( 37, 8); (165, 8); (101, 8); (229, 8); | |
( 21, 8); (149, 8); ( 85, 8); (213, 8); ( 53, 8); | |
(181, 8); (117, 8); (245, 8); ( 13, 8); (141, 8); | |
( 77, 8); (205, 8); ( 45, 8); (173, 8); (109, 8); | |
(237, 8); ( 29, 8); (157, 8); ( 93, 8); (221, 8); | |
( 61, 8); (189, 8); (125, 8); (253, 8); ( 19, 9); | |
(275, 9); (147, 9); (403, 9); ( 83, 9); (339, 9); | |
(211, 9); (467, 9); ( 51, 9); (307, 9); (179, 9); | |
(435, 9); (115, 9); (371, 9); (243, 9); (499, 9); | |
( 11, 9); (267, 9); (139, 9); (395, 9); ( 75, 9); | |
(331, 9); (203, 9); (459, 9); ( 43, 9); (299, 9); | |
(171, 9); (427, 9); (107, 9); (363, 9); (235, 9); | |
(491, 9); ( 27, 9); (283, 9); (155, 9); (411, 9); | |
( 91, 9); (347, 9); (219, 9); (475, 9); ( 59, 9); | |
(315, 9); (187, 9); (443, 9); (123, 9); (379, 9); | |
(251, 9); (507, 9); ( 7, 9); (263, 9); (135, 9); | |
(391, 9); ( 71, 9); (327, 9); (199, 9); (455, 9); | |
( 39, 9); (295, 9); (167, 9); (423, 9); (103, 9); | |
(359, 9); (231, 9); (487, 9); ( 23, 9); (279, 9); | |
(151, 9); (407, 9); ( 87, 9); (343, 9); (215, 9); | |
(471, 9); ( 55, 9); (311, 9); (183, 9); (439, 9); | |
(119, 9); (375, 9); (247, 9); (503, 9); ( 15, 9); | |
(271, 9); (143, 9); (399, 9); ( 79, 9); (335, 9); | |
(207, 9); (463, 9); ( 47, 9); (303, 9); (175, 9); | |
(431, 9); (111, 9); (367, 9); (239, 9); (495, 9); | |
( 31, 9); (287, 9); (159, 9); (415, 9); ( 95, 9); | |
(351, 9); (223, 9); (479, 9); ( 63, 9); (319, 9); | |
(191, 9); (447, 9); (127, 9); (383, 9); (255, 9); | |
(511, 9); ( 0, 7); ( 64, 7); ( 32, 7); ( 96, 7); | |
( 16, 7); ( 80, 7); ( 48, 7); (112, 7); ( 8, 7); | |
( 72, 7); ( 40, 7); (104, 7); ( 24, 7); ( 88, 7); | |
( 56, 7); (120, 7); ( 4, 7); ( 68, 7); ( 36, 7); | |
(100, 7); ( 20, 7); ( 84, 7); ( 52, 7); (116, 7); | |
( 3, 8); (131, 8); ( 67, 8); (195, 8); ( 35, 8); | |
(163, 8); ( 99, 8); (227, 8) |] | |
let _static_dtree = | |
[| ( 0, 5); (16, 5); ( 8, 5); (24, 5); ( 4, 5); | |
(20, 5); (12, 5); (28, 5); ( 2, 5); (18, 5); | |
(10, 5); (26, 5); ( 6, 5); (22, 5); (14, 5); | |
(30, 5); ( 1, 5); (17, 5); ( 9, 5); (25, 5); | |
( 5, 5); (21, 5); (13, 5); (29, 5); ( 3, 5); | |
(19, 5); (11, 5); (27, 5); ( 7, 5); (23, 5) |] | |
end | |
(** non-blocking and functionnal implementation of Deflate *) | |
module type DEFLATE = | |
sig | |
type error = .. | |
type error += Lz77_error of L.error | |
module F : sig type t = int array * int array end | |
type ('i, 'o) t | |
val pp_error : Format.formatter -> error -> unit | |
val pp : Format.formatter -> ('i, 'o) t -> unit | |
val get_frequencies : ('i, 'o) t -> F.t | |
val set_frequencies : ?paranoid:bool -> F.t -> ('i, 'o) t -> ('i, 'o) t | |
val finish : ('i, 'o) t -> ('i, 'o) t | |
val no_flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val partial_flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val sync_flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val full_flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val eval : 'a B.t -> 'a B.t -> ('a, 'a) t -> | |
[ `Await of ('a, 'a) t | |
| `Flush of ('a, 'a) t | |
| `End of ('a, 'a) t | |
| `Error of ('a, 'a) t * error ] | |
val used_in : ('i, 'o) t -> int | |
val used_out : ('i, 'o) t -> int | |
val default : proof:'o B.t -> ?wbits:int -> int -> ('i, 'o) t | |
val to_result : 'a B.t -> 'a B.t -> | |
('a B.t -> int) -> | |
('a B.t -> int -> int) -> | |
('a, 'a) t -> (('a, 'a) t, error) result | |
val bytes : Bytes.t -> Bytes.t -> | |
(Bytes.t -> int) -> | |
(Bytes.t -> int -> int) -> | |
(B.st, B.st) t -> ((B.st, B.st) t, error) result | |
val bigstring : B.Bigstring.t -> B.Bigstring.t -> | |
(B.Bigstring.t -> int) -> | |
(B.Bigstring.t -> int -> int) -> | |
(B.bs, B.bs) t -> ((B.bs, B.bs) t, error) result | |
end | |
module Deflate : DEFLATE = | |
struct | |
type error = .. | |
type error += Lz77_error of L.error | |
module F = | |
struct | |
type t = int array * int array | |
let pp fmt (lit, dst) = Format.fprintf fmt "(#lit, #dst)" | |
let make () = | |
let lit, dst = Array.make 286 0, Array.make 30 0 in | |
(** XXX: to force the existence of the opcode EOB. *) | |
Array.set lit 256 1; | |
(lit, dst) | |
let add_literal (lit, dst) chr = | |
lit.(Char.code chr) <- lit.(Char.code chr) + 1 | |
let add_distance (lit, dst) (len, dist) = | |
lit.(Table._length.(len) + 256 + 1) <- | |
lit.(Table._length.(len) + 256 + 1) + 1; | |
dst.(Table._distance dist) <- | |
dst.(Table._distance dist) + 1 | |
let get_literals (lit, dst) = lit | |
let get_distances (lit, dst) = dst | |
end | |
type ('i, 'o) t = | |
{ hold : int | |
; bits : int | |
; temp : ([ Safe.read | Safe.write ], 'o) Safe.t | |
; o_off : int | |
; o_pos : int | |
; o_len : int | |
; i_off : int | |
; i_pos : int | |
; i_len : int | |
; level : int | |
; wbits : int | |
; adler : Int32.t | |
; state : ('i, 'o) state } | |
and ('i, 'o) k = (Safe.read, 'i) Safe.t -> | |
(Safe.write, 'o) Safe.t -> | |
('i, 'o) t -> | |
('i, 'o) res | |
and ('i, 'o) state = | |
| Header of ('i, 'o) k | |
| MakeBlock of ('i, 'o) block | |
| WriteBlock of ('i, 'o) k | |
| FastBlock of (int * int) array * | |
(int * int) array * | |
Hunk.t Q.t * code * flush | |
| AlignBlock of F.t option * bool | |
| FixedBlock of F.t | |
| DynamicHeader of ('i, 'o) k | |
| StaticHeader of ('i, 'o) k | |
| WriteCrc of ('i, 'o) k | |
| End | |
| Exception of error | |
and ('i, 'o) res = | |
| Cont of ('i, 'o) t | |
| Wait of ('i, 'o) t | |
| Flush of ('i, 'o) t | |
| Ok of ('i, 'o) t | |
| Error of ('i, 'o) t * error | |
and ('i, 'o) block = | |
| Static of { lz : 'i L.t | |
; frequencies : F.t | |
; deflate : Hunk.t Seq.t } | |
| Dynamic of { lz : 'i L.t | |
; frequencies : F.t | |
; deflate : Hunk.t Seq.t } | |
| Flat of int | |
and flush = | |
| Sync of F.t | Partial of F.t | Full | Final | |
and code = | |
| Length | |
| ExtLength | |
| Dist | |
| ExtDist | |
let pp_error fmt = function | |
| Lz77_error lz -> Format.fprintf fmt "(Lz77_error %a)" L.pp_error lz | |
let pp_code fmt = function | |
| Length -> Format.fprintf fmt "Length" | |
| ExtLength -> Format.fprintf fmt "ExtLength" | |
| Dist -> Format.fprintf fmt "Dist" | |
| ExtDist -> Format.fprintf fmt "ExtDist" | |
let pp_flush fmt = function | |
| Sync f -> Format.fprintf fmt "(Sync %a)" F.pp f | |
| Partial f -> Format.fprintf fmt "(Partial %a)" F.pp f | |
| Full -> Format.fprintf fmt "Full" | |
| Final -> Format.fprintf fmt "Final" | |
let pp_block fmt = function | |
| Static { lz; frequencies; deflate; } -> | |
Format.fprintf fmt "(Static (%a, %a, #deflate))" | |
L.pp lz F.pp frequencies | |
| Dynamic { lz; frequencies; deflate; } -> | |
Format.fprintf fmt "(Dynamic (%a, %a, #deflate))" | |
L.pp lz F.pp frequencies | |
| Flat pos -> | |
Format.fprintf fmt "(Flat %d)" pos | |
let pp_state fmt = function | |
| Header k -> Format.fprintf fmt "(Header #fun)" | |
| MakeBlock block -> Format.fprintf fmt "(MakeBlock %a)" pp_block block | |
| WriteBlock k -> Format.fprintf fmt "(WriteBlock #fun)" | |
| FastBlock (ltree, dtree, queue, code, flush) -> | |
Format.fprintf fmt "(FastBlock (#ltree, #dtree, #deflate, %a, %a))" | |
pp_code code pp_flush flush | |
| AlignBlock (Some f, last) -> | |
Format.fprintf fmt "(AlignBlock (Some %a, last:%b))" | |
F.pp f last | |
| AlignBlock (None, last) -> | |
Format.fprintf fmt "(AlignBlock (None, last:%b))" last | |
| FixedBlock f -> | |
Format.fprintf fmt "(FixedBlock %a)" F.pp f | |
| DynamicHeader k -> | |
Format.fprintf fmt "(DynamicHeader #fun)" | |
| StaticHeader k -> | |
Format.fprintf fmt "(StaticHeader #fun)" | |
| WriteCrc k -> | |
Format.fprintf fmt "(WriteCrc #fun)" | |
| End -> Format.fprintf fmt "End" | |
| Exception exn -> Format.fprintf fmt "(Exception %a)" pp_error exn | |
let pp fmt { hold; bits | |
; o_off; o_pos; o_len | |
; i_off; i_pos; i_len | |
; level | |
; wbits | |
; adler | |
; state } = | |
Format.fprintf fmt "{@[<hov>hold = %d;@ \ | |
bits = %d;@ \ | |
o_off = %d;@ \ | |
o_pos = %d;@ \ | |
o_len = %d;@ \ | |
i_off = %d;@ \ | |
i_pos = %d;@ \ | |
i_len = %d;@ \ | |
level = %d;@ \ | |
wbits = %d;@ \ | |
adler = %ld;@ \ | |
state = %a@]}" | |
hold bits | |
o_off o_pos o_len | |
i_off i_pos i_len | |
level | |
wbits | |
adler | |
pp_state state | |
let await t = Wait t | |
let error t exn = Error ({ t with state = Exception exn }, exn) | |
let ok t = Ok { t with state = End } | |
let block_from_flush = function | |
| Partial f -> FixedBlock f | |
| Full -> AlignBlock (None, false) | |
| Final -> AlignBlock (None, true) | |
| Sync f -> AlignBlock (Some f, false) | |
module KHeader = | |
struct | |
let rec put_byte chr k src dst t = | |
if (t.o_len - t.o_pos) > 0 | |
then begin | |
Safe.set dst (t.o_off + t.o_pos) (Char.chr chr); | |
k src dst { t with o_pos = t.o_pos + 1 } | |
end else Flush { t with state = Header (put_byte chr k) } | |
end | |
module KWriteBlock = | |
struct | |
let rec put_byte chr k src dst t = | |
if (t.o_len - t.o_pos) > 0 | |
then begin | |
Safe.set dst (t.o_off + t.o_pos) (Char.chr chr); | |
k src dst { t with o_pos = t.o_pos + 1 } | |
end else Flush { t with state = WriteBlock (put_byte chr k) } | |
let put_short short k src dst t = | |
(put_byte (short land 0xFF) | |
@@ put_byte (short lsr 8 land 0xFF) k) | |
src dst t | |
let align k src dst t = | |
if t.bits > 8 | |
then (put_short t.hold | |
@@ fun src dst t -> | |
k src dst { t with hold = 0 | |
; bits = 0 }) | |
src dst t | |
else if t.bits > 0 | |
then (put_byte t.hold | |
@@ fun src dst t -> | |
k src dst { t with hold = 0 | |
; bits = 0 }) | |
src dst t | |
else k src dst { t with hold = 0 | |
; bits = 0 } | |
let put_short_msb short k src dst t = | |
(put_byte (short lsr 8 land 0xFF) | |
@@ put_byte (short land 0xFF) k) | |
src dst t | |
let put_bits (code, len) k src dst t = | |
if t.bits > 16 - len | |
then | |
put_short (t.hold lor (code lsl t.bits)) | |
(fun src dst t -> | |
k src dst | |
{ t with hold = code lsr (16 - t.bits) | |
; bits = t.bits + len - 16 }) | |
src dst t | |
else k src dst | |
{ t with hold = t.hold lor (code lsl t.bits) | |
; bits = t.bits + len } | |
let put_bit bit k src dst t = | |
if bit then put_bits (1, 1) k src dst t | |
else put_bits (0, 1) k src dst t | |
end | |
module KDynamicHeader = | |
struct | |
let put_trans trans_length hclen k src dst t = | |
let rec loop i src dst t = | |
if i = hclen | |
then k src dst t | |
else KWriteBlock.put_bits | |
(trans_length.(i), 3) | |
(loop (i + 1)) | |
src | |
dst | |
t | |
in loop 0 src dst t | |
let put_symbols tree_symbol tree_code tree_length k src dst t = | |
let rec loop i src dst t = | |
if i = Array.length tree_symbol | |
then k src dst t | |
else let code = tree_symbol.(i) in | |
KWriteBlock.put_bits (tree_code.(code), tree_length.(code)) | |
(fun src dst t -> | |
if code >= 16 | |
then let bitlen = match code with | |
| 16 -> 2 | |
| 17 -> 3 | |
| 18 -> 7 | |
| _ -> assert false | |
in | |
KWriteBlock.put_bits | |
(tree_symbol.(i + 1), bitlen) | |
(loop (i + 2)) | |
src | |
dst | |
t | |
else loop (i + 1) src dst t) | |
src dst t | |
in loop 0 src dst t | |
end | |
let get_tree_symbols hlit lit_len_lengths hdist dist_lengths = | |
let src = Array.make (hlit + hdist) 0 in | |
let result = Array.make (286 + 30) 0 in | |
let freqs = Array.make 19 0 in | |
for i = 0 to hlit - 1 do src.(i) <- lit_len_lengths.(i) done; | |
for i = hlit to hlit + hdist - 1 do src.(i) <- dist_lengths.(i - hlit) done; | |
let n_result = ref 0 in | |
let i = ref 0 in | |
let l = Array.length src in | |
while !i < l do | |
let j = ref 1 in | |
while !i + !j < l && src.(!i + !j) = src.(!i) | |
do incr j done; | |
let run_length = ref !j in | |
if src.(!i) = 0 | |
then | |
if !run_length < 3 | |
then | |
while !run_length > 0 do | |
result.(!n_result) <- 0; | |
incr n_result; | |
freqs.(0) <- freqs.(0) + 1; | |
decr run_length; | |
done | |
else | |
while !run_length > 0 do | |
let rpt = ref (if !run_length < 138 then !run_length else 138) in | |
if !rpt > !run_length - 3 && !rpt < !run_length | |
then rpt := !run_length - 3; | |
if !rpt <= 10 | |
then begin | |
result.(!n_result) <- 17; | |
incr n_result; | |
result.(!n_result) <- !rpt - 3; | |
incr n_result; | |
freqs.(17) <- freqs.(17) + 1; | |
end else begin | |
result.(!n_result) <- 18; | |
incr n_result; | |
result.(!n_result) <- !rpt - 11; | |
incr n_result; | |
freqs.(18) <- freqs.(18) + 1; | |
end; | |
run_length := !run_length - !rpt; | |
done | |
else | |
begin | |
result.(!n_result) <- src.(!i); | |
incr n_result; | |
freqs.(src.(!i)) <- freqs.(src.(!i)) + 1; | |
decr run_length; | |
if !run_length < 3 | |
then | |
while !run_length > 0 do | |
result.(!n_result) <- src.(!i); | |
incr n_result; | |
freqs.(src.(!i)) <- freqs.(src.(!i)) + 1; | |
decr run_length; | |
done | |
else | |
while !run_length > 0 do | |
let rpt = ref (if !run_length < 6 then !run_length else 6) in | |
if !rpt > !run_length - 3 && !rpt < !run_length | |
then rpt := !run_length - 3; | |
result.(!n_result) <- 16; | |
incr n_result; | |
result.(!n_result) <- !rpt - 3; | |
incr n_result; | |
freqs.(16) <- freqs.(16) + 1; | |
run_length := !run_length - !rpt; | |
done | |
end; | |
i := !i + !j; | |
done; | |
Array.sub result 0 !n_result, freqs | |
let block_of_level ~wbits ?frequencies level = | |
match level with | |
| 0 -> Flat 0 | |
| n -> | |
let frequencies = match frequencies with | |
| Some f -> f | |
| None -> F.make () | |
in | |
let on = function | |
| Hunk.Literal chr -> | |
F.add_literal frequencies chr | |
| Hunk.Match (len, dist) -> | |
F.add_distance frequencies (len, dist) | |
in | |
match n with | |
| 1 -> Static { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 2 -> Static { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 3 -> Static { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 4 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 5 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 6 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 7 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 8 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| 9 -> Dynamic { lz = L.default ~on ~level wbits | |
; frequencies | |
; deflate = Seq.empty } | |
| _ -> raise (Invalid_argument "Z.block_of_level") | |
let zip arr1 arr2 = | |
Array.init (Array.length arr1) (fun i -> arr1.(i), arr2.(i)) | |
let rec write_block ltree dtree queue flush src dst t = | |
match Q.take_front_exn queue with | |
| Hunk.Literal chr, tl -> | |
(KWriteBlock.put_bits ltree.(Char.code chr) | |
@@ fun src dst t -> | |
Cont { t with state = FastBlock (ltree, dtree, tl, Length, flush) }) | |
src dst t | |
| Hunk.Match (len, dist), tl -> | |
(KWriteBlock.put_bits ltree.(Table._length.(len) + 256 + 1) | |
@@ KWriteBlock.put_bits (len - Table._base_length.(Table._length.(len)), | |
Table._extra_lbits.(Table._length.(len))) | |
@@ KWriteBlock.put_bits dtree.(Table._distance dist) | |
@@ KWriteBlock.put_bits (dist - Table._base_dist.(Table._distance dist), | |
Table._extra_dbits.(Table._distance dist)) | |
@@ fun src dst t -> | |
Cont { t with state = FastBlock (ltree, dtree, tl, Length, flush) }) | |
src dst t | |
| exception Q.Empty -> | |
KWriteBlock.put_bits ltree.(256) | |
(fun src dst t -> Cont { t with state = block_from_flush flush }) | |
src dst t | |
let static frequencies queue flush src dst t = | |
let flush = flush frequencies in | |
(KWriteBlock.put_bit false (* XXX: when the user expect a final block, zlib | |
put an empty block to align the output | |
in byte - this last block has the final | |
flag. | |
*) | |
@@ KWriteBlock.put_bits (1, 2) | |
@@ fun src dst t -> | |
Cont { t with state = FastBlock (Table._static_ltree, | |
Table._static_dtree, | |
queue, | |
Length, | |
flush) }) | |
src dst t | |
let dynamic frequencies queue flush src dst t = | |
let trans_length = Array.make 19 0 in | |
let literal_length = T.get_lengths (F.get_literals frequencies) 15 in | |
let literal_code = T.get_codes_from_lengths literal_length in | |
let distance_length = T.get_lengths (F.get_distances frequencies) 7 in | |
let distance_code = T.get_codes_from_lengths distance_length in | |
let hlit = ref 286 in | |
while !hlit > 257 && literal_length.(!hlit - 1) = 0 do decr hlit done; | |
let hdist = ref 30 in | |
while !hdist > 1 && distance_length.(!hdist - 1) = 0 do decr hdist done; | |
let tree_symbol, f = get_tree_symbols | |
!hlit literal_length | |
!hdist distance_length | |
in | |
let tree_length = T.get_lengths f 7 in | |
for i = 0 to 18 | |
do trans_length.(i) <- tree_length.(Table._hclen_order.(i)) done; | |
let hclen = ref 19 in | |
while !hclen > 4 && trans_length.(!hclen - 1) = 0 do decr hclen done; | |
let tree_code = T.get_codes_from_lengths tree_length in | |
let hlit = !hlit in | |
let hdist = !hdist in | |
let hclen = !hclen in | |
let flush = flush frequencies in | |
(KWriteBlock.put_bit false (* XXX: when the user expect a final block, | |
zlib put an empty block to align the | |
output in byte - this last block has the | |
final flag. | |
*) | |
@@ KWriteBlock.put_bits (2, 2) | |
@@ KWriteBlock.put_bits (hlit - 257, 5) | |
@@ KWriteBlock.put_bits (hdist - 1, 5) | |
@@ KWriteBlock.put_bits (hclen - 4, 4) | |
@@ KDynamicHeader.put_trans trans_length hclen | |
@@ KDynamicHeader.put_symbols tree_symbol tree_code tree_length | |
(fun src dst t -> | |
let ltree = zip literal_code literal_length in | |
let dtree = zip distance_code distance_length in | |
Cont { t with state = FastBlock (ltree, | |
dtree, | |
queue, | |
Length, | |
flush) })) | |
src dst t | |
let crc src dst ({ adler; _ } as t) = | |
(KWriteBlock.align | |
@@ KWriteBlock.put_short_msb | |
(Int32.to_int (Int32.logand (Int32.shift_right adler 16) 0xFFFFl)) | |
@@ KWriteBlock.put_short_msb | |
(Int32.to_int (Int32.logand adler 0xFFFFl)) | |
@@ fun src dst t -> ok t) | |
src dst t | |
let rec write_flat off pos len final src dst t = | |
if (len - pos) = 0 | |
then (if final | |
then Cont { t with state = WriteCrc crc } | |
else Cont { t with state = MakeBlock (Flat 0) }) | |
else begin | |
let n = min (len - pos) (t.o_len - t.o_pos) in | |
Safe.blit t.temp (off + pos) dst (t.o_off + t.o_pos) n; | |
if t.o_len - (t.o_pos + n) = 0 | |
then Flush { t with state = WriteBlock (write_flat 0 (pos + n) len final) | |
; o_pos = t.o_pos + n } | |
else Cont { t with state = WriteBlock (write_flat 0 (pos + n) len final) | |
; o_pos = t.o_pos + n } | |
end | |
let flat off pos len final src dst t = | |
(KWriteBlock.put_bit final | |
@@ KWriteBlock.put_bits (0, 2) | |
@@ KWriteBlock.align | |
@@ KWriteBlock.put_short len | |
@@ KWriteBlock.put_short (lnot len) | |
@@ write_flat off pos len final) | |
src dst t | |
let make_block src dst t = function | |
| Static { lz; frequencies; deflate; } -> | |
(match L.eval src lz with | |
| `Await (lz, seq) -> | |
await { t with state = MakeBlock | |
(Static { lz | |
; frequencies | |
; deflate = Seq.append deflate seq }) | |
; i_pos = t.i_pos + L.used_in lz | |
; adler = Safe.adler32 | |
src | |
t.adler | |
(t.i_off + t.i_pos) | |
(L.used_in lz) } | |
| `Error (lz, exn) -> | |
error t (Lz77_error exn)) | |
| Dynamic { lz; frequencies; deflate; } -> | |
(match L.eval src lz with | |
| `Await (lz, seq) -> | |
await { t with state = MakeBlock | |
(Dynamic { lz | |
; frequencies | |
; deflate = Seq.append deflate seq }) | |
; i_pos = t.i_pos + L.used_in lz | |
; adler = Safe.adler32 | |
src | |
t.adler | |
(t.i_off + t.i_pos) | |
(L.used_in lz) } | |
| `Error (lz, exn) -> | |
error t (Lz77_error exn)) | |
| Flat pos -> | |
let len = min (t.i_len - t.i_pos) (0x8000 - pos) in | |
Safe.blit src (t.i_off + t.i_pos) t.temp pos len; | |
if pos + len = 0x8000 (* End of block *) | |
then Cont { t with state = WriteBlock (flat 0 0 0x8000 false) | |
; i_pos = t.i_pos + len | |
; adler = Safe.adler32 | |
src | |
t.adler | |
(t.i_off + t.i_pos) | |
len } | |
else await { t with state = MakeBlock (Flat (pos + len)) | |
; i_pos = t.i_pos + len | |
; adler = Safe.adler32 | |
src | |
t.adler | |
(t.i_off + t.i_pos) | |
len } | |
let fixed_block frequencies last src dst t = | |
(KWriteBlock.put_bit last | |
@@ KWriteBlock.put_bits (1, 2) | |
@@ KWriteBlock.put_bits Table._static_ltree.(256) | |
@@ fun src dst t -> | |
let block = block_of_level ~wbits:t.wbits ~frequencies t.level in | |
Cont { t with state = if last | |
then WriteCrc crc | |
else MakeBlock block }) | |
src dst t | |
let align_block frequencies last src dst t = | |
(KWriteBlock.put_bit last | |
@@ KWriteBlock.put_bits (0, 2) | |
@@ KWriteBlock.align | |
@@ KWriteBlock.put_short 0x0000 | |
@@ KWriteBlock.put_short 0xFFFF | |
@@ fun src dst t -> | |
let block = block_of_level ~wbits:t.wbits ?frequencies t.level in | |
Cont { t with state = if last | |
then WriteCrc crc | |
else MakeBlock block }) | |
src dst t | |
let rec fast_block src dst t ltree dtree queue code flush = | |
let q = ref queue in | |
let hold = ref t.hold in | |
let bits = ref t.bits in | |
let o_pos = ref t.o_pos in | |
let goto = ref code in | |
while Q.is_empty !q = false && t.o_len - !o_pos > 1 | |
do let (hd, tl) = Q.take_front_exn !q in | |
let (code, len), new_goto, new_q = match !goto, hd with | |
| Length, Hunk.Literal chr -> | |
ltree.(Char.code chr), Length, tl | |
| Length, Hunk.Match (len, dist) -> | |
ltree.(Table._length.(len) + 256 + 1), ExtLength, !q | |
| ExtLength, Hunk.Match (len, dist) -> | |
let code = Table._length.(len) in | |
(len - Table._base_length.(code), | |
Table._extra_lbits.(code)), | |
Dist, !q | |
| Dist, Hunk.Match (len, dist) -> | |
dtree.(Table._distance dist), ExtDist, !q | |
| ExtDist, Hunk.Match (len, dist) -> | |
let code = Table._distance dist in | |
(dist - Table._base_dist.(code), | |
Table._extra_dbits.(code)), | |
Length, tl | |
| _ -> assert false | |
in | |
if !bits + len > 16 | |
then begin | |
Safe.set dst (t.o_off + !o_pos) | |
(Char.chr ((!hold lor (code lsl !bits)) land 0xFF)); | |
Safe.set dst (t.o_off + !o_pos + 1) | |
(Char.chr ((!hold lor (code lsl !bits)) lsr 8 land 0xFF)); | |
hold := code lsr (16 - !bits); | |
bits := !bits + len - 16; | |
o_pos := !o_pos + 2; | |
end else begin | |
hold := !hold lor (code lsl !bits); | |
bits := !bits + len; | |
end; | |
goto := new_goto; | |
q := new_q; | |
done; | |
let state = match Q.take_front_exn !q, !goto with | |
| _, Length -> | |
WriteBlock (write_block ltree dtree !q flush) | |
| (Hunk.Match (len, dist), tl), ExtLength -> | |
let fn = | |
KWriteBlock.put_bits | |
(len - Table._base_length.(Table._length.(len)), | |
Table._extra_lbits.(Table._length.(len))) | |
@@ KWriteBlock.put_bits dtree.(Table._distance dist) | |
@@ KWriteBlock.put_bits | |
(dist - Table._base_dist.(Table._distance dist), | |
Table._extra_dbits.(Table._distance dist)) | |
@@ fun src dst t -> | |
Cont { t with state = WriteBlock | |
(write_block ltree dtree tl flush) } | |
in | |
WriteBlock fn | |
| (Hunk.Match (len, dist), tl), Dist -> | |
let fn = | |
KWriteBlock.put_bits dtree.(Table._distance dist) | |
@@ KWriteBlock.put_bits | |
(dist - Table._base_dist.(Table._distance dist), | |
Table._extra_dbits.(Table._distance dist)) | |
@@ fun src dst t -> | |
Cont { t with state = WriteBlock | |
(write_block ltree dtree tl flush) } | |
in | |
WriteBlock fn | |
| (Hunk.Match (len, dist), tl), ExtDist -> | |
let fn = | |
KWriteBlock.put_bits | |
(dist - Table._base_dist.(Table._distance dist), | |
Table._extra_dbits.(Table._distance dist)) | |
@@ fun src dst t -> | |
Cont { t with state = WriteBlock | |
(write_block ltree dtree tl flush) } | |
in | |
WriteBlock fn | |
| exception Q.Empty -> | |
let fn = | |
KWriteBlock.put_bits ltree.(256) | |
@@ fun src dst t -> Cont { t with state = block_from_flush flush } | |
in | |
WriteBlock fn | |
| _ -> assert false | |
in | |
Cont { t with hold = !hold | |
; bits = !bits | |
; o_pos = !o_pos | |
; state } | |
let write src dst t mode last = match mode with | |
| Static { lz; deflate; } -> () | |
| Dynamic { lz; frequencies; deflate; } -> () | |
| Flat _ -> () | |
let flush off len t = | |
{ t with o_off = off | |
; o_len = len | |
; o_pos = 0 } | |
let get_frequencies t = match t.state with | |
| MakeBlock (Dynamic { frequencies; _ }) | |
| MakeBlock (Static { frequencies; _ }) -> frequencies | |
| _ -> raise (Invalid_argument "Z.frequencies: bad state") | |
let set_frequencies ?(paranoid = false) (lit, dst) t = | |
let check = | |
Seq.iter | |
(function Hunk.Literal chr -> | |
if lit.(Char.code chr) > 0 | |
then () | |
else raise (Invalid_argument "Z.set_frequencies: invalid \ | |
frequencies") | |
| Hunk.Match (len, dist) -> | |
if lit.(Table._length.(len) + 256 + 1) > 0 | |
&& dst.(Table._distance dist) > 0 | |
then () | |
else raise (Invalid_argument "Z.set_frequencies: invalid \ | |
frequencies")) | |
in | |
if lit.(256) > 0 | |
then match t.state with | |
| MakeBlock (Dynamic x) -> | |
if paranoid then check x.deflate; | |
{ t with state = MakeBlock | |
(Dynamic { x with frequencies = (lit, dst) }) } | |
| MakeBlock (Static x) -> | |
if paranoid then check x.deflate; | |
{ t with state = MakeBlock | |
(Static { x with frequencies = (lit, dst) }) } | |
| _ -> raise (Invalid_argument "Z.set_frequencies: bad state") | |
else raise (Invalid_argument "Z.set_frequencies: invalid frequencies") | |
let finish t = match t.state with | |
| MakeBlock (Dynamic { lz; frequencies; deflate; }) -> | |
{ t with state = DynamicHeader | |
(dynamic | |
frequencies | |
(Q.of_seq deflate) | |
(fun _ -> Final)) } | |
| MakeBlock (Static { lz; frequencies; deflate; }) -> | |
{ t with state = StaticHeader | |
(static | |
frequencies | |
(Q.of_seq deflate) | |
(fun _ -> Final)) } | |
| MakeBlock (Flat len) -> | |
{ t with state = WriteBlock (flat 0 0 len true) } | |
| _ -> raise (Invalid_argument "Z.finish: bad state") | |
let no_flush off len t = match t.state with | |
| MakeBlock (Static { lz; frequencies; deflate; }) -> | |
{ t with state = MakeBlock (Static { lz = L.refill off len lz | |
; frequencies | |
; deflate }) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| MakeBlock (Dynamic { lz; frequencies; deflate; }) -> | |
{ t with state = MakeBlock (Dynamic { lz = L.refill off len lz | |
; frequencies | |
; deflate }) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| MakeBlock (Flat len') -> | |
{ t with state = MakeBlock (Flat len') | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| _ -> raise (Invalid_argument "Z.no_flush: bad state") | |
let partial_flush off len t = match t.state with | |
| MakeBlock block -> | |
if (t.i_len - t.i_pos) = 0 | |
then match block with | |
| Dynamic { lz; frequencies; deflate; } -> | |
{ t with state = DynamicHeader | |
(dynamic | |
frequencies | |
(Q.of_seq deflate) | |
(fun f -> Partial f)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Static { lz; frequencies; deflate; } -> | |
{ t with state = StaticHeader | |
(static | |
frequencies | |
(Q.of_seq deflate) | |
(fun f -> Partial f)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Flat _ -> assert false (* TODO *) | |
else raise (Invalid_argument (Format.sprintf "Z.partial_flush: you lost \ | |
something (pos: %d, \ | |
len: %d)" | |
t.i_pos t.i_len)) | |
| _ -> raise (Invalid_argument "Z.partial_flush: bad state") | |
let sync_flush off len t = match t.state with | |
| MakeBlock block -> | |
if (t.i_len - t.i_pos) = 0 | |
then match block with | |
| Dynamic { lz; frequencies; deflate; } -> | |
{ t with state = DynamicHeader | |
(dynamic | |
frequencies | |
(Q.of_seq deflate) | |
(fun f -> Sync f)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Static { lz; frequencies; deflate; } -> | |
{ t with state = StaticHeader | |
(static | |
frequencies | |
(Q.of_seq deflate) | |
(fun f -> Sync f)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Flat _ -> assert false (* TODO *) | |
else raise (Invalid_argument (Format.sprintf "Z.sync_flush: you lost \ | |
something (pos: %d, \ | |
len: %d)" | |
t.i_pos t.i_len)) | |
| _ -> raise (Invalid_argument "Z.sync_flush: bad state") | |
let full_flush off len t = match t.state with | |
| MakeBlock block -> | |
if (t.i_len - t.i_pos) = 0 | |
then match block with | |
| Dynamic { lz; frequencies; deflate; } -> | |
{ t with state = DynamicHeader | |
(dynamic | |
frequencies | |
(Q.of_seq deflate) | |
(fun _ -> Full)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Static { lz; frequencies; deflate; } -> | |
{ t with state = StaticHeader | |
(static | |
frequencies | |
(Q.of_seq deflate) | |
(fun _ -> Full)) | |
; i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
| Flat _ -> assert false (* TODO *) | |
else raise (Invalid_argument (Format.sprintf "Z.full_flush: you lost \ | |
something (pos: %d, \ | |
len: %d)" | |
t.i_pos t.i_len)) | |
| _ -> raise (Invalid_argument "Z.full_flush: bad state") | |
let header wbits mode src dst t = | |
let header = (8 + ((wbits - 8) lsl 4)) lsl 8 in | |
let header = header lor (0x4 lsl 5) in | |
(* XXX: FDICT = 0 and FLEVEL = 2, | |
we use a default algorithm. *) | |
let header = header + (31 - (header mod 31)) in | |
(KHeader.put_byte (header lsr 8) | |
@@ KHeader.put_byte (header land 0xFF) | |
@@ fun src dst t -> | |
Cont { t with hold = 0 | |
; bits = 0 | |
; state = MakeBlock mode }) | |
(* XXX: not necessary to update [hold] and [bits] but to | |
be clear. *) | |
src dst t | |
let eval src dst t = | |
let safe_src = Safe.read_only src in | |
let safe_dst = Safe.write_only dst in | |
let eval0 t = | |
match t.state with | |
| Header k -> k safe_src safe_dst t | |
| MakeBlock block -> make_block safe_src safe_dst t block | |
| WriteBlock k -> k safe_src safe_dst t | |
| FastBlock (ltree, dtree, queue, code, flush) -> | |
fast_block safe_src safe_dst t ltree dtree queue code flush | |
| AlignBlock (f, last) -> align_block f last safe_src safe_dst t | |
| FixedBlock f -> fixed_block f false safe_src safe_dst t | |
| DynamicHeader k -> k safe_src safe_dst t | |
| StaticHeader k -> k safe_src safe_dst t | |
| WriteCrc k -> k safe_src safe_dst t | |
| End -> ok t | |
| Exception exn -> error t exn | |
in | |
let rec loop t = | |
match eval0 t with | |
| Cont t -> loop t | |
| Wait t -> `Await t | |
| Flush t -> `Flush t | |
| Ok t -> `End t | |
| Error (t, exn) -> `Error (t, exn) | |
in | |
loop t | |
let used_in t = t.i_pos | |
let used_out t = t.o_pos | |
let default ~proof ?(wbits = 15) level = | |
{ hold = 0 | |
; bits = 0 | |
; temp = Safe.read_and_write @@ B.from ~proof 0x8000 | |
; o_off = 0 | |
; o_pos = 0 | |
; o_len = 0 | |
; i_off = 0 | |
; i_pos = 0 | |
; i_len = 0 | |
; level | |
; wbits | |
; adler = 1l | |
; state = Header (header wbits (block_of_level ~wbits level))} | |
let to_result src dst refiller flusher t = | |
let rec aux t = match eval src dst t with | |
| `Await t -> | |
let n = refiller src in | |
let t = | |
if n = 0 | |
then finish t | |
else no_flush 0 n t | |
in | |
aux t | |
| `Flush t -> | |
let n = used_out t in | |
let n = flusher dst n in | |
aux (flush 0 n t) | |
| `End t -> | |
if used_out t = 0 | |
then Pervasives.Ok t | |
else let n = flusher dst (used_out t) in | |
Pervasives.Ok (flush 0 n t) | |
| `Error (t, exn) -> Pervasives.Error exn | |
in aux t | |
let bytes src dst refiller flusher t = | |
to_result (B.from_bytes src) (B.from_bytes dst) | |
(function B.Bytes v -> refiller v) | |
(function B.Bytes v -> flusher v) t | |
let bigstring src dst refiller flusher t = | |
to_result (B.from_bigstring src) (B.from_bigstring dst) | |
(function B.Bigstring v -> refiller v) | |
(function B.Bigstring v -> flusher v) t | |
end | |
(** non-blocking and functionnal implementation of Inflate *) | |
module type INFLATE = | |
sig | |
type error = .. | |
type error += Invalid_kind_of_block | |
type error += Invalid_complement_of_length | |
type error += Invalid_dictionary | |
type error += Invalid_crc | |
type ('i, 'o) t | |
val pp_error : Format.formatter -> error -> unit | |
val pp : Format.formatter -> ('i, 'o) t -> unit | |
val eval : 'a B.t -> 'a B.t -> ('a, 'a) t -> | |
[ `Await of ('a, 'a) t | |
| `Flush of ('a, 'a) t | |
| `End of ('a, 'a) t | |
| `Error of ('a, 'a) t * error ] | |
val refill : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val flush : int -> int -> ('i, 'o) t -> ('i, 'o) t | |
val used_in : ('i, 'o) t -> int | |
val used_out : ('i, 'o) t -> int | |
val write : ('i, 'o) t -> int | |
val default : ('i, 'o) t | |
val to_result : 'a B.t -> 'a B.t -> | |
('a B.t -> int) -> | |
('a B.t -> int -> int) -> | |
('a, 'a) t -> (('a, 'a) t, error) result | |
val bytes : Bytes.t -> Bytes.t -> | |
(Bytes.t -> int) -> | |
(Bytes.t -> int -> int) -> | |
(B.st, B.st) t -> ((B.st, B.st) t, error) result | |
val bigstring : B.Bigstring.t -> B.Bigstring.t -> | |
(B.Bigstring.t -> int) -> | |
(B.Bigstring.t -> int -> int) -> | |
(B.bs, B.bs) t -> ((B.bs, B.bs) t, error) result | |
end | |
module Inflate : INFLATE = | |
struct | |
module Adler32 = | |
struct | |
type t = Int32.t | |
let default = 1l | |
let update buf off len crc = crc | |
let atom chr crc = crc | |
let fill chr len crc = crc | |
let make a b = 1l | |
let eq a b = a = b | |
let neq a b = not (eq a b) | |
end | |
(* functionnal implementation of Heap, bisoux @c-cube *) | |
module Heap = | |
struct | |
type priority = int | |
type 'a queue = Empty | Node of priority * 'a * 'a queue * 'a queue | |
let empty = Empty | |
let rec push queue priority elt = | |
match queue with | |
| Empty -> Node (priority, elt, Empty, Empty) | |
| Node (p, e, left, right) -> | |
if priority <= p | |
then Node (priority, elt, push right p e, left) | |
else Node (p, e, push right priority elt, left) | |
exception Empty_heap | |
let rec remove = function | |
| Empty -> raise Empty_heap | |
| Node (p, e, left, Empty) -> left | |
| Node (p, e, Empty, right) -> right | |
| Node (p, e, (Node (lp, le, _, _) as left), | |
(Node (rp, re, _, _) as right)) -> | |
if lp <= rp | |
then Node (lp, le, remove left, right) | |
else Node (rp, re, left, remove right) | |
let take = function | |
| Empty -> raise Empty_heap | |
| Node (p, e, _, _) as queue -> (p, e, remove queue) | |
let to_list heap = | |
let rec aux acc heap = match heap with | |
| Empty -> acc | |
| Node (_, x, l, r) -> x :: aux (aux acc l) r | |
in aux [] heap | |
end | |
module Huffman = | |
struct | |
exception Invalid_huffman | |
let prefix heap max = | |
let tbl = Array.make (1 lsl max) (0, 0) in | |
let rec backward huff incr = | |
if huff land incr <> 0 | |
then backward huff (incr lsr 1) | |
else incr | |
in | |
let rec aux huff heap = match Heap.take heap with | |
| bits, (len, value), heap -> | |
let rec loop decr fill = | |
Array.set tbl (huff + fill) (len, value); | |
if fill <> 0 then loop decr (fill - decr) | |
in | |
let decr = 1 lsl len in | |
loop decr ((1 lsl max) - decr); | |
let incr = backward huff (1 lsl (len - 1)) in | |
aux (if incr <> 0 then (huff land (incr - 1)) + incr else 0) heap | |
| exception Heap.Empty_heap -> () | |
in | |
aux 0 heap; tbl | |
let make table position size max_bits = | |
let bl_count = Array.make (max_bits + 1) 0 in | |
for i = 0 to size - 1 do | |
let p = Array.get table (i + position) in | |
if p >= (max_bits + 1) then raise Invalid_huffman; | |
Array.set bl_count p (Array.get bl_count p + 1); | |
done; | |
let code = ref 0 in | |
let next_code = Array.make (max_bits + 1) 0 in | |
for i = 1 to max_bits - 1 do | |
code := (!code + Array.get bl_count i) lsl 1; | |
Array.set next_code i !code; | |
done; | |
let ordered = ref Heap.Empty in | |
let max = ref 0 in | |
for i = 0 to size - 1 do | |
let l = Array.get table (i + position) in | |
if l <> 0 then begin | |
let n = Array.get next_code (l - 1) in | |
Array.set next_code (l - 1) (n + 1); | |
ordered := Heap.push !ordered n (l, i); | |
max := if l > !max then l else !max; | |
end; | |
done; | |
prefix !ordered !max, !max | |
end | |
module Window = | |
struct | |
type 'a t = | |
{ rpos : int | |
; wpos : int | |
; size : int | |
; buffer : ([ Safe.read | Safe.write ], 'a) Safe.t | |
; crc : Adler32.t } | |
let make_by ~proof size = | |
{ rpos = 0 | |
; wpos = 0 | |
; size = size + 1 | |
; buffer = Safe.read_and_write @@ B.from ~proof (size + 1) | |
; crc = Adler32.default } | |
let available_to_write { wpos; rpos; size; _ } = | |
if wpos >= rpos then size - (wpos - rpos) - 1 | |
else rpos - wpos - 1 | |
let drop n ({ rpos; size; _ } as t) = | |
{ t with rpos = if rpos + n < size then rpos + n | |
else rpos + n - size } | |
let move n ({ wpos; size; _ } as t) = | |
{ t with wpos = if wpos + n < size then wpos + n | |
else wpos + n - size } | |
let write_ro buf off len t = | |
let t = if len > available_to_write t | |
then drop (len - (available_to_write t)) t | |
else t in | |
let pre = t.size - t.wpos in | |
let extra = len - pre in | |
if extra > 0 then begin | |
Safe.blit buf off t.buffer t.wpos pre; | |
Safe.blit buf (off + pre) t.buffer 0 extra; | |
end else | |
Safe.blit buf off t.buffer t.wpos len; | |
move len { t with crc = Adler32.update buf off len t.crc } | |
let write_rw buf off len t = | |
let t = if len > available_to_write t | |
then drop (len - (available_to_write t)) t | |
else t in | |
let pre = t.size - t.wpos in | |
let extra = len - pre in | |
if extra > 0 then begin | |
Safe.blit buf off t.buffer t.wpos pre; | |
Safe.blit buf (off + pre) t.buffer 0 extra; | |
end else begin | |
Safe.blit buf off t.buffer t.wpos len; | |
end; | |
move len t | |
let write_char chr t = | |
let t = if 1 > available_to_write t | |
then drop (1 - (available_to_write t)) t | |
else t in | |
Safe.set t.buffer t.wpos chr; | |
move 1 { t with crc = Adler32.atom chr t.crc } | |
let fill_char chr len t = | |
let t = if len > available_to_write t | |
then drop (len - (available_to_write t)) t | |
else t in | |
let pre = t.size - t.wpos in | |
let extra = len - pre in | |
if extra > 0 then begin | |
Safe.fill t.buffer t.wpos pre chr; | |
Safe.fill t.buffer 0 extra chr; | |
end else | |
Safe.fill t.buffer t.wpos len chr; | |
move len { t with crc = Adler32.fill chr len t.crc } | |
let rec sanitize n ({ size; _ } as t) = | |
if n < 0 then sanitize (size + n) t | |
else if n >= 0 && n < size then n | |
else sanitize (n - size) t | |
let ( % ) n t = sanitize n t | |
let checksum { crc; _ } = crc | |
end | |
type error = .. | |
type error += Invalid_kind_of_block | |
type error += Invalid_complement_of_length | |
type error += Invalid_dictionary | |
type error += Invalid_crc | |
let pp_error fmt = function | |
| Invalid_kind_of_block -> | |
Format.fprintf fmt "Invalid_kind_of_block" | |
| Invalid_complement_of_length -> | |
Format.fprintf fmt "Invalid_complement_of_length" | |
| Invalid_dictionary -> | |
Format.fprintf fmt "Invalid_dictionary" | |
| Invalid_crc -> | |
Format.fprintf fmt "Invalid_crc" | |
| _ -> | |
Format.fprintf fmt "<error>" | |
let reverse_bits = | |
let t = | |
[| 0x00; 0x80; 0x40; 0xC0; 0x20; 0xA0; 0x60; 0xE0; 0x10; 0x90; 0x50; 0xD0; | |
0x30; 0xB0; 0x70; 0xF0; 0x08; 0x88; 0x48; 0xC8; 0x28; 0xA8; 0x68; 0xE8; | |
0x18; 0x98; 0x58; 0xD8; 0x38; 0xB8; 0x78; 0xF8; 0x04; 0x84; 0x44; 0xC4; | |
0x24; 0xA4; 0x64; 0xE4; 0x14; 0x94; 0x54; 0xD4; 0x34; 0xB4; 0x74; 0xF4; | |
0x0C; 0x8C; 0x4C; 0xCC; 0x2C; 0xAC; 0x6C; 0xEC; 0x1C; 0x9C; 0x5C; 0xDC; | |
0x3C; 0xBC; 0x7C; 0xFC; 0x02; 0x82; 0x42; 0xC2; 0x22; 0xA2; 0x62; 0xE2; | |
0x12; 0x92; 0x52; 0xD2; 0x32; 0xB2; 0x72; 0xF2; 0x0A; 0x8A; 0x4A; 0xCA; | |
0x2A; 0xAA; 0x6A; 0xEA; 0x1A; 0x9A; 0x5A; 0xDA; 0x3A; 0xBA; 0x7A; 0xFA; | |
0x06; 0x86; 0x46; 0xC6; 0x26; 0xA6; 0x66; 0xE6; 0x16; 0x96; 0x56; 0xD6; | |
0x36; 0xB6; 0x76; 0xF6; 0x0E; 0x8E; 0x4E; 0xCE; 0x2E; 0xAE; 0x6E; 0xEE; | |
0x1E; 0x9E; 0x5E; 0xDE; 0x3E; 0xBE; 0x7E; 0xFE; 0x01; 0x81; 0x41; 0xC1; | |
0x21; 0xA1; 0x61; 0xE1; 0x11; 0x91; 0x51; 0xD1; 0x31; 0xB1; 0x71; 0xF1; | |
0x09; 0x89; 0x49; 0xC9; 0x29; 0xA9; 0x69; 0xE9; 0x19; 0x99; 0x59; 0xD9; | |
0x39; 0xB9; 0x79; 0xF9; 0x05; 0x85; 0x45; 0xC5; 0x25; 0xA5; 0x65; 0xE5; | |
0x15; 0x95; 0x55; 0xD5; 0x35; 0xB5; 0x75; 0xF5; 0x0D; 0x8D; 0x4D; 0xCD; | |
0x2D; 0xAD; 0x6D; 0xED; 0x1D; 0x9D; 0x5D; 0xDD; 0x3D; 0xBD; 0x7D; 0xFD; | |
0x03; 0x83; 0x43; 0xC3; 0x23; 0xA3; 0x63; 0xE3; 0x13; 0x93; 0x53; 0xD3; | |
0x33; 0xB3; 0x73; 0xF3; 0x0B; 0x8B; 0x4B; 0xCB; 0x2B; 0xAB; 0x6B; 0xEB; | |
0x1B; 0x9B; 0x5B; 0xDB; 0x3B; 0xBB; 0x7B; 0xFB; 0x07; 0x87; 0x47; 0xC7; | |
0x27; 0xA7; 0x67; 0xE7; 0x17; 0x97; 0x57; 0xD7; 0x37; 0xB7; 0x77; 0xF7; | |
0x0F; 0x8F; 0x4F; 0xCF; 0x2F; 0xAF; 0x6F; 0xEF; 0x1F; 0x9F; 0x5F; 0xDF; | |
0x3F; 0xBF; 0x7F; 0xFF |] | |
in | |
fun bits -> t.(bits) | |
module Lookup = | |
struct | |
type t = | |
{ table : (int * int) array | |
; max : int | |
; mask : int } | |
let make table max = | |
{ table; max; mask = (1 lsl max) - 1; } | |
let fixed_chr = | |
let tbl = | |
Array.init 288 | |
(fun n -> if n < 144 then 8 | |
else if n < 256 then 9 | |
else if n < 280 then 7 | |
else 8) | |
in | |
let tbl, max = Huffman.make tbl 0 288 9 in | |
make tbl max | |
let fixed_dst = | |
let tbl = Array.make (1 lsl 5) (0, 0) in | |
Array.iteri (fun i _ -> Array.set tbl i (5, reverse_bits (i lsl 3))) tbl; | |
make tbl 5 | |
end | |
type ('i, 'o) t = | |
{ last : bool | |
; hold : int | |
; bits : int | |
; o_off : int | |
; o_pos : int | |
; o_len : int | |
; i_off : int | |
; i_pos : int | |
; i_len : int | |
; write : int | |
; state : ('i, 'o) state } | |
and ('i, 'o) k = (Safe.read, 'i) Safe.t -> | |
(Safe.write, 'o) Safe.t -> | |
('i, 'o) t -> | |
('i, 'o) res | |
and ('i, 'o) state = | |
| Header of ('i, 'o) k | |
| Last of 'o Window.t | |
| Block of 'o Window.t | |
| Flat of ('i, 'o) k | |
| Fixed of 'o Window.t | |
| Dictionary of ('i, 'o) k | |
| Inffast of ('o Window.t * Lookup.t * Lookup.t * code) | |
| Inflate of ('i, 'o) k | |
| Switch of 'o Window.t | |
| Crc of ('i, 'o) k | |
| Exception of error | |
and ('i, 'o) res = | |
| Cont of ('i, 'o) t | |
| Wait of ('i, 'o) t | |
| Flush of ('i, 'o) t | |
| Ok of ('i, 'o) t | |
| Error of ('i, 'o) t * error | |
and code = | |
| Length | |
| ExtLength of int | |
| Dist of int | |
| ExtDist of int * int | |
| Write of int * int | |
let pp_code fmt = function | |
| Length -> Format.fprintf fmt "Length" | |
| ExtLength c -> Format.fprintf fmt "(ExtLength %d)" c | |
| Dist c -> Format.fprintf fmt "(Dist %d)" c | |
| ExtDist (a, b) -> Format.fprintf fmt "(ExtDist (%d, %d))" a b | |
| Write (a, b) -> Format.fprintf fmt "(Write (%d, %d))" a b | |
let pp_state fmt = function | |
| Header k -> Format.fprintf fmt "(Header #fun)" | |
| Last w -> Format.fprintf fmt "(Last #window)" | |
| Block w -> Format.fprintf fmt "(Block #fun)" | |
| Flat k -> Format.fprintf fmt "(Flat #fun)" | |
| Fixed w -> Format.fprintf fmt "(Fixed #window)" | |
| Dictionary k -> Format.fprintf fmt "(Dictionary #fun)" | |
| Inffast (w, l, d, c) -> Format.fprintf fmt "(Inffast %a)" pp_code c | |
| Inflate k -> Format.fprintf fmt "(Inflate #fun)" | |
| Switch w -> Format.fprintf fmt "(Switch #window)" | |
| Crc w -> Format.fprintf fmt "(Crc #window)" | |
| Exception e -> Format.fprintf fmt "(Exception %a)" pp_error e | |
let pp fmt { last; hold; bits | |
; o_off; o_pos; o_len | |
; i_off; i_pos; i_len; write | |
; state } = | |
Format.fprintf fmt "{@[<hov>last = %b;@ \ | |
hold = %d;@ \ | |
bits = %d;@ \ | |
o_off = %d;@ \ | |
o_pos = %d;@ \ | |
o_len = %d;@ \ | |
i_off = %d;@ \ | |
i_pos = %d;@ \ | |
i_len = %d;@ \ | |
write = %d; | |
state = %a@]}" | |
last hold bits | |
o_off o_pos o_len i_off i_pos i_len write | |
pp_state state | |
let error t exn = | |
Error ({ t with state = Exception exn }, exn) | |
module KHeader = | |
struct | |
let rec get_byte k src dst t = | |
if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
k byte src dst | |
{ t with i_pos = t.i_pos + 1 } | |
else Wait { t with state = Header (get_byte k) } | |
end | |
(* Continuation passing-style stored in [Dictionary] *) | |
module KDictionary = | |
struct | |
let rec get_byte k src dst t = | |
if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
k byte src dst | |
{ t with i_pos = t.i_pos + 1 } | |
else Wait { t with state = Dictionary (get_byte k) } | |
let peek_bits n k src dst t = | |
let rec loop src dst t = | |
if t.bits < n | |
then get_byte (fun byte src dst t -> | |
(loop[@tailcall]) | |
src dst | |
{ t with hold = t.hold lor (byte lsl t.bits) | |
; bits = t.bits + 8 }) | |
src dst t | |
else k src dst t | |
in (loop[@tailcall]) src dst t | |
let drop_bits n k src dst t = | |
k src dst | |
{ t with hold = t.hold lsr n | |
; bits = t.bits - n } | |
let get_bits n k src dst t = | |
let catch src dst t = | |
let value = t.hold land ((1 lsl n) - 1) in | |
k value src dst { t with hold = t.hold lsr n | |
; bits = t.bits - n } | |
in | |
let rec loop src dst t = | |
if t.bits < n | |
then get_byte (fun byte src dst t -> | |
(loop[@tailcall]) | |
src dst | |
{ t with hold = t.hold lor (byte lsl t.bits) | |
; bits = t.bits + 8 }) | |
src dst t | |
else catch src dst t | |
in (loop[@tailcall]) src dst t | |
end | |
(* Continuation passing-style stored in [Flat] *) | |
module KFlat = | |
struct | |
let rec get_byte k src dst t = | |
if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
k byte src dst | |
{ t with i_pos = t.i_pos + 1 } | |
else Wait { t with state = Flat (get_byte k) } | |
let drop_bits n k src dst t = | |
k src dst { t with hold = t.hold lsr n | |
; bits = t.bits - n } | |
let rec get_byte k src dst t = | |
if t.bits / 8 > 0 | |
then let byte = t.hold land 255 in | |
k byte src dst { t with hold = t.hold lsr 8 | |
; bits = t.bits - 8 } | |
else if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
k byte src dst | |
{ t with i_pos = t.i_pos + 1 } | |
else Wait { t with state = Flat (get_byte k) } | |
let get_ui16 k = | |
get_byte | |
@@ fun byte0 -> get_byte | |
@@ fun byte1 -> k (byte0 lor (byte1 lsl 8)) | |
end | |
(* Continuation passing-style stored in [Inflate] *) | |
module KInflate = | |
struct | |
let rec get lookup k src dst t = | |
if t.bits < lookup.Lookup.max | |
then | |
if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
(get[@tailcall]) lookup k src dst | |
{ t with i_pos = t.i_pos + 1 | |
; hold = t.hold lor (byte lsl t.bits) | |
; bits = t.bits + 8 } | |
else Wait { t with state = Inflate (get lookup k) } | |
else let (len, v) = Array.get | |
lookup.Lookup.table (t.hold land lookup.Lookup.mask) in | |
k v src dst { t with hold = t.hold lsr len | |
; bits = t.bits - len } | |
let rec put_chr window chr k src dst t = | |
if t.o_len - t.o_pos > 0 | |
then begin | |
let window = Window.write_char chr window in | |
Safe.set dst (t.o_off + t.o_pos) chr; | |
k window src dst { t with o_pos = t.o_pos + 1 } | |
end else Flush { t with state = Inflate (put_chr window chr k) } | |
let rec fill_chr window length chr k src dst t = | |
if t.o_len - t.o_pos > 0 | |
then begin | |
let len = min length (t.o_len - t.o_pos) in | |
let window = Window.fill_char chr len window in | |
Safe.fill dst (t.o_off + t.o_pos) len chr; | |
if length - len > 0 | |
then Flush | |
{ t with o_pos = t.o_pos + len | |
; state = Inflate (fill_chr window (length - len) chr k) } | |
else k window src dst { t with o_pos = t.o_pos + len } | |
end else Flush { t with state = Inflate (fill_chr window length chr k) } | |
let rec write window lookup_chr lookup_dst length distance k src dst t = | |
match distance with | |
| 1 -> | |
let chr = Safe.get window.Window.buffer | |
Window.((window.wpos - 1) % window) in | |
fill_chr window length chr k src dst t | |
| distance -> | |
let len = min (t.o_len - t.o_pos) length in | |
let off = Window.((window.wpos - distance) % window) in | |
let sze = window.Window.size in | |
let pre = sze - off in | |
let ext = len - pre in | |
let window = | |
if ext > 0 | |
then begin | |
let window0 = | |
Window.write_rw window.Window.buffer off pre window in | |
Safe.blit window0.Window.buffer off dst (t.o_off + t.o_pos) pre; | |
let window1 = | |
Window.write_rw window0.Window.buffer 0 ext window0 in | |
Safe.blit window1.Window.buffer 0 dst (t.o_off + t.o_pos + pre) ext; | |
window1 | |
end else begin | |
let window0 = | |
Window.write_rw window.Window.buffer off len window in | |
Safe.blit window0.Window.buffer off dst (t.o_off + t.o_pos) len; | |
window0 | |
end | |
in | |
if length - len > 0 | |
then Flush | |
{ t with o_pos = t.o_pos + len | |
; write = t.write + len | |
; state = Inflate (write | |
window | |
lookup_chr | |
lookup_dst | |
(length - len) | |
distance | |
k) } | |
else Cont | |
{ t with o_pos = t.o_pos + len | |
; write = t.write + len | |
; state = Inffast (window, lookup_chr, lookup_dst, Length) } | |
let rec read_extra_dist distance k src dst t = | |
let len = Array.get Table._extra_dbits distance in | |
if t.bits < len | |
then if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
read_extra_dist | |
distance k | |
src dst | |
{ t with hold = t.hold lor (byte lsl t.bits) | |
; bits = t.bits + 8 | |
; i_pos = t.i_pos + 1 } | |
else Wait | |
{ t with state = Inflate (read_extra_dist distance k) } | |
else let extra = t.hold land ((1 lsl len) - 1) in | |
k (Array.get Table._base_dist distance + 1 + extra) src dst | |
{ t with hold = t.hold lsr len | |
; bits = t.bits - len } | |
let rec read_extra_length length k src dst t = | |
let len = Array.get Table._extra_lbits length in | |
if t.bits < len | |
then if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
read_extra_length | |
length k | |
src dst | |
{ t with hold = t.hold lor (byte lsl t.bits) | |
; bits = t.bits + 8 | |
; i_pos = t.i_pos + 1 } | |
else Wait | |
{ t with state = Inflate (read_extra_length length k) } | |
else let extra = t.hold land ((1 lsl len) - 1) in | |
k ((Array.get Table._base_length length) + 3 + extra) src dst | |
{ t with hold = t.hold lsr len | |
; bits = t.bits - len } | |
end | |
(* Continuation passing-style stored in [Crc] *) | |
module KCrc = | |
struct | |
let drop_bits n k src dst t = | |
k src dst { t with hold = t.hold lsr n | |
; bits = t.bits - n } | |
let rec get_byte k src dst t = | |
if t.bits / 8 > 0 | |
then let byte = t.hold land 255 in | |
k byte src dst { t with hold = t.hold lsr 8 | |
; bits = t.bits - 8 } | |
else if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
k byte src dst | |
{ t with i_pos = t.i_pos + 1 } | |
else Wait { t with state = Crc (get_byte k) } | |
end | |
(* Dictionary *) | |
module Dictionary = | |
struct | |
type t = | |
{ idx : int | |
; prv : int | |
; max : int | |
; dictionary : int array } | |
let make max = | |
{ idx = 0 | |
; prv = 0 | |
; max | |
; dictionary = Array.make max 0 } | |
let inflate (tbl, max_bits, max) k src dst t = | |
let mask_bits = (1 lsl max_bits) - 1 in | |
let rec get k src dst t = | |
if t.bits < max_bits | |
then KDictionary.peek_bits max_bits | |
(fun src dst t -> (get[@tailcall]) k src dst t) src dst t | |
else let (len, v) = Array.get tbl (t.hold land mask_bits) in | |
KDictionary.drop_bits len (k v) src dst t | |
in | |
let rec loop state value src dst t = match value with | |
| n when n <= 15 -> | |
Array.set state.dictionary state.idx n; | |
if state.idx + 1 < state.max | |
then get (fun src dst t -> (loop[@tailcall]) | |
{ state with idx = state.idx + 1 | |
; prv = n } | |
src dst t) src dst t | |
else k state.dictionary src dst t | |
| 16 -> | |
let aux n src dst t = | |
if state.idx + n + 3 > state.max | |
then error t Invalid_dictionary | |
else begin | |
for j = 0 to n + 3 - 1 | |
do Array.set state.dictionary (state.idx + j) state.prv done; | |
if state.idx + n + 3 < state.max | |
then get (fun src dst t -> (loop[@tailcall]) | |
{ state with idx = state.idx + n + 3 } | |
src dst t) src dst t | |
else k state.dictionary src dst t | |
end | |
in | |
KDictionary.get_bits 2 aux src dst t | |
| 17 -> | |
let aux n src dst t = | |
if state.idx + n + 3 > state.max | |
then error t Invalid_dictionary | |
else begin | |
if state.idx + n + 3 < state.max | |
then get (fun src dst t -> (loop[@tailcall]) | |
{ state with idx = state.idx + n + 3 } | |
src dst t) src dst t | |
else k state.dictionary src dst t | |
end | |
in | |
KDictionary.get_bits 3 aux src dst t | |
| 18 -> | |
let aux n src dst t = | |
if state.idx + n + 11 > state.max | |
then error t Invalid_dictionary | |
else begin | |
if state.idx + n + 11 < state.max | |
then get ((loop[@tailclal]) | |
{ state with idx = state.idx + n + 11 }) src dst t | |
else k state.dictionary src dst t | |
end | |
in | |
KDictionary.get_bits 7 aux src dst t | |
| _ -> error t Invalid_dictionary | |
in | |
get (fun src dst t -> (loop[@tailcall]) (make max) src dst t) src dst t | |
end | |
let fixed src dst t window = | |
Cont { t with state = Inffast (window, | |
Lookup.fixed_chr, | |
Lookup.fixed_dst, | |
Length) } | |
let dictionary window src dst t = | |
let make_table hlit hdist hclen buf src dst t = | |
let tbl, max = Huffman.make buf 0 19 7 in | |
Dictionary.inflate (tbl, max, hlit + hdist) | |
(fun dict src dst t -> | |
let tbl_chr, max_chr = Huffman.make dict 0 hlit 15 in | |
let tbl_dst, max_dst = Huffman.make dict hlit hdist 15 in | |
Cont { t with state = Inffast (window, | |
Lookup.make tbl_chr max_chr, | |
Lookup.make tbl_dst max_dst, | |
Length) }) | |
src dst t | |
in | |
let read_table hlit hdist hclen src dst t = | |
let buf = Array.make 19 0 in | |
let rec loop idx code src dst t = | |
Array.set buf (Array.get Table._hclen_order idx) code; | |
if idx + 1 = hclen | |
then begin | |
for i = hclen to 18 | |
do Array.set buf (Array.get Table._hclen_order i) 0 done; | |
make_table hlit hdist hclen buf src dst t | |
end else | |
KDictionary.get_bits 3 | |
(fun src dst t -> (loop[@tailcall]) (idx + 1) src dst t) src dst t | |
in | |
KDictionary.get_bits 3 | |
(fun src dst t -> (loop[@tailcall]) 0 src dst t) | |
src dst t | |
in | |
let read_hclen hlit hdist = KDictionary.get_bits 4 | |
(fun hclen -> read_table hlit hdist (hclen + 4)) in | |
let read_hdist hlit = KDictionary.get_bits 5 | |
(fun hdist -> read_hclen hlit (hdist + 1)) in | |
let read_hlit = KDictionary.get_bits 5 | |
(fun hlit -> read_hdist (hlit + 257)) in | |
read_hlit src dst t | |
let rec ok src dst t = | |
Ok { t with state = Crc ok } | |
let crc window src dst t = | |
let crc = Window.checksum window in | |
(KCrc.drop_bits (t.bits mod 8) | |
@@ KCrc.get_byte | |
@@ fun a1 -> KCrc.get_byte | |
@@ fun a2 -> KCrc.get_byte | |
@@ fun b1 -> KCrc.get_byte | |
@@ fun b2 src dst t -> | |
if Adler32.neq | |
(Adler32.make ((a1 lsl 8) lor a2) ((b1 lsl 8) lor b2)) | |
crc | |
then ok src dst t (* TODO *) | |
else ok src dst t) src dst t | |
let switch src dst t window = | |
if t.last | |
then Cont { t with state = Crc (crc window) } | |
else Cont { t with state = Last window } | |
let flat window src dst t = | |
let rec loop window length src dst t = | |
let n = min length (min (t.i_len - t.i_pos) (t.o_len - t.o_pos)) in | |
let window = Window.write_ro src (t.i_off + t.i_pos) n window in | |
Safe.blit src (t.i_off + t.i_pos) dst (t.o_off + t.o_pos) n; | |
if length - n = 0 | |
then Cont { t with i_pos = t.i_pos + n | |
; o_pos = t.o_pos + n | |
; state = Switch window } | |
else match t.i_len - (t.i_pos + n), t.o_len - (t.o_pos + n) with | |
| 0, b -> | |
Wait { t with i_pos = t.i_pos + n | |
; o_pos = t.o_pos + n | |
; state = Flat (loop window (length - n)) } | |
| a, 0 -> | |
Flush { t with i_pos = t.i_pos + n | |
; o_pos = t.o_pos + n | |
; state = Flat (loop window (length - n)) } | |
| a, b -> | |
Cont { t with i_pos = t.i_pos + n | |
; o_pos = t.o_pos + n | |
; state = Flat (loop window (length - n)) } | |
in | |
let header window len nlen src dst t = | |
if nlen <> 0xFFFF - len | |
then Cont { t with state = Exception Invalid_complement_of_length } | |
else Cont { t with hold = 0 | |
; bits = 0 | |
; state = Flat (loop window len) } | |
in | |
(KFlat.drop_bits (t.bits mod 8) | |
@@ KFlat.get_ui16 | |
@@ fun len -> KFlat.get_ui16 | |
@@ fun nlen -> header window len nlen) | |
src dst t | |
let rec inflate window lookup_chr lookup_dst src dst t = | |
let rec loop window length src dst t = match length with | |
| literal when literal < 256 -> | |
KInflate.put_chr window (Char.chr literal) | |
(fun window src dst t -> KInflate.get lookup_chr | |
(fun length src dst t -> (loop[@tailcall]) window length src dst t) | |
src dst t) | |
src dst t | |
| 256 -> | |
Cont { t with state = Switch window } | |
| length -> | |
(* Party-hard *) | |
KInflate.read_extra_length (length - 257) | |
(fun length src dst t -> KInflate.get lookup_dst | |
(fun distance src dst t -> KInflate.read_extra_dist distance | |
(fun distance src dst t -> KInflate.write | |
window lookup_chr lookup_dst length distance | |
(fun window src dst t -> (inflate[@tailcall]) | |
window lookup_chr lookup_dst src dst t) | |
src dst t) | |
src dst t) | |
src dst t) | |
src dst t | |
in | |
KInflate.get | |
lookup_chr | |
(fun length src dst t -> (loop[@tailcall]) window length src dst t) | |
src dst t | |
exception End | |
let inffast src dst t window lookup_chr lookup_dst goto = | |
let hold = ref t.hold in | |
let bits = ref t.bits in | |
let goto = ref goto in | |
let i_pos = ref t.i_pos in | |
let o_pos = ref t.o_pos in | |
let write = ref t.write in | |
let window = ref window in | |
try | |
while (t.i_len - !i_pos) > 1 && t.o_len - !o_pos > 0 | |
do match !goto with | |
| Length -> | |
if !bits < lookup_chr.Lookup.max | |
then begin | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
end; | |
let (len, value) = Array.get | |
lookup_chr.Lookup.table | |
(!hold land lookup_chr.Lookup.mask) | |
in | |
hold := !hold lsr len; | |
bits := !bits - len; | |
if value < 256 | |
then begin | |
Safe.set dst (t.o_off + !o_pos) (Char.chr value); | |
window := Window.write_char (Char.chr value) !window; | |
incr o_pos; | |
incr write; | |
goto := Length; | |
end else if value = 256 then begin raise End | |
end else begin | |
goto := ExtLength (value - 257) | |
end | |
| ExtLength length -> | |
let len = Array.get Table._extra_lbits length in | |
if !bits < len | |
then begin | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
end; | |
let extra = !hold land ((1 lsl len) - 1) in | |
hold := !hold lsr len; | |
bits := !bits - len; | |
goto := Dist ((Array.get Table._base_length length) + 3 + extra) | |
| Dist length -> | |
if !bits < lookup_dst.Lookup.max | |
then begin | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
end; | |
let (len, value) = Array.get | |
lookup_dst.Lookup.table | |
(!hold land lookup_dst.Lookup.mask) | |
in | |
hold := !hold lsr len; | |
bits := !bits - len; | |
goto := ExtDist (length, value) | |
| ExtDist (length, dist) -> | |
let len = Array.get Table._extra_dbits dist in | |
if !bits < len | |
then begin | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
hold := !hold lor ((Char.code | |
@@ Safe.get src (t.i_off + !i_pos)) lsl !bits); | |
bits := !bits + 8; | |
incr i_pos; | |
end; | |
let extra = !hold land ((1 lsl len) - 1) in | |
hold := !hold lsr len; | |
bits := !bits - len; | |
goto := Write (length, | |
(Array.get Table._base_dist dist) + 1 + extra) | |
| Write (length, 1) -> | |
let chr = Safe.get !window.Window.buffer | |
Window.((!window.wpos - 1) % !window) in | |
let n = min length (t.o_len - !o_pos) in | |
window := Window.fill_char chr n !window; | |
Safe.fill dst (t.o_off + !o_pos) n chr; | |
o_pos := !o_pos + n; | |
write := !write + n; | |
goto := if length - n = 0 then Length else Write (length - n, 1) | |
| Write (length, dist) -> | |
let n = min length (t.o_len - !o_pos) in | |
let off = Window.((!window.Window.wpos - dist) % !window) in | |
let len = !window.Window.size in | |
let pre = len - off in | |
let ext = n - pre in | |
window := if ext > 0 | |
then begin | |
let window0 = | |
Window.write_rw !window.Window.buffer off pre !window in | |
Safe.blit window0.Window.buffer off dst (t.o_off + !o_pos) pre; | |
let window1 = | |
Window.write_rw window0.Window.buffer 0 ext window0 in | |
Safe.blit window1.Window.buffer 0 dst (t.o_off + !o_pos + pre) ext; | |
window1 | |
end else begin | |
let window0 = | |
Window.write_rw !window.Window.buffer off n !window in | |
Safe.blit window0.Window.buffer off dst (t.o_off + !o_pos) n; | |
window0 | |
end; | |
o_pos := !o_pos + n; | |
write := !write + n; | |
goto := if length - n = 0 then Length else Write (length - n, dist) | |
done; | |
let write_fn length distance src dst t = | |
KInflate.write !window lookup_chr lookup_dst length distance | |
(fun window src dst t -> | |
inflate window lookup_chr lookup_dst src dst t) | |
src dst t | |
in | |
let state = match !goto with | |
| Length -> | |
Inflate (inflate !window lookup_chr lookup_dst) | |
| ExtLength length -> | |
let fn length src dst t = | |
KInflate.read_extra_length length | |
(fun length src dst t -> KInflate.get lookup_dst | |
(fun distance src dst t -> KInflate.read_extra_dist distance | |
(fun distance src dst t -> | |
write_fn length distance src dst t) | |
src dst t) | |
src dst t) | |
src dst t | |
in | |
Inflate (fn length) | |
| Dist length -> | |
let fn length src dst t = | |
KInflate.get lookup_dst | |
(fun distance src dst t -> KInflate.read_extra_dist distance | |
(fun distance src dst t -> write_fn length distance src dst t) | |
src dst t) | |
src dst t | |
in | |
Inflate (fn length) | |
| ExtDist (length, distance) -> | |
let fn length distance src dst t = | |
KInflate.read_extra_dist distance | |
(fun distance src dst t -> write_fn length distance src dst t) | |
src dst t | |
in | |
Inflate (fn length distance) | |
| Write (length, distance) -> | |
let fn length distance src dst t = | |
write_fn length distance src dst t in | |
Inflate (fn length distance) | |
in | |
Cont { t with hold = !hold | |
; bits = !bits | |
; i_pos = !i_pos | |
; o_pos = !o_pos | |
; write = !write | |
; state = state } | |
with End -> | |
Cont { t with hold = !hold | |
; bits = !bits | |
; i_pos = !i_pos | |
; o_pos = !o_pos | |
; write = !write | |
; state = Switch !window } | |
let block src dst t window = | |
if t.bits > 1 | |
then let state = match t.hold land 0x3 with | |
| 0 -> Flat (flat window) | |
| 1 -> Fixed window | |
| 2 -> Dictionary (dictionary window) | |
| _ -> Exception Invalid_kind_of_block | |
in | |
Cont { t with hold = t.hold lsr 2 | |
; bits = t.bits - 2 | |
; state } | |
else if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
Cont { t with i_pos = t.i_pos + 1 | |
; hold = (t.hold lor (byte lsl t.bits)) | |
; bits = t.bits + 8 } | |
else Wait t | |
let last src dst t window = | |
if t.bits > 0 | |
then let last = t.hold land 1 = 1 in | |
Cont { t with last = last | |
; hold = t.hold lsr 1 | |
; bits = t.bits - 1 | |
; state = Block window } | |
else if (t.i_len - t.i_pos) > 0 | |
then let byte = Char.code @@ Safe.get src (t.i_off + t.i_pos) in | |
Cont { t with i_pos = t.i_pos + 1 | |
; hold = (t.hold lor (byte lsl t.bits)) | |
; bits = t.bits + 8 } | |
else Wait t | |
let header src dst t = | |
(KHeader.get_byte | |
@@ fun byte0 -> KHeader.get_byte | |
@@ fun byte1 src dst t -> | |
let window = Window.make_by ~proof:(Safe.proof dst) (1 lsl (byte0 lsr 4 + 8)) in | |
Cont { t with state = Last window }) | |
src dst t | |
let eval src dst t = | |
let safe_src = Safe.read_only src in | |
let safe_dst = Safe.write_only dst in | |
let eval0 t = | |
match t.state with | |
| Header k -> k safe_src safe_dst t | |
| Last window -> last safe_src safe_dst t window | |
| Block window -> block safe_src safe_dst t window | |
| Flat k -> k safe_src safe_dst t | |
| Fixed window -> fixed safe_src safe_dst t window | |
| Dictionary k -> k safe_src safe_dst t | |
| Inffast (window, lookup_chr, lookup_dst, code) -> | |
inffast safe_src safe_dst t window lookup_chr lookup_dst code | |
| Inflate k -> k safe_src safe_dst t | |
| Switch window -> switch safe_src safe_dst t window | |
| Crc k -> k safe_src safe_dst t | |
| Exception exn -> error t exn | |
in | |
let rec loop t = | |
match eval0 t with | |
| Cont t -> loop t | |
| Wait t -> `Await t | |
| Flush t -> `Flush t | |
| Ok t -> `End t | |
| Error (t, exn) -> `Error (t, exn) | |
in | |
loop t | |
let default = | |
{ last = false | |
; hold = 0 | |
; bits = 0 | |
; i_off = 0 | |
; i_pos = 0 | |
; i_len = 0 | |
; o_off = 0 | |
; o_pos = 0 | |
; o_len = 0 | |
; write = 0 | |
; state = Header header } | |
let refill off len t = | |
if t.i_pos = t.i_len | |
then { t with i_off = off | |
; i_len = len | |
; i_pos = 0 } | |
else raise (Invalid_argument (Format.sprintf "Z.refill: you lost something \ | |
(pos: %d, len: %d)" | |
t.i_pos t.i_len)) | |
let flush off len t = | |
{ t with o_off = off | |
; o_len = len | |
; o_pos = 0 } | |
let used_in t = t.i_pos | |
let used_out t = t.o_pos | |
let write t = t.write | |
let rec to_result src dst refiller flusher t = | |
let rec aux t = match eval src dst t with | |
| `Await t -> | |
let n = refiller src in | |
aux (refill 0 n t) | |
| `Flush t -> | |
let n = used_out t in | |
let n = flusher dst n in | |
aux (flush 0 n t) | |
| `End t -> | |
if used_out t = 0 | |
then Pervasives.Ok t | |
else let n = flusher dst (used_out t) in | |
Pervasives.Ok (flush 0 n t) | |
| `Error (t, exn) -> Pervasives.Error exn | |
in aux t | |
let bytes src dst refiller flusher t = | |
to_result (B.from_bytes src) (B.from_bytes dst) | |
(function B.Bytes v -> refiller v) | |
(function B.Bytes v -> flusher v) t | |
let bigstring src dst refiller flusher t = | |
to_result (B.from_bigstring src) (B.from_bigstring dst) | |
(function B.Bigstring v -> refiller v) | |
(function B.Bigstring v -> flusher v) t | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(** See [bs.c]. *) | |
external bs_read : Unix.file_descr -> B.Bigstring.t -> int -> int -> int = | |
"bigstring_read" [@@noalloc] | |
external bs_write : Unix.file_descr -> B.Bigstring.t -> int -> int -> int = | |
"bigstring_write" [@@noalloc] | |
(** Abstract [Unix.read] with ['a B.t]. *) | |
let unix_read (type a) ch (tmp : a B.t) off len = match tmp with | |
| B.Bytes v -> Unix.read ch v off len | |
| B.Bigstring v -> bs_read ch v off len | |
let unix_write (type a) ch (tmp : a B.t) off len = match tmp with | |
| B.Bytes v -> Unix.write ch v off len | |
| B.Bigstring v -> bs_write ch v off len | |
let _chunk = 0xFFFF | |
let () = | |
let src = B.from_bigstring @@ B.Bigstring.create _chunk in | |
let dst = B.from_bigstring @@ B.Bigstring.create _chunk in | |
if Array.length Sys.argv = 1 | |
then let t = Decompress.Deflate.default ~proof:src ~wbits:15 5 in | |
let _ = Decompress.Deflate.to_result | |
src dst | |
(fun src -> unix_read Unix.stdin src 0 _chunk) | |
(fun dst len -> let _ = unix_write Unix.stdout dst 0 len in _chunk) | |
t | |
in () | |
else let t = Decompress.Inflate.default in | |
let _ = Decompress.Inflate.to_result | |
src dst | |
(fun src -> unix_read Unix.stdin src 0 _chunk) | |
(fun dst len -> let _ = unix_write Unix.stdout dst 0 len in _chunk) | |
t | |
in () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open Ocamlbuild_plugin | |
let () = dispatch @@ function | |
| After_hygiene -> | |
pdep [ "link" ] "linkdep" (fun param -> [ param ]) | |
| _ -> () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* Copyright (c) 2013, Simon Cruanes | |
All rights reserved. | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions are met: | |
Redistributions of source code must retain the above copyright notice, this | |
list of conditions and the following disclaimer. Redistributions in binary | |
form must reproduce the above copyright notice, this list of conditions and | |
the following disclaimer in the documentation and/or other materials | |
provided with the distribution. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) | |
type 'a digit = | |
| Zero | |
| One of 'a | |
| Two of 'a * 'a | |
| Three of 'a * 'a * 'a | |
type 'a t = | |
| Shallow of 'a digit | |
| Deep of int * 'a digit * ('a * 'a) t Lazy.t * 'a digit | |
let empty = Shallow Zero | |
let is_empty = function Shallow Zero -> true | _ -> false | |
let _single x = Shallow (One x) | |
let _double x y = Shallow (Two (x, y)) | |
let _three x y z = Shallow (Three (x, y, z)) | |
let _deep n hd middle tl = | |
assert (hd <> Zero && tl <> Zero); | |
Deep (n, hd, middle, tl) | |
let _empty = Lazy.from_val empty | |
let rec cons | |
: 'a. 'a -> 'a t -> 'a t | |
= fun x q -> match q with | |
| Shallow Zero -> _single x | |
| Shallow (One y) -> _double x y | |
| Shallow (Two (y, z)) -> _three x y z | |
| Shallow (Three (y, z, z')) -> | |
_deep 4 (Two (x, y)) _empty (Two (z, z')) | |
| Deep (_, Zero, _middle, _tl) -> assert false | |
| Deep (n, One y, _middle, _tl) -> | |
_deep (n + 1) (Two (x, y)) _middle _tl | |
| Deep (n, Two (y, z), _middle, _tl) -> | |
_deep (n + 1) (Three (x, y, z)) _middle _tl | |
| Deep (n, Three (y, z, z'), lazy _middle, _tl) -> | |
_deep (n + 1) (Two (x, y)) (lazy (cons (z, z') _middle)) _tl | |
exception Empty | |
let rec take_front_exn | |
: 'a. 'a t -> ('a * 'a t) | |
= fun q -> match q with | |
| Shallow Zero -> raise Empty | |
| Shallow (One x) -> x, empty | |
| Shallow (Two (x, y)) -> x, Shallow (One y) | |
| Shallow (Three (x, y, z)) -> x, Shallow (Two (y, z)) | |
| Deep (_, Zero, _, _) -> assert false | |
| Deep (n, One x, lazy _middle, _tail) -> | |
if is_empty _middle | |
then x, Shallow _tail | |
else | |
let (y, z), _middle = take_front_exn _middle in | |
x, _deep (n - 1) (Two (y, z)) (Lazy.from_val _middle) _tail | |
| Deep (n, Two (x, y), _middle, _tail) -> | |
x, _deep (n - 1) (One y) _middle _tail | |
| Deep (n, Three (x, y, z), _middle, _tail) -> | |
x, _deep (n - 1) (Two (y, z)) _middle _tail | |
let take_front q = | |
try Some (take_front_exn q) | |
with Empty -> None | |
let add_seq_front seq q = | |
let l = ref [] in | |
seq (fun x -> l := x :: !l); | |
List.fold_left (fun q x -> cons x q) q !l | |
let of_seq seq = add_seq_front seq empty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type read = [ `Read ] | |
type write = [ `Write ] | |
type ('a, 'i) t = 'i B.t constraint 'a = [< `Read | `Write ] | |
let read_and_write : 'i B.t -> ([ read | write ], 'i) t = fun x -> x | |
let read_only : 'i B.t -> (read, 'i) t = fun x -> x | |
let write_only : 'i B.t -> (write, 'i) t = fun x -> x | |
let length = B.length | |
let get = B.get | |
let set = B.set | |
let get_u16 = B.get_u16 | |
let get_u32 = B.get_u32 | |
let get_u64 = B.get_u64 | |
let sub = B.sub | |
let fill = B.fill | |
let blit = B.blit | |
let pp = B.pp | |
let tpp = B.tpp | |
let to_string = B.to_string | |
let adler32 = B.adler32 | |
external proof : ('a, 'i) t -> 'i B.t = "%identity" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* Copyright (c) 2012-2016, Simon Cruanes | |
All rights reserved. | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions are met: | |
Redistributions of source code must retain the above copyright notice, this | |
list of conditions and the following disclaimer. Redistributions in binary | |
form must reproduce the above copyright notice, this list of conditions and | |
the following disclaimer in the documentation and/or other materials | |
provided with the distribution. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) | |
type 'a t = ('a -> unit) -> unit | |
let empty _ = () | |
let cons x l k = k x; l k | |
let snoc l x k = l k; k x | |
module MList = | |
struct | |
type 'a node = | |
| Nil | |
| Cons of 'a array * int ref * 'a node ref | |
let of_seq_with seq k = | |
let start = ref Nil in | |
let chunk_size = ref 8 in | |
(* XXX: fill the list. pref: tail-reference from previous node *) | |
let prev, cur = ref start, ref Nil in | |
seq | |
(fun x -> | |
k x; (* callback *) | |
match !cur with | |
| Nil -> | |
let n = !chunk_size in | |
if n < 4096 then chunk_size := 2 * !chunk_size; | |
cur := Cons (Array.make n x, ref 1, ref Nil) | |
| Cons (a, n, next) -> | |
assert (!n < Array.length a); | |
a.(!n) <- x; | |
incr n; | |
if !n = Array.length a | |
then begin | |
!prev := !cur; | |
prev := next; | |
cur := Nil | |
end); | |
!prev := !cur; | |
!start | |
let of_seq seq = | |
of_seq_with seq (fun _ -> ()) | |
let rec iter f l = match l with | |
| Nil -> () | |
| Cons (a, n, tl) -> | |
for i = 0 to !n - 1 do f a.(i) done; | |
iter f !tl | |
let to_seq l k = iter k l | |
end | |
let persistent seq = | |
let l = MList.of_seq seq in | |
MList.to_seq l | |
let fold f init seq = | |
let r = ref init in | |
seq (fun elt -> r := f !r elt); | |
!r | |
exception Break | |
let fold_while f s seq = | |
let state = ref s in | |
let consume x = | |
let acc, cont = f (!state) x in | |
state := acc; | |
match cont with | |
| `Stop -> raise Break | |
| `Continue -> () | |
in | |
try seq consume; !state | |
with Break -> !state | |
let iter f seq = seq f | |
let concat s k = s (fun s' -> s' k) | |
let append s1 s2 k = s1 k; s2 k | |
let to_queue q seq = seq (fun x -> Queue.push x q) | |
let of_queue q k = Queue.iter k q | |
let to_list seq = | |
List.rev (fold (fun y x -> x :: y) [] seq) | |
let of_list l k = List.iter k l |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment