Back to... Zip-Ada

Source file : zip-compress-deflate.adb



--  Legal licensing note:

--  Copyright (c) 2009 .. 2024 Gautier de Montmollin
--  SWITZERLAND

--  Permission is hereby granted, free of charge, to any person obtaining a copy
--  of this software and associated documentation files (the "Software"), to deal
--  in the Software without restriction, including without limitation the rights
--  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--  copies of the Software, and to permit persons to whom the Software is
--  furnished to do so, subject to the following conditions:

--  The above copyright notice and this permission notice shall be included in
--  all copies or substantial portions of the Software.

--  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
--  THE SOFTWARE.

--  NB: this is the MIT License, as found on the site
--  http://www.opensource.org/licenses/mit-license.php

-----------------
--  The "Deflate" method combines a LZ77 compression
--  method with some Huffman encoding gymnastics.
--
--  Magic numbers in this procedure are adjusted through experimentation and marked with: *Tuned*
--
--  To do:
--    - Taillaule: try with slider and/or initial lz window not centered
--    - Taillaule: compare slider to random and fixed in addition to initial
--    - Taillaule: try L_sup distance
--    - Taillaule: restrict BL_Vector to short LZ distances (long distances perhaps too random)
--    - Taillaule: check LZ vector norms on literals only, too (consider distances & lengths as noise)
--    - Taillaule: use a corpus of files badly compressed by our Deflate comparatively
--        to other Deflates (e.g. 7Z seems better with databases)
--    - Add DeflOpt to slowest method, or approximate it by tweaking
--        distance and length statistics before computing their Huffman codes, or
--        reinvent it by computing the size of emitted codes and trying slight changes
--        to the codes' bit lengths.
--    - Improve LZ77 compression: see Zip.LZ77 to-do list; check with bypass_LZ77 below
--        and various programs based on LZ77 using the trace >= some and the LZ77 dump
--        in UnZip.Decompress.
--    - Make this procedure standalone & generic like LZMA.Encoding;
--        use it in the Zada project (Zlib replacement)
--
--  Change log:
--------------
--
--  16-Mar-2016: Taillaule algorithm: first version ready for release.
--  20-Feb-2016: (rev.305) Start of smarter techniques for "Dynamic" encoding: Taillaule algorithm
--   4-Feb-2016: Start of "Dynamic" encoding format (compression structure sent before block)
--
--  19-Feb-2011: All distance and length codes implemented.
--  18-Feb-2011: First version working with Deflate fixed and restricted distance & length codes.
--  17-Feb-2011: Created (single-block, "fixed" Huffman encoding).

with Huffman.Encoding.Length_Limited_Coding;
with LZ77;

with Ada.Text_IO,
     Ada.Unchecked_Deallocation;

procedure Zip.Compress.Deflate
  (input,
   output           : in out Zip_Streams.Root_Zipstream_Type'Class;
   input_size_known :        Boolean;
   input_size       :        Zip_64_Data_Size_Type;  --  ignored if unknown
   feedback         :        Feedback_Proc;
   method           :        Deflation_Method;
   CRC              : in out Interfaces.Unsigned_32;  --  only updated here
   crypto           : in out CRC_Crypto.Crypto_pack;
   output_size      :    out Zip_64_Data_Size_Type;
   compression_ok   :    out Boolean)  --  indicates compressed < uncompressed
is
  --  Options for testing.
  --  All should be on False for normal use of this procedure.

  deactivate_scanning : constant Boolean := False;  --  Impact analysis of the scanning method
  trace               : constant Boolean := False;  --  Log file with details
  trace_descriptors   : constant Boolean := False;  --  Additional logging of Huffman descriptors

  --  A log file is used when trace = True.
  log         : Ada.Text_IO.File_Type;
  log_name    : constant String := "Zip.Compress.Deflate.zcd";  --  A CSV with an unusual extension
  sep         : constant Character := ';';

  use Ada.Text_IO;
  use Interfaces;

  -------------------------------------
  -- Buffered I/O - byte granularity --
  -------------------------------------

  IO_buffers : IO_Buffers_Type;

  procedure Put_byte (B : Byte) is  --  Put a byte, at the byte granularity level
  pragma Inline (Put_byte);
  begin
    IO_buffers.OutBuf (IO_buffers.OutBufIdx) := B;
    IO_buffers.OutBufIdx := IO_buffers.OutBufIdx + 1;
    if IO_buffers.OutBufIdx > IO_buffers.OutBuf'Last then
      Write_Block (IO_buffers, input_size_known, input_size, output, output_size, crypto);
    end if;
  end Put_byte;

  procedure Flush_byte_buffer is
  begin
    if IO_buffers.OutBufIdx > 1 then
      Write_Block (IO_buffers, input_size_known, input_size, output, output_size, crypto);
    end if;
  end Flush_byte_buffer;

  ------------------------------------------------------
  --  Bit code buffer, for sending data at bit level  --
  ------------------------------------------------------

  --  Output buffer. Bits are inserted starting at the right (least
  --  significant bits). The width of bit_buffer must be at least 16 bits.
  subtype U32 is Unsigned_32;
  bit_buffer : U32 := 0;
  --  Number of valid bits in bit_buffer.  All bits above the last valid bit are always zero.
  valid_bits : Integer := 0;

  procedure Flush_bit_buffer is
  begin
    while valid_bits > 0 loop
      Put_byte (Byte (bit_buffer and 16#FF#));
      bit_buffer := Shift_Right (bit_buffer, 8);
      valid_bits := Integer'Max (0, valid_bits - 8);
    end loop;
    bit_buffer := 0;
  end Flush_bit_buffer;

  --  Bit codes are at most 15 bits for Huffman codes,
  --  or 13 for explicit codes (distance extra bits).
  subtype Code_Size_Type is Integer range 1 .. 15;

  --  Send a value on a given number of bits.
  procedure Put_Bits (code : U32; code_size : Code_Size_Type) with Inline is
  begin
    --  Put bits from code at the left of existing ones. They might be shifted away
    --  partially on the left side (or even entirely if valid_bits is already = 32).
    bit_buffer := bit_buffer or Shift_Left (code, valid_bits);
    valid_bits := valid_bits + code_size;
    if valid_bits > 32 then
      --  Flush 32 bits to output as 4 bytes
      Put_byte (Byte (bit_buffer and 16#FF#));
      Put_byte (Byte (Shift_Right (bit_buffer,  8) and 16#FF#));
      Put_byte (Byte (Shift_Right (bit_buffer, 16) and 16#FF#));
      Put_byte (Byte (Shift_Right (bit_buffer, 24) and 16#FF#));
      valid_bits := valid_bits - 32;
      --  Empty buffer and put on it the rest of the code
      bit_buffer := Shift_Right (code, code_size - valid_bits);
    end if;
  end Put_Bits;

  ------------------------------------------------------
  -- Deflate, post LZ encoding, with Huffman encoding --
  ------------------------------------------------------

  --  The Huffman code set (and therefore the Huffman tree) is completely determined by
  --  the bit length to be used for reaching leaf nodes, thanks to two special
  --  rules (explanation in RFC 1951, section 3.2.2).
  --
  --  So basically the process is the following:
  --
  --     (A) Gather statistics (just counts) for the alphabet.
  --     (B) Turn these counts into code lengths, by calling Length_limited_Huffman_code_lengths.
  --     (C) Build Huffman codes (the bits to be sent) with a call to Prepare_Huffman_codes.
  --
  --  In short:
  --
  --     data -> (A) -> stats -> (B) -> Huffman codes' bit lengths -> (C) -> Huffman codes

  type Bit_Length_Array is array (Natural range <>) of Natural;

  subtype Alphabet_lit_len is Natural range 0 .. 287;
  subtype Bit_length_array_lit_len is Bit_Length_Array (Alphabet_lit_len);
  subtype Alphabet_dis is Natural range 0 .. 31;
  subtype Bit_length_array_dis is Bit_Length_Array (Alphabet_dis);

  type Deflate_Huff_Descriptors is record
    --  Tree descriptor for Literal, EOB or Length encoding
    lit_len : Huffman.Encoding.Descriptor (0 .. 287);
    --  Tree descriptor for Distance encoding
    dis : Huffman.Encoding.Descriptor (0 .. 31);
  end record;
  --  NB: Appnote: "Literal codes 286-287 and distance codes 30-31 are never used
  --                  but participate in the Huffman construction."
  --  Setting upper bound to 285 for literals leads to invalid codes, sometimes.

  --  Copy bit length vectors into Deflate Huffman descriptors

  function Build_descriptors (
    bl_for_lit_len : Bit_length_array_lit_len;
    bl_for_dis     : Bit_length_array_dis
  )
  return Deflate_Huff_Descriptors
  is
    new_d : Deflate_Huff_Descriptors;
  begin
    for i in new_d.lit_len'Range loop
      new_d.lit_len (i) := (bit_length => bl_for_lit_len (i), code => Huffman.Encoding.invalid);
      if trace_descriptors and then trace and then Is_Open (log) then
        Put (log, Integer'Image (bl_for_lit_len (i)) & sep);
      end if;
    end loop;
    for i in new_d.dis'Range loop
      new_d.dis (i) := (bit_length => bl_for_dis (i), code => Huffman.Encoding.invalid);
      if trace_descriptors and then trace and then Is_Open (log) then
        Put (log, Integer'Image (bl_for_dis (i)) & sep);
      end if;
    end loop;
    if trace_descriptors and then trace and then Is_Open (log) then
      New_Line (log);
    end if;
    return new_d;
  end Build_descriptors;

  type Count_type is range 0 .. Zip_64_Data_Size_Type'Last / 2 - 1;
  type Stats_type is array (Natural range <>) of Count_type;

  --  The following is a translation of Zopfli's OptimizeHuffmanForRle (v. 11-May-2016).
  --  Possible gain: shorten the compression header containing the Huffman trees' bit lengths.
  --  Possible loss: since the stats do not correspond anymore exactly to the data
  --  to be compressed, the Huffman trees might be suboptimal.
  --
  --  Zopfli comment:
  --    Changes the population counts in a way that the consequent Huffman tree
  --    compression, especially its rle-part, will be more likely to compress this data
  --    more efficiently.
  --
  procedure Tweak_for_better_RLE (counts : in out Stats_type) is
    length : Integer := counts'Length;
    stride : Integer;
    symbol, sum, limit, new_count : Count_type;
    good_for_rle : array (counts'Range) of Boolean := (others => False);
  begin
    --  1) We don't want to touch the trailing zeros. We may break the
    --     rules of the format by adding more data in the distance codes.
    loop
      if length = 0 then
        return;
      end if;
      exit when counts (length - 1) /= 0;
      length := length - 1;
    end loop;
    --  Now counts(0..length - 1) does not have trailing zeros.
    --
    --  2) Let's mark all population counts that already can be encoded with an rle code.
    --
    --  Let's not spoil any of the existing good rle codes.
    --  Mark any seq of 0's that is longer than 5 as a good_for_rle.
    --  Mark any seq of non-0's that is longer than 7 as a good_for_rle.
    symbol := counts (0);
    stride := 0;
    for i in 0 .. length loop
      if i = length or else counts (i) /= symbol then
        if (symbol = 0 and then stride >= 5) or else (symbol /= 0 and then stride >= 7) then
          for k in 0 .. stride - 1 loop
            good_for_rle (i - k - 1) := True;
          end loop;
        end if;
        stride := 1;
        if i /= length then
          symbol := counts (i);
        end if;
      else
        stride := stride + 1;
      end if;
    end loop;
    --  3) Let's replace those population counts that lead to more rle codes.
    stride := 0;
    limit := counts (0);
    sum := 0;
    for i in 0 .. length loop
      if i = length or else good_for_rle (i)
          or else (i > 0 and then good_for_rle (i - 1))  --  Added from Brotli, item #1
          --  Heuristic for selecting the stride ranges to collapse.
          or else abs (counts (i) - limit) >= 4
      then
        if stride >= 4 or else (stride >= 3 and then sum = 0) then
          --  The stride must end, collapse what we have, if we have enough (4).
          --  GdM: new_count is the average of counts on the stride's interval, upper-rounded.
          new_count := Count_type'Max (1, (sum + Count_type (stride) / 2) / Count_type (stride));
          if sum = 0 then
            --  Don't make an all zeros stride to be upgraded to ones.
            new_count := 0;
          end if;
          for k in 0 .. stride - 1 loop
            --  We don't want to change value at counts(i),
            --  that is already belonging to the next stride. Thus - 1.
            counts (i - k - 1) := new_count;  --  GdM: Replace histogram value by averaged value.
          end loop;
        end if;
        stride := 0;
        sum := 0;
        if i < length - 3 then
          --  All interesting strides have a count of at least 4, at least when non-zeros.
          --  GdM: limit is the average of next 4 counts, upper-rounded.
          limit := (counts (i) + counts (i + 1) + counts (i + 2) + counts (i + 3) + 2) / 4;
        elsif i < length then
          limit := counts (i);
        else
          limit := 0;
        end if;
      end if;
      stride := stride + 1;
      if i /= length then
        sum := sum + counts (i);
      end if;
    end loop;
  end Tweak_for_better_RLE;

  subtype Stats_lit_len_type is Stats_type (Alphabet_lit_len);
  subtype Stats_dis_type is Stats_type (Alphabet_dis);

  --  Phase (B) : we turn statistics into Huffman bit lengths.
  function Build_descriptors (
    stats_lit_len  : Stats_lit_len_type;
    stats_dis      : Stats_dis_type
  )
  return Deflate_Huff_Descriptors
  is
    bl_for_lit_len : Bit_length_array_lit_len;
    bl_for_dis     : Bit_length_array_dis;
    procedure LLHCL_lit_len is new
      Huffman.Encoding.Length_Limited_Coding
        (Alphabet_lit_len, Count_type, Stats_lit_len_type, Bit_length_array_lit_len, 15);
    procedure LLHCL_dis is new
      Huffman.Encoding.Length_Limited_Coding
        (Alphabet_dis, Count_type, Stats_dis_type, Bit_length_array_dis, 15);
    stats_dis_copy : Stats_dis_type := stats_dis;
    --
    procedure Patch_statistics_for_buggy_decoders is
      --  Workaround for buggy Info-Zip decoder versions.
      --  See "PatchDistanceCodesForBuggyDecoders" in Zopfli's deflate.c
      --  NB: here, we patch the statistics and not the resulting bit lengths,
      --      to be sure we avoid invalid Huffman code sets in the end.
      --  The decoding bug concerns Zlib v.<= 1.2.1, UnZip v.<= 6.0, WinZip v.<=10.0.
      used : Natural := 0;
    begin
      for i in stats_dis_copy'Range loop
        if stats_dis_copy (i) /= 0 then
          used := used + 1;
        end if;
      end loop;
      case used is
        when 0 =>  --  No distance code used at all (data must be almost random).
          stats_dis_copy (0 .. 1) := (1, 1);
        when 1 =>
          if stats_dis_copy (0) = 0 then
            stats_dis_copy (0) := 1;  --  Now, code 0 and some other code have non-zero counts.
          else
            stats_dis_copy (1) := 1;  --  Now, codes 0 and 1 have non-zero counts.
          end if;
        when others =>
          null;  --  No workaround needed when 2 or more distance codes are defined.
      end case;
    end Patch_statistics_for_buggy_decoders;
  begin
    Patch_statistics_for_buggy_decoders;
    LLHCL_lit_len (stats_lit_len, bl_for_lit_len);  --  Call the magic algorithm for setting
    LLHCL_dis (stats_dis_copy, bl_for_dis);         --    up Huffman lengths of both trees
    return Build_descriptors (bl_for_lit_len, bl_for_dis);
  end Build_descriptors;

  --  Here is one original part in the Taillaule algorithm: use of basic
  --  topology (L1, L2 distances) to check similarities between Huffman code sets.

  --  Bit length vector. Convention: 16 is unused bit length (close to the bit length for the
  --  rarest symbols, 15, and far from the bit length for the most frequent symbols, 1).
  --  Deflate uses 0 for unused.
  subtype BL_code is Integer_M32 range 1 .. 16;
  type BL_vector is array (1 .. 288 + 32) of BL_code;

  function Convert (h : Deflate_Huff_Descriptors) return BL_vector is
    bv : BL_vector;
    j : Positive := 1;
  begin
    for i in h.lit_len'Range loop
      if h.lit_len (i).bit_length = 0 then
        bv (j) := 16;
      else
        bv (j) := Integer_M32 (h.lit_len (i).bit_length);
      end if;
      j := j + 1;
    end loop;
    for i in h.dis'Range loop
      if h.dis (i).bit_length = 0 then
        bv (j) := 16;
      else
        bv (j) := Integer_M32 (h.dis (i).bit_length);
      end if;
      j := j + 1;
    end loop;
    return bv;
  end Convert;

  --  L1 or Manhattan distance
  function L1_distance (b1, b2 : BL_vector) return Natural_M32 is
    s : Natural_M32 := 0;
  begin
    for i in b1'Range loop
      s := s + abs (b1 (i) - b2 (i));
    end loop;
    return s;
  end L1_distance;

  --  L1, tweaked
  --
  tweak : constant array (BL_code) of Positive_M32 :=
    --  For the origin of the tweak function, see "za_work.xls", sheet "Deflate".
    --  function f3 = 0.20 f1 [logarithmic] + 0.80 * identity
    --  NB: all values are multiplied by 100 for accuracy.
        (100, 255, 379, 490, 594, 694, 791, 885, 978, 1069, 1159, 1249, 1338, 1426, 1513, 1600);
    --  Neutral is:
    --  (100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600)

  --
  function L1_tweaked (b1, b2 : BL_vector) return Natural_M32 is
    s : Natural_M32 := 0;
  begin
    for i in b1'Range loop
      s := s + abs (tweak (b1 (i)) - tweak (b2 (i)));
    end loop;
    return s;
  end L1_tweaked;

  --  L2 or Euclidean distance
  function L2_distance_square (b1, b2 : BL_vector) return Natural_M32 is
    s : Natural_M32 := 0;
  begin
    for i in b1'Range loop
      s := s + (b1 (i) - b2 (i)) ** 2;
    end loop;
    return s;
  end L2_distance_square;

  --  L2, tweaked
  function L2_tweaked_square (b1, b2 : BL_vector) return Natural_M32 is
    s : Natural_M32 := 0;
  begin
    for i in b1'Range loop
      s := s + (tweak (b1 (i)) - tweak (b2 (i))) ** 2;
    end loop;
    return s;
  end L2_tweaked_square;

  type Distance_type is (L1, L1_tweaked, L2, L2_tweaked);

  function Similar
    (h1, h2    : Deflate_Huff_Descriptors;
     dist_kind : Distance_type;
     threshold : Natural;
     comment   : String)
  return Boolean
  is
    dist  : Natural_M32;
    thres : Natural_M32 := Natural_M32 (threshold);
  begin
    case dist_kind is
      when L1 =>
        dist := L1_distance (Convert (h1), Convert (h2));
      when L1_tweaked =>
        thres := thres * tweak (1);
        dist  := L1_tweaked (Convert (h1), Convert (h2));
      when L2 =>
        thres := thres * thres;
        dist  := L2_distance_square (Convert (h1), Convert (h2));
      when L2_tweaked =>
        thres := (thres * thres) * (tweak (1) * tweak (1));
        dist  := L2_tweaked_square (Convert (h1), Convert (h2));
    end case;
    if trace then
      Put_Line (log,
        "Checking similarity." & sep &
        Distance_type'Image (dist_kind) & sep &
        "Distance (ev. x100, **2):" & sep & Integer_M32'Image (dist) & sep & sep &
        "Threshold (ev. x100, **2):" & sep & Integer_M32'Image (thres) & sep & sep &
        comment
      );
    end if;
    return dist < thres;
  end Similar;

  --  Another original part in the Taillaule algorithm: the possibility of recycling
  --  Huffman codes. It is possible only if previous block was not stored and if
  --  the new block's used alphabets are included in the old block's used alphabets.
  function Recyclable (h_old, h_new : Deflate_Huff_Descriptors) return Boolean is
  begin
    for i in h_old.lit_len'Range loop
      if h_old.lit_len (i).bit_length = 0 and h_new.lit_len (i).bit_length > 0 then
        return False;  --  Code used in new, but not in old
      end if;
    end loop;
    for i in h_old.dis'Range loop
      if h_old.dis (i).bit_length = 0 and h_new.dis (i).bit_length > 0 then
        return False;  --  Code used in new, but not in old
      end if;
    end loop;
    return True;
  end Recyclable;

  --  Phase (C): the Prepare_Huffman_Codes procedure finds the Huffman code
  --  for each value, given the bit length imposed as input.

  function Prepare_Huffman_Codes (dhd : Deflate_Huff_Descriptors) return Deflate_Huff_Descriptors
  is
    dhd_var : Deflate_Huff_Descriptors := dhd;
  begin
    Huffman.Encoding.Prepare_Codes (dhd_var.lit_len, Code_Size_Type'Last, True);
    Huffman.Encoding.Prepare_Codes (dhd_var.dis, Code_Size_Type'Last, True);
    return dhd_var;
  end Prepare_Huffman_Codes;

  --  Emit a variable length Huffman code
  procedure Put_Huffman_Code (lc : Huffman.Encoding.Length_Code_Pair) is
  pragma Inline (Put_Huffman_Code);
  begin
    --  Huffman code of length 0 should never occur: when constructing
    --  the code lengths (LLHCL) any single occurrence in the statistics
    --  will trigger the build of a code length of 1 or more.
    Put_Bits
      (code      => U32 (lc.code),
       code_size => Code_Size_Type (lc.bit_length));  --  Range check for length 0 (if enabled).
  end Put_Huffman_Code;

  --  This is where the "dynamic" Huffman trees are sent before the block's data are sent.
  --
  --  The decoder needs to know in advance the pair of trees (1st tree for literals-eob-LZ
  --  lengths, 2nd tree for LZ distances) for decoding the compressed data.
  --  But this information takes some room. Fortunately Deflate allows for compressing it
  --  with a combination of Huffman and Run-Length Encoding (RLE) to make this header smaller.
  --  Concretely, the trees are described by the bit length of each symbol, so the header's
  --  content is a vector of length max 320, whose contents are in the 0 .. 18 range and typically
  --  look like:  ... 8, 8, 9, 7, 8, 10, 6, 8, 8, 8, 8, 8, 11, 8, 9, 8, ...
  --  Clearly this vector has redundancies and can be sent in a compressed form.
  --  In this example, the RLE will compress the string of 8's with a single code 8, then a code 17
  --  (repeat x times). Anyway, the very frequent 8's will be encoded with a small number of
  --  bits (less than the 5 plain bits, or maximum 7 Huffman-encoded bits
  --  needed for encoding integers in the 0 .. 18 range).
  --
  procedure Put_Compression_Structure
    (dhd           :        Deflate_Huff_Descriptors;
     cost_analysis :        Boolean;     --  If True: just simulate the whole, and count needed bits
     bits          : in out Count_type)  --  This is incremented when cost_analysis = True
  is
    subtype Alphabet is Integer range 0 .. 18;
    type Alpha_Array is new Bit_Length_Array (Alphabet);
    truc_freq, truc_bl : Alpha_Array;
    truc : Huffman.Encoding.Descriptor (Alphabet);
    --  Compression structure: cs_bl is the "big" array with all bit lengths
    --  for compressing data. cs_bl will be sent compressed, too.
    cs_bl : array (1 .. dhd.lit_len'Length + dhd.dis'Length) of Natural;
    last_cs_bl : Natural;
    max_used_lln_code : Alphabet_lit_len := 0;
    max_used_dis_code : Alphabet_dis     := 0;
    --
    procedure Concatenate_all_bit_lengths is
      idx : Natural := 0;
    begin
      for a in reverse Alphabet_lit_len loop
        if dhd.lit_len (a).bit_length > 0 then
          max_used_lln_code := a;
          exit;
        end if;
      end loop;
      for a in reverse Alphabet_dis loop
        if dhd.dis (a).bit_length > 0 then
          max_used_dis_code := a;
          exit;
        end if;
      end loop;
      --  Copy bit lengths for both trees into one array, cs_bl.
      for a in 0 .. max_used_lln_code loop
        idx := idx + 1;
        cs_bl (idx) := dhd.lit_len (a).bit_length;
      end loop;
      for a in 0 .. max_used_dis_code loop
        idx := idx + 1;
        cs_bl (idx) := dhd.dis (a).bit_length;
      end loop;
      last_cs_bl := idx;
    end Concatenate_all_bit_lengths;
    --
    extra_bits_needed : constant array (Alphabet) of Natural :=
       (16 => 2, 17 => 3, 18 => 7, others => 0);
    --
    type Emission_mode is (simulate, effective);
    --
    procedure Emit_data_compression_structures (emit_mode : Emission_mode) is
      procedure Emit_data_compression_atom (x : Alphabet; extra_code : U32 := 0) is
        --  x is a bit length (value in 0..15), or a RLE instruction
      begin
        case emit_mode is
          when simulate =>
            truc_freq (x) := truc_freq (x) + 1;  --  +1 for x's histogram bar
          when effective =>
            Put_Huffman_Code (truc (x));
            declare
              extra_bits : constant Natural := extra_bits_needed (x);
            begin
              if extra_bits > 0 then
                Put_Bits (extra_code, extra_bits);
              end if;
            end;
        end case;
      end Emit_data_compression_atom;
      idx : Natural := 0;
      rep : Positive;  --  Number of times current atom is repeated, >= 1
    begin
      --  Emit the bit lengths, with some RLE encoding (Appnote: 5.5.3; RFC 1951: 3.2.7)
      idx := 1;
      loop
        rep := 1;  --  Current atom, cs_bl(idx), is repeated 1x so far - obvious, isn't it ?
        for j in idx + 1 .. last_cs_bl loop
          exit when cs_bl (j) /= cs_bl (idx);
          rep := rep + 1;
        end loop;
        --  Now rep is the number of repetitions of current atom, including itself.
        if idx > 1 and then cs_bl (idx) = cs_bl (idx - 1) and then rep >= 3
            --  Better repeat a long sequence of zeros by using codes 17 or 18
            --  just after a 138-long previous sequence:
          and then not (cs_bl (idx) = 0 and then rep > 6)
        then
          rep := Integer'Min (rep, 6);
          Emit_data_compression_atom (16, U32 (rep - 3));    -- 16: "Repeat previous 3 to 6 times"
          idx := idx + rep;
        elsif cs_bl (idx) = 0 and then rep >= 3 then
          --  The 0 bit length may occur on long ranges of an alphabet (unused symbols)
          if rep <= 10 then
            Emit_data_compression_atom (17, U32 (rep - 3));  -- 17: "Repeat zero 3 to 10 times"
          else
            rep := Integer'Min (rep, 138);
            Emit_data_compression_atom (18, U32 (rep - 11)); -- 18: "Repeat zero 11 to 138 times"
          end if;
          idx := idx + rep;
        else
          Emit_data_compression_atom (cs_bl (idx));
          idx := idx + 1;
        end if;
        exit when idx > last_cs_bl;
      end loop;
    end Emit_data_compression_structures;
    --  Alphabet permutation for shortening in-use alphabet.
    --  After the RLE codes 16, 17, 18 and the bit length 0, which are assumed to be always used,
    --  the most usual bit lengths (around 8, which is the "neutral" bit length) appear first.
    --  For example, if the rare bit lengths 1 and 15 don't occur in any of the two Huffman trees
    --  for LZ data, then codes 1 and 15 have a length 0 in the local Alphabet and we can omit
    --  sending the last two bit lengths.
    alphabet_permutation : constant array (Alphabet) of Natural :=
       (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
    procedure LLHCL is new
      Huffman.Encoding.Length_Limited_Coding (Alphabet, Natural, Alpha_Array, Alpha_Array, 7);
    a_non_zero : Alphabet;
  begin
    Concatenate_all_bit_lengths;
    truc_freq := (others => 0);
    Emit_data_compression_structures (simulate);
    --  We have now statistics of all bit lengths occurrences of both Huffman
    --  trees used for compressing the data.
    --  We turn these counts into bit lengths for the local tree
    --  that helps us to store the compression structure in a more compact form.
    LLHCL (truc_freq, truc_bl);  --  Call the magic algorithm for setting up Huffman lengths
    --  At least lengths for codes 16, 17, 18, 0 will always be sent,
    --  even if all other bit lengths are 0 because codes 1 to 15 are unused.
    a_non_zero := 3;
    for a in Alphabet loop
      if a > a_non_zero and then truc_bl (alphabet_permutation (a)) > 0 then
        a_non_zero := a;
      end if;
    end loop;
    if cost_analysis then
      --  In this mode, no data output: we sum up the exact
      --  number of bits needed by the compression header.
      bits := bits + 14 + Count_type (1 + a_non_zero) * 3;
      for a in Alphabet loop
        bits := bits + Count_type (truc_freq (a) * (truc_bl (a) + extra_bits_needed (a)));
      end loop;
    else
      --  We output the compression header to the output stream.
      for a in Alphabet loop
        truc (a).bit_length := truc_bl (a);
      end loop;
      Huffman.Encoding.Prepare_Codes (truc, Code_Size_Type'Last, True);
      --  Output of the compression structure
      Put_Bits (U32 (max_used_lln_code - 256), 5);  --  max_used_lln_code is always >= 256 = EOB code
      Put_Bits (U32 (max_used_dis_code), 5);
      Put_Bits (U32 (a_non_zero - 3), 4);
      --  Save the local alphabet's Huffman lengths. It's the compression structure
      --  for compressing the data compression structure. Easy, isn't it ?
      for a in 0 .. a_non_zero loop
        Put_Bits (U32 (truc (alphabet_permutation (a)).bit_length), 3);
      end loop;
      --  Emit the Huffman lengths for encoding the data, in the local Huffman-encoded fashion.
      Emit_data_compression_structures (effective);
    end if;
  end Put_Compression_Structure;

  End_Of_Block : constant := 256;

  --  Default Huffman trees, for "fixed" blocks, as defined in appnote.txt or RFC 1951
  default_lit_len_bl : constant Bit_length_array_lit_len :=
      (0 .. 143   => 8,  --  For literals ("plain text" bytes)
     144 .. 255   => 9,  --  For more literals ("plain text" bytes)
     End_Of_Block => 7,  --  For EOB (256)
     257 .. 279   => 7,  --  For length codes
     280 .. 287   => 8   --  For more length codes
    );
  default_dis_bl : constant Bit_length_array_dis := (others => 5);

  Deflate_fixed_descriptors : constant Deflate_Huff_Descriptors :=
    Prepare_Huffman_Codes (Build_descriptors (default_lit_len_bl, default_dis_bl));

  --  Current tree descriptors
  curr_descr : Deflate_Huff_Descriptors := Deflate_fixed_descriptors;

  --  Write a normal, "clear-text" (post LZ, pre Huffman), 8-bit character (literal)
  procedure Put_literal_byte (b : Byte) is
  begin
    Put_Huffman_Code (curr_descr.lit_len (Integer (b)));
  end Put_literal_byte;

  --  Possible ranges for distance and length encoding in the Zip-Deflate format:
  subtype Length_range is Integer range 3 .. 258;
  subtype Distance_range is Integer range 1 .. 32768;

  --  This is where LZ distance-length tokens are written to the output stream.
  --  The Deflate format defines a sort of logarithmic compression, with codes
  --  for various distance and length ranges, plus extra bits for specifying the
  --  exact values. The codes are sent as Huffman codes with variable bit lengths
  --  (nothing to do with the lengths of LZ distance-length tokens).

  --                              Length Codes
  --                              ------------
  --       Extra             Extra              Extra              Extra
  --  Code Bits Length  Code Bits Lengths  Code Bits Lengths  Code Bits Length(s)
  --  ---- ---- ------  ---- ---- -------  ---- ---- -------  ---- ---- ---------
  --   257   0     3     265   1   11,12    273   3   35-42    281   5  131-162
  --   258   0     4     266   1   13,14    274   3   43-50    282   5  163-194
  --   259   0     5     267   1   15,16    275   3   51-58    283   5  195-226
  --   260   0     6     268   1   17,18    276   3   59-66    284   5  227-257
  --   261   0     7     269   2   19-22    277   4   67-82    285   0    258
  --   262   0     8     270   2   23-26    278   4   83-98
  --   263   0     9     271   2   27-30    279   4   99-114
  --   264   0    10     272   2   31-34    280   4  115-130
  --
  --  Example: the code # 266 means the LZ length (# of message bytes to be copied)
  --           shall be 13 or 14, depending on the extra bit value.

  deflate_code_for_lz_length : constant array (Length_range) of Natural :=
      (3          => 257,  -- Codes 257..264, with no extra bit
       4          => 258,
       5          => 259,
       6          => 260,
       7          => 261,
       8          => 262,
       9          => 263,
       10         => 264,
       11  .. 12  => 265,  -- Codes 265..268, with 1 extra bit
       13  .. 14  => 266,
       15  .. 16  => 267,
       17  .. 18  => 268,
       19  .. 22  => 269,  -- Codes 269..272, with 2 extra bits
       23  .. 26  => 270,
       27  .. 30  => 271,
       31  .. 34  => 272,
       35  .. 42  => 273,  -- Codes 273..276, with 3 extra bits
       43  .. 50  => 274,
       51  .. 58  => 275,
       59  .. 66  => 276,
       67  .. 82  => 277,  -- Codes 277..280, with 4 extra bits
       83  .. 98  => 278,
       99  .. 114 => 279,
       115 .. 130 => 280,
       131 .. 162 => 281,  -- Codes 281..284, with 5 extra bits
       163 .. 194 => 282,
       195 .. 226 => 283,
       227 .. 257 => 284,
       258        => 285   -- Code 285, with no extra bit
     );

  extra_bits_for_lz_length_offset : constant array (Length_range) of Integer :=
      (3 ..  10 | 258 =>  Huffman.Encoding.invalid,  --  just a placeholder, there is no extra bit there!
      11 ..  18       =>  11,
      19 ..  34       =>  19,
      35 ..  66       =>  35,
      67 .. 130       =>  67,
     131 .. 257       => 131);

  extra_bits_for_lz_length : constant array (Length_range) of Natural :=
      (3 ..  10 | 258 => 0,
      11 ..  18       => 1,
      19 ..  34       => 2,
      35 ..  66       => 3,
      67 .. 130       => 4,
     131 .. 257       => 5);

  procedure Put_DL_code (distance : Distance_range; length : Length_range) is
    extra_bits : Natural;
  begin
    Put_Huffman_Code (curr_descr.lit_len (deflate_code_for_lz_length (length)));
    --  Extra bits are needed to differentiate lengths sharing the same code.
    extra_bits := extra_bits_for_lz_length (length);
    if extra_bits > 0 then
      --  We keep only the last extra_bits bits of the length (minus given offset).
      --  Example: if extra_bits = 1, only the parity is sent (0 or 1);
      --  the rest has been already sent with Put_Huffman_code above.
      --  Equivalent: x:= x mod (2 ** extra_bits);
      Put_Bits (
        U32 (length - extra_bits_for_lz_length_offset (length))
          and
        (Shift_Left (U32'(1), extra_bits) - 1),
        extra_bits);
    end if;
    --                             Distance Codes
    --                             --------------
    --       Extra           Extra             Extra               Extra
    --  Code Bits Dist  Code Bits  Dist   Code Bits Distance  Code Bits Distance
    --  ---- ---- ----  ---- ---- ------  ---- ---- --------  ---- ---- --------
    --    0   0    1      8   3   17-24    16    7  257-384    24   11  4097-6144
    --    1   0    2      9   3   25-32    17    7  385-512    25   11  6145-8192
    --    2   0    3     10   4   33-48    18    8  513-768    26   12  8193-12288
    --    3   0    4     11   4   49-64    19    8  769-1024   27   12 12289-16384
    --    4   1   5,6    12   5   65-96    20    9 1025-1536   28   13 16385-24576
    --    5   1   7,8    13   5   97-128   21    9 1537-2048   29   13 24577-32768
    --    6   2   9-12   14   6  129-192   22   10 2049-3072
    --    7   2  13-16   15   6  193-256   23   10 3073-4096
    --
    --
    --  Example: the code # 10 means the LZ distance (# positions back in the circular
    --           message buffer for starting the copy) shall be 33, plus the value given
    --           by the 4 extra bits (between 0 and 15).
    --
    case distance is
      when 1 .. 4 =>          --  Codes 0..3, with no extra bit
        Put_Huffman_Code (curr_descr.dis (distance - 1));
      when 5 .. 8 =>          --  Codes 4..5, with 1 extra bit
        Put_Huffman_Code (curr_descr.dis (4 + (distance - 5) / 2));
        Put_Bits (U32 ((distance - 5) mod 2), 1);
      when 9 .. 16 =>         --  Codes 6..7, with 2 extra bits
        Put_Huffman_Code (curr_descr.dis (6 + (distance - 9) / 4));
        Put_Bits (U32 ((distance - 9) mod 4), 2);
      when 17 .. 32 =>        --  Codes 8..9, with 3 extra bits
        Put_Huffman_Code (curr_descr.dis (8 + (distance - 17) / 8));
        Put_Bits (U32 ((distance - 17) mod 8), 3);
      when 33 .. 64 =>        --  Codes 10..11, with 4 extra bits
        Put_Huffman_Code (curr_descr.dis (10 + (distance - 33) / 16));
        Put_Bits (U32 ((distance - 33) mod 16), 4);
      when 65 .. 128 =>       --  Codes 12..13, with 5 extra bits
        Put_Huffman_Code (curr_descr.dis (12 + (distance - 65) / 32));
        Put_Bits (U32 ((distance - 65) mod 32), 5);
      when 129 .. 256 =>      --  Codes 14..15, with 6 extra bits
        Put_Huffman_Code (curr_descr.dis (14 + (distance - 129) / 64));
        Put_Bits (U32 ((distance - 129) mod 64), 6);
      when 257 .. 512 =>      --  Codes 16..17, with 7 extra bits
        Put_Huffman_Code (curr_descr.dis (16 + (distance - 257) / 128));
        Put_Bits (U32 ((distance - 257) mod 128), 7);
      when 513 .. 1024 =>     --  Codes 18..19, with 8 extra bits
        Put_Huffman_Code (curr_descr.dis (18 + (distance - 513) / 256));
        Put_Bits (U32 ((distance - 513) mod 256), 8);
      when 1025 .. 2048 =>    --  Codes 20..21, with 9 extra bits
        Put_Huffman_Code (curr_descr.dis (20 + (distance - 1025) / 512));
        Put_Bits (U32 ((distance - 1025) mod 512), 9);
      when 2049 .. 4096 =>    --  Codes 22..23, with 10 extra bits
        Put_Huffman_Code (curr_descr.dis (22 + (distance - 2049) / 1024));
        Put_Bits (U32 ((distance - 2049) mod 1024), 10);
      when 4097 .. 8192 =>    --  Codes 24..25, with 11 extra bits
        Put_Huffman_Code (curr_descr.dis (24 + (distance - 4097) / 2048));
        Put_Bits (U32 ((distance - 4097) mod 2048), 11);
      when 8193 .. 16384 =>   --  Codes 26..27, with 12 extra bits
        Put_Huffman_Code (curr_descr.dis (26 + (distance - 8193) / 4096));
        Put_Bits (U32 ((distance - 8193) mod 4096), 12);
      when 16385 .. 32768 =>  --  Codes 28..29, with 13 extra bits
        Put_Huffman_Code (curr_descr.dis (28 + (distance - 16385) / 8192));
        Put_Bits (U32 ((distance - 16385) mod 8192), 13);
    end case;
  end Put_DL_code;

  function Deflate_code_for_LZ_distance (distance : Distance_range) return Natural is
  begin
    case distance is
      when 1 .. 4 => -- Codes 0..3, with no extra bit
        return distance - 1;
      when 5 .. 8 => -- Codes 4..5, with 1 extra bit
        return  4 + (distance - 5) / 2;
      when 9 .. 16 => -- Codes 6..7, with 2 extra bits
        return  6 + (distance - 9) / 4;
      when 17 .. 32 => -- Codes 8..9, with 3 extra bits
        return  8 + (distance - 17) / 8;
      when 33 .. 64 => -- Codes 10..11, with 4 extra bits
        return  10 + (distance - 33) / 16;
      when 65 .. 128 => -- Codes 12..13, with 5 extra bits
        return  12 + (distance - 65) / 32;
      when 129 .. 256 => -- Codes 14..15, with 6 extra bits
        return  14 + (distance - 129) / 64;
      when 257 .. 512 => -- Codes 16..17, with 7 extra bits
        return  16 + (distance - 257) / 128;
      when 513 .. 1024 => -- Codes 18..19, with 8 extra bits
        return  18 + (distance - 513) / 256;
      when 1025 .. 2048 => -- Codes 20..21, with 9 extra bits
        return  20 + (distance - 1025) / 512;
      when 2049 .. 4096 => -- Codes 22..23, with 10 extra bits
        return  22 + (distance - 2049) / 1024;
      when 4097 .. 8192 => -- Codes 24..25, with 11 extra bits
        return  24 + (distance - 4097) / 2048;
      when 8193 .. 16384 => -- Codes 26..27, with 12 extra bits
        return  26 + (distance - 8193) / 4096;
      when 16385 .. 32768 => -- Codes 28..29, with 13 extra bits
        return  28 + (distance - 16385) / 8192;
    end case;
  end Deflate_code_for_LZ_distance;

  -----------------
  --  LZ Buffer  --
  -----------------

  --  We buffer the LZ codes (plain, or distance/length) in order to
  --  analyse them and try to do smart things.

  max_expand : constant := 14;  --  *Tuned* Sometimes it is better to store data and expand short strings
  code_for_max_expand : constant := 266;
  subtype Expanded_data is Byte_Buffer (1 .. max_expand);

  type LZ_atom_kind is (plain_byte, distance_length);
  type LZ_atom is record
    kind          : LZ_atom_kind;
    plain         : Byte;
    lz_distance   : Natural;
    lz_length     : Natural;
    lz_expanded   : Expanded_data;
  end record;

  --  *Tuned*. Min: 2**14, = 16384 (min half buffer 8192)
  --  Optimal so far: 2**17
  LZ_buffer_size : constant := 2**17;
  type LZ_buffer_index_type is mod LZ_buffer_size;
  type LZ_buffer_type is array (LZ_buffer_index_type range <>) of LZ_atom;

  empty_lit_len_stat : constant Stats_lit_len_type := (End_Of_Block => 1, others => 0);
  --  End_Of_Block will have to happen once, but never appears in the LZ statistics...
  empty_dis_stat : constant Stats_dis_type := (others => 0);

  --
  --  Compute statistics for both Literal-length, and Distance alphabets, from a LZ buffer
  --
  procedure Get_statistics (
    lzb           :  in LZ_buffer_type;
    stats_lit_len : out Stats_lit_len_type;
    stats_dis     : out Stats_dis_type
  )
  is
    lit_len : Alphabet_lit_len;
    dis     : Alphabet_dis;
  begin
    stats_lit_len := empty_lit_len_stat;
    stats_dis     := empty_dis_stat;
    for i in lzb'Range loop
      case lzb (i).kind is
        when plain_byte =>
          lit_len := Alphabet_lit_len (lzb (i).plain);
          stats_lit_len (lit_len) := stats_lit_len (lit_len) + 1;     --  +1 for this literal
        when distance_length =>
          lit_len := deflate_code_for_lz_length (lzb (i).lz_length);
          stats_lit_len (lit_len) := stats_lit_len (lit_len) + 1;     --  +1 for this length code
          dis := Deflate_code_for_LZ_distance (lzb (i).lz_distance);
          stats_dis (dis) := stats_dis (dis) + 1;                     --  +1 for this distance code
      end case;
    end loop;
  end Get_statistics;

  --
  --  Send a LZ buffer using currently defined Huffman codes
  --
  procedure Put_LZ_buffer (lzb : LZ_buffer_type) is
  begin
    for i in lzb'Range loop
      case lzb (i).kind is
        when plain_byte =>
          Put_literal_byte (lzb (i).plain);
        when distance_length =>
          Put_DL_code (lzb (i).lz_distance, lzb (i).lz_length);
      end case;
    end loop;
  end Put_LZ_buffer;

  block_to_finish : Boolean := False;
  last_block_marked : Boolean := False;
  type Block_type is (stored, fixed, dynamic, reserved);  --  Appnote, 5.5.2
  --  If last_block_type = dynamic, we may recycle previous block's Huffman codes
  last_block_type : Block_type := reserved;

  procedure Mark_new_block (last_block_for_stream : Boolean) is
  begin
    if block_to_finish and last_block_type in fixed .. dynamic then
      Put_Huffman_Code (curr_descr.lit_len (End_Of_Block));  --  Finish previous block
    end if;
    block_to_finish := True;
    Put_Bits (code => Boolean'Pos (last_block_for_stream), code_size => 1);
    last_block_marked := last_block_for_stream;
  end Mark_new_block;

  --  Send a LZ buffer completely decoded as literals (LZ compression is discarded)
  procedure Expand_LZ_buffer (lzb : LZ_buffer_type; last_block : Boolean) is
    b1, b2 : Byte;
    to_be_sent : Natural_M32 := 0;
    --  to_be_sent is not always equal to lzb'Length: sometimes you have a DL code
    mid : LZ_buffer_index_type;
  begin
    for i in lzb'Range loop
      case lzb (i).kind is
        when plain_byte =>
          to_be_sent := to_be_sent + 1;
        when distance_length =>
          to_be_sent := to_be_sent + Natural_M32 (lzb (i).lz_length);
      end case;
    end loop;
    if to_be_sent > 16#FFFF# then  --  Ow, cannot send all that in one chunk.
      --  Instead of a tedious block splitting, just divide and conquer:
      mid := LZ_buffer_index_type ((Natural_M32 (lzb'First) + Natural_M32 (lzb'Last)) / 2);
      if trace then
        Put_Line (log,
          "Expand_LZ_buffer: splitting large stored block: " &
          LZ_buffer_index_type'Image (lzb'First) &
          LZ_buffer_index_type'Image (mid) &
          LZ_buffer_index_type'Image (lzb'Last)
        );
      end if;
      Expand_LZ_buffer (lzb (lzb'First .. mid), last_block => False);
      Expand_LZ_buffer (lzb (mid + 1   .. lzb'Last), last_block => last_block);
      return;
    end if;
    if trace then
      Put_Line (log, "Expand_LZ_buffer: sending" & Natural_M32'Image (to_be_sent) & " 'plain' bytes");
    end if;
    b1 := Byte (to_be_sent mod 256);
    b2 := Byte (to_be_sent  /  256);
    Mark_new_block (last_block_for_stream => last_block);
    last_block_type := stored;
    Put_Bits (code => 0, code_size => 2);  --  Signals a "stored" block
    Flush_bit_buffer;  --  Go to byte boundary
    Put_byte (b1);
    Put_byte (b2);
    Put_byte (not b1);
    Put_byte (not b2);
    for i in lzb'Range loop
      case lzb (i).kind is
        when plain_byte =>
          Put_byte (lzb (i).plain);
        when distance_length =>
          for j in 1 .. lzb (i).lz_length loop
            Put_byte (lzb (i).lz_expanded (j));
          end loop;
      end case;
    end loop;
  end Expand_LZ_buffer;

  --  Extra bits that need to be sent after various Deflate codes

  extra_bits_for_lz_length_code : constant array (257 .. 285) of Natural :=
     (257 .. 264 => 0,
      265 .. 268 => 1,
      269 .. 272 => 2,
      273 .. 276 => 3,
      277 .. 280 => 4,
      281 .. 284 => 5,
      285        => 0
    );

  extra_bits_for_lz_distance_code : constant array (0 .. 29) of Natural :=
     (0 ..  3 =>  0,
      4 ..  5 =>  1,
      6 ..  7 =>  2,
      8 ..  9 =>  3,
     10 .. 11 =>  4,
     12 .. 13 =>  5,
     14 .. 15 =>  6,
     16 .. 17 =>  7,
     18 .. 19 =>  8,
     20 .. 21 =>  9,
     22 .. 23 => 10,
     24 .. 25 => 11,
     26 .. 27 => 12,
     28 .. 29 => 13
    );

  subtype Long_length_codes is
    Alphabet_lit_len range code_for_max_expand + 1 .. Alphabet_lit_len'Last;
  zero_bl_long_lengths : constant Stats_type (Long_length_codes) := (others => 0);

  --  Send_as_block.
  --
  --  lzb (can be a slice of the principal buffer) will be sent as:
  --        * a new "dynamic" block, preceded by a compression structure header
  --    or  * the continuation of previous "dynamic" block
  --    or  * a new "fixed" block, if lz data's Huffman descriptor is close enough to "fixed"
  --    or  * a new "stored" block, if lz data are too random

  procedure Send_as_block (lzb : LZ_buffer_type; last_block : Boolean) is
    new_descr, new_descr_2 : Deflate_Huff_Descriptors;
    --
    procedure Send_fixed_block is
    begin
      if last_block_type = fixed then
        --  Cool, we don't need to mark a block boundary: the Huffman codes are already
        --  the expected ones. We can just continue sending the LZ atoms.
        null;
      else
        Mark_new_block (last_block_for_stream => last_block);
        curr_descr := Deflate_fixed_descriptors;
        Put_Bits (code => 1, code_size => 2);  --  Signals a "fixed" block
        last_block_type := fixed;
      end if;
      Put_LZ_buffer (lzb);
    end Send_fixed_block;
    --
    stats_lit_len, stats_lit_len_2 : Stats_lit_len_type;
    stats_dis, stats_dis_2 : Stats_dis_type;
    --
    procedure Send_dynamic_block (dyn : Deflate_Huff_Descriptors) is
      dummy : Count_type := 0;
    begin
      Mark_new_block (last_block_for_stream => last_block);
      curr_descr := Prepare_Huffman_Codes (dyn);
      Put_Bits (code => 2, code_size => 2);  --  Signals a "dynamic" block
      Put_Compression_Structure (curr_descr, cost_analysis => False, bits => dummy);
      Put_LZ_buffer (lzb);
      last_block_type := dynamic;
    end Send_dynamic_block;
    --  The following variables will contain the *exact* number of bits taken
    --  by the block to be sent, using different Huffman encodings, or stored.
    stored_format_bits,                      --  Block is stored (no compression)
    fixed_format_bits,                       --  Fixed (preset) Huffman codes
    dynamic_format_bits,                     --  Dynamic Huffman codes using block's statistics
    dynamic_format_bits_2,                   --  Dynamic Huffman codes after Tweak_for_better_RLE
    recycled_format_bits : Count_type := 0;  --  Continue previous block, use current Huffman codes
    --
    stored_format_possible : Boolean;  --  Can we store (needs expansion of DL codes) ?
    recycling_possible     : Boolean;  --  Can we recycle current Huffman codes ?
    --
    procedure Compute_sizes_of_variants is
      c     : Count_type;
      extra : Natural;
    begin
      --  We count bits taken by literals, for each block format variant.
      for i in 0 .. 255 loop
        c := stats_lit_len (i);  --  This literal appears c times in the LZ buffer
        stored_format_bits    := stored_format_bits    + 8                                               * c;
        fixed_format_bits     := fixed_format_bits     + Count_type (default_lit_len_bl (i))             * c;
        dynamic_format_bits   := dynamic_format_bits   + Count_type (new_descr.lit_len (i).bit_length)   * c;
        dynamic_format_bits_2 := dynamic_format_bits_2 + Count_type (new_descr_2.lit_len (i).bit_length) * c;
        recycled_format_bits  := recycled_format_bits  + Count_type (curr_descr.lit_len (i).bit_length)  * c;
      end loop;
      --  We count bits taken by DL codes.
      if stored_format_possible then
        for i in lzb'Range loop
          case lzb (i).kind is
            when plain_byte =>
              null;  --  Already counted
            when distance_length =>
               --  In the stored format, DL codes are expanded
              stored_format_bits := stored_format_bits + 8 * Count_type (lzb (i).lz_length);
          end case;
        end loop;
      end if;
      --  For compressed formats, count Huffman bits and extra bits.
      --  Lengths codes:
      for i in 257 .. 285 loop
        c := stats_lit_len (i);  --  This length code appears c times in the LZ buffer
        extra := extra_bits_for_lz_length_code (i);
        fixed_format_bits     := fixed_format_bits     + Count_type (default_lit_len_bl (i) + extra)             * c;
        dynamic_format_bits   := dynamic_format_bits   + Count_type (new_descr.lit_len (i).bit_length + extra)   * c;
        dynamic_format_bits_2 := dynamic_format_bits_2 + Count_type (new_descr_2.lit_len (i).bit_length + extra) * c;
        recycled_format_bits  := recycled_format_bits  + Count_type (curr_descr.lit_len (i).bit_length + extra)  * c;
      end loop;
      --  Distance codes:
      for i in 0 .. 29 loop
        c := stats_dis (i);  --  This distance code appears c times in the LZ buffer
        extra := extra_bits_for_lz_distance_code (i);
        fixed_format_bits     := fixed_format_bits     + Count_type (default_dis_bl (i) + extra)             * c;
        dynamic_format_bits   := dynamic_format_bits   + Count_type (new_descr.dis (i).bit_length + extra)   * c;
        dynamic_format_bits_2 := dynamic_format_bits_2 + Count_type (new_descr_2.dis (i).bit_length + extra) * c;
        recycled_format_bits  := recycled_format_bits  + Count_type (curr_descr.dis (i).bit_length + extra)  * c;
      end loop;
      --  Supplemental bits to be counted
      --
      stored_format_bits := stored_format_bits +
        (1 + (stored_format_bits / 8) / 65_535)  --  Number of stored blocks needed
        * 5  -- 5 bytes per header
        * 8; -- ... converted into bits
      --
      c := 1;  --  Is-last-block flag
      if block_to_finish and last_block_type in fixed .. dynamic then
        c := c + Count_type (curr_descr.lit_len (End_Of_Block).bit_length);
      end if;
      stored_format_bits    := stored_format_bits + c;
      fixed_format_bits     := fixed_format_bits + c + 2;
      dynamic_format_bits   := dynamic_format_bits + c + 2;
      dynamic_format_bits_2 := dynamic_format_bits_2 + c + 2;
      --  For both dynamic formats, we also counts the bits taken by the compression header!
      Put_Compression_Structure (new_descr,   cost_analysis => True, bits => dynamic_format_bits);
      Put_Compression_Structure (new_descr_2, cost_analysis => True, bits => dynamic_format_bits_2);
    end Compute_sizes_of_variants;
    --
    optimal_format_bits : Count_type;
  begin
    Get_statistics (lzb, stats_lit_len, stats_dis);
    new_descr := Build_descriptors (stats_lit_len, stats_dis);
    stats_lit_len_2 := stats_lit_len;
    stats_dis_2 := stats_dis;
    Tweak_for_better_RLE (stats_lit_len_2);
    Tweak_for_better_RLE (stats_dis_2);
    new_descr_2 := Build_descriptors (stats_lit_len_2, stats_dis_2);
    --  For "stored" block format, prevent expansion of DL codes with length > max_expand.
    --  We check stats are all 0 for long length codes:
    stored_format_possible := stats_lit_len (Long_length_codes) = zero_bl_long_lengths;
    recycling_possible :=
      last_block_type = fixed  --  The "fixed" alphabets use all symbols, then always recyclable.
        or else
      (last_block_type = dynamic and then Recyclable (curr_descr, new_descr));
    Compute_sizes_of_variants;
    if not stored_format_possible then
      stored_format_bits := Count_type'Last;
    end if;
    if not recycling_possible then
      recycled_format_bits := Count_type'Last;
    end if;
    optimal_format_bits := Count_type'Min (
      Count_type'Min (stored_format_bits, fixed_format_bits),
      Count_type'Min (
        Count_type'Min (dynamic_format_bits, dynamic_format_bits_2),
        recycled_format_bits)
    );
    --
    --  Selection of the block format with smallest size.
    --
    if fixed_format_bits = optimal_format_bits then
      if trace then
        Put_Line (log, "### New ""fixed"" block");
      end if;
      Send_fixed_block;
    elsif dynamic_format_bits = optimal_format_bits then
      if trace then
        Put_Line (log, "### New ""dynamic"" block with compression structure header");
      end if;
      Send_dynamic_block (new_descr);
    elsif dynamic_format_bits_2 = optimal_format_bits then
      if trace then
        Put_Line (log, "### New ""dynamic"" block, RLE-tweaked, with compression structure header");
      end if;
      Send_dynamic_block (new_descr_2);
    elsif recycled_format_bits = optimal_format_bits then
      if trace then
        Put_Line (log, "### Recycle: continue using existing Huffman compression structures");
      end if;
      Put_LZ_buffer (lzb);
    else  --  We have stored_format_bits = optimal_format_bits
      if trace then
        Put_Line (log, "### Too random - use ""stored"" block");
      end if;
      Expand_LZ_buffer (lzb, last_block);
    end if;
  end Send_as_block;

  subtype Full_range_LZ_buffer_type is LZ_buffer_type (LZ_buffer_index_type);
  type p_Full_range_LZ_buffer_type is access Full_range_LZ_buffer_type;
  procedure Dispose is
    new Ada.Unchecked_Deallocation (Full_range_LZ_buffer_type, p_Full_range_LZ_buffer_type);

  --  This is the main, big, fat, circular buffer containing LZ codes,
  --  each LZ code being a literal or a DL code.
  --  Heap allocation is needed only because default stack is too small on some targets.
  lz_buffer : p_Full_range_LZ_buffer_type := null;
  lz_buffer_index : LZ_buffer_index_type := 0;
  past_lz_data : Boolean := False;
  --  When True: some LZ_buffer_size data before lz_buffer_index (modulo!) are real, past data

  ---------------------------------------------------------------------------------
  --  Scanning and sampling: the really sexy part of the Taillaule algorithm...  --
  ---------------------------------------------------------------------------------

  --  We examine similarities in the LZ data flow at different step sizes.
  --  If the optimal Huffman encoding for this portion is very different, we choose to
  --  cut current block and start a new one. The shorter the step, the higher the threshold
  --  for starting a dynamic block, since the compression header is taking some room each time.

  --  *Tuned* (a bit...)
  min_step : constant := 750;

  type Step_threshold_metric is record
    slider_step       : LZ_buffer_index_type;  --  Should be a multiple of min_step.
    cutting_threshold : Positive;
    metric            : Distance_type;
  end record;

  --  *Tuned* thresholds
  --  NB: the enwik8, then silesia, then others tests are tough for lowering any!
  step_choice : constant array (Positive range <>) of Step_threshold_metric :=
     ((8 * min_step,  420, L1_tweaked),  --  Deflate_1, Deflate_2, Deflate_3 (enwik8)
      (4 * min_step,  430, L1_tweaked),  --             Deflate_2, Deflate_3 (silesia)
          (min_step, 2050, L1_tweaked)   --                        Deflate_3 (DB test)
    );

  max_choice : constant array (Taillaule_Deflation_Method) of Positive :=
    (Deflate_1 => 1, Deflate_2 => 2, others => step_choice'Last);

  slider_size : constant := 4096;
  half_slider_size : constant := slider_size / 2;
  slider_max : constant := slider_size - 1;

  --  Phases (A) and (B) are done in a single function: we get Huffman
  --  descriptors that should be good for encoding a given sequence of LZ atoms.
  function Build_descriptors (lzb : LZ_buffer_type) return Deflate_Huff_Descriptors is
    stats_lit_len : Stats_lit_len_type;
    stats_dis     : Stats_dis_type;
  begin
    Get_statistics (lzb, stats_lit_len, stats_dis);
    return Build_descriptors (stats_lit_len, stats_dis);
  end Build_descriptors;

  procedure Scan_and_send_from_main_buffer (from, to : LZ_buffer_index_type; last_flush : Boolean) is
    --  The following descriptors are *not* used for compressing, but for detecting similarities.
    initial_hd, sliding_hd : Deflate_Huff_Descriptors;
    start, slide_mid, send_from : LZ_buffer_index_type;
    sliding_hd_computed : Boolean;
  begin
    if to - from < slider_max then
      Send_as_block (lz_buffer (from .. to), last_flush);
      return;
    end if;
    --  For further comments: n := LZ_buffer_size
    if past_lz_data then  --  We have n / 2 previous data before 'from'.
      start := from - LZ_buffer_index_type (half_slider_size);
    else
      start := from;  --  Cannot have past data
    end if;
    if start > from then  --  Looped over, (mod n). Slider data are in two chunks in main buffer
      --  put_line(from'img & to'img & start'img);
      declare
        copy_from : LZ_buffer_index_type := start;
        copy : LZ_buffer_type (0 .. slider_max);
      begin
        for i in copy'Range loop
          copy (i) := lz_buffer (copy_from);
          copy_from := copy_from + 1;  --  Loops over (mod n)
        end loop;
        initial_hd := Build_descriptors (copy);
      end;
      --  Concatenation instead of above loop bombs with a Range Check error:
      --  lz_buffer(start .. lz_buffer'Last) &
      --  lz_buffer(0 .. start + LZ_buffer_index_type(slider_max))
    else
      initial_hd := Build_descriptors (lz_buffer (start .. start + slider_max));
    end if;
    send_from := from;
    slide_mid := from + min_step;
    Scan_LZ_data :
    while Integer_M32 (slide_mid) + half_slider_size < Integer_M32 (to) loop
      exit Scan_LZ_data when deactivate_scanning;
      sliding_hd_computed := False;
      Browse_step_level :
      for level in step_choice'Range loop
        exit Browse_step_level when level > max_choice (method);
        if (slide_mid - from) mod step_choice (level).slider_step = 0 then
          if not sliding_hd_computed then
            sliding_hd := Build_descriptors (lz_buffer (slide_mid - half_slider_size .. slide_mid + half_slider_size));
            sliding_hd_computed := True;
          end if;
          if not Similar (
            initial_hd,
            sliding_hd,
            step_choice (level).metric,
            step_choice (level).cutting_threshold,
            "Compare sliding to initial (step size=" &
            LZ_buffer_index_type'Image (step_choice (level).slider_step) & ')'
          )
          then
            if trace then
              Put_Line (log,
                "### Cutting @ " & LZ_buffer_index_type'Image (slide_mid) &
                "  ('from' is" & LZ_buffer_index_type'Image (from) &
                ", 'to' is" & LZ_buffer_index_type'Image (to) & ')'
              );
            end if;
            Send_as_block (lz_buffer (send_from .. slide_mid - 1), last_block => False);
            send_from := slide_mid;
            initial_hd := sliding_hd;  --  Reset reference descriptor for further comparisons
            exit Browse_step_level;  --  Cutting once at a given place is enough :-)
          end if;
        end if;
      end loop Browse_step_level;
      --  Exit before an eventual increment of slide_mid that would loop over (mod n).
      exit Scan_LZ_data when Integer_M32 (slide_mid) + min_step + half_slider_size >= Integer_M32 (to);
      slide_mid := slide_mid + min_step;
    end loop Scan_LZ_data;
    --
    --  Send last block for slice from .. to.
    --
    if send_from <= to then
      Send_as_block (lz_buffer (send_from .. to), last_block => last_flush);
    end if;
  end Scan_and_send_from_main_buffer;

  procedure Flush_half_buffer (last_flush : Boolean) is
    last_idx : constant LZ_buffer_index_type := lz_buffer_index - 1;
    n_div_2 : constant := LZ_buffer_size / 2;
  begin
    if last_idx < n_div_2 then
      Scan_and_send_from_main_buffer (0, last_idx, last_flush);        -- 1st half
    else
      Scan_and_send_from_main_buffer (n_div_2, last_idx, last_flush);  -- 2nd half
    end if;
    --  From this point, all further calls to Flush_half_buffer will
    --  have n_div_2 elements of past data.
    past_lz_data := True;
  end Flush_half_buffer;

  procedure Push (a : LZ_atom) is
  pragma Inline (Push);
  begin
    lz_buffer (lz_buffer_index) := a;
    lz_buffer_index := lz_buffer_index + 1;  --  becomes 0 when reaching LZ_buffer_size (modular)
    if lz_buffer_index * 2 = 0 then
      Flush_half_buffer (last_flush => False);
    end if;
  end Push;

  procedure Put_or_delay_literal_byte (b : Byte) is
  pragma Inline (Put_or_delay_literal_byte);
  begin
    case method is
      when Deflate_Fixed =>
        Put_literal_byte (b);  --  Buffering is not needed in this mode
      when Taillaule_Deflation_Method =>
        Push ((plain_byte, b, 0, 0, (b, others => 0)));
    end case;
  end Put_or_delay_literal_byte;

  procedure Put_or_delay_DL_code (distance, length : Integer; expand : Expanded_data) is
  pragma Inline (Put_or_delay_DL_code);
  begin
    case method is
      when Deflate_Fixed =>
        Put_DL_code (distance, length);  --  Buffering is not needed in this mode
      when Taillaule_Deflation_Method =>
        Push ((distance_length, 0, distance, length, expand));
    end case;
  end Put_or_delay_DL_code;

  --------------------------------
  -- LZ77 front-end compression --
  --------------------------------

  procedure Encode is

    feedback_milestone,
    Bytes_in   : Zip_Streams.ZS_Size_Type := 0;   --  Count of input file bytes processed
    user_aborting : Boolean;
    PctDone : Natural;

    function Read_byte return Byte is
      b : Byte;
      use Zip_Streams;
    begin
      b := IO_buffers.InBuf (IO_buffers.InBufIdx);
      IO_buffers.InBufIdx := IO_buffers.InBufIdx + 1;
      Zip.CRC_Crypto.Update (CRC, (1 => b));
      Bytes_in := Bytes_in + 1;
      if feedback /= null then
        if Bytes_in = 1 then
          feedback (0, False, user_aborting);
        end if;
        if feedback_milestone > 0 and then
           ((Bytes_in - 1) mod feedback_milestone = 0
            or Bytes_in = ZS_Size_Type (input_size))
        then
          if input_size_known then
            PctDone := Integer ((100.0 * Float (Bytes_in)) / Float (input_size));
            feedback (PctDone, False, user_aborting);
          else
            feedback (0, False, user_aborting);
          end if;
          if user_aborting then
            raise User_abort;
          end if;
        end if;
      end if;
      return b;
    end Read_byte;

    function More_bytes return Boolean is
    begin
      if IO_buffers.InBufIdx > IO_buffers.MaxInBufIdx then
        Read_Block (IO_buffers, input);
      end if;
      return not IO_buffers.InputEoF;
    end More_bytes;

    --  LZ77 parameters
    Look_Ahead_LZ77    : constant Integer := 258;
    String_buffer_size : constant := 2**15;  --  Required: 2**15 for Deflate, 2**16 for Deflate_e
    type Text_buffer_index is mod String_buffer_size;
    type Text_buffer is array (Text_buffer_index) of Byte;
    Text_Buf : Text_buffer;
    R : Text_buffer_index;

    --  If the DLE coding doesn't fit the format constraints, we need
    --  to decode it as a simple sequence of literals. The buffer used is
    --  called "Text" buffer by reference to "clear-text", but actually it
    --  is any binary data.

    procedure LZ77_emits_DL_code (distance, length : Integer) is
      --  NB: no worry, all arithmetics in Text_buffer_index are modulo String_buffer_size.
      b : Byte;
      copy_start : Text_buffer_index;
      expand : Expanded_data;
      ie : Positive := 1;
    begin
      if distance = String_buffer_size then  --  Happens with 7-Zip, cannot happen with Info-Zip.
        copy_start := R;
      else
        copy_start := R - Text_buffer_index (distance);
      end if;
      --  Expand into the circular text buffer to have it up to date
      for K in 0 .. Text_buffer_index (length - 1) loop
        b := Text_Buf (copy_start + K);
        Text_Buf (R) := b;
        R := R + 1;
        if ie <= max_expand then  --  Also memorize short sequences for LZ buffer
          expand (ie) := b;       --  for the case a block needs to be stored in clear.
          ie := ie + 1;
        end if;
      end loop;
      if distance in Distance_range and length in Length_range then
        Put_or_delay_DL_code (distance, length, expand);
      else
        if trace then
          Put_Line (log,
            "<> Too bad, cannot encode this distance-length pair, " &
            "then we have to expand to output (dist = " & Integer'Image (distance) &
            " len=" & Integer'Image (length) & ")"
          );
        end if;
        for K in 0 .. Text_buffer_index (length - 1) loop
          Put_or_delay_literal_byte (Text_Buf (copy_start + K));
        end loop;
      end if;
    end LZ77_emits_DL_code;

    procedure LZ77_emits_literal_byte (b : Byte) is
    begin
      Text_Buf (R) := b;
      R := R + 1;
      Put_or_delay_literal_byte (b);
    end LZ77_emits_literal_byte;

    procedure Dummy_Estimate_DL_Codes (
      matches          : in out LZ77.Matches_Array;
      old_match_index  : in     Natural;
      prefixes         : in     LZ77.Byte_Array;
      best_score_index :    out Positive;
      best_score_set   :    out LZ77.Prefetch_Index_Type;
      match_trace      :    out LZ77.DLP_Array
    )
    is null;

    LZ77_choice : constant array (Deflation_Method) of LZ77.Method_Type :=
      (Deflate_Fixed  => LZ77.IZ_4,
       Deflate_0      => LZ77.No_LZ77,
       Deflate_1      => LZ77.IZ_6,     --  level 6 is the default in Info-Zip's zip.exe
       Deflate_2      => LZ77.IZ_8,
       Deflate_3      => LZ77.IZ_10,
       Deflate_R      => LZ77.Rich);

    procedure My_LZ77 is
      new LZ77.Encode
         (String_buffer_size => String_buffer_size,
          Look_Ahead         => Look_Ahead_LZ77,
          Threshold          => 2,  --  From a string match length > 2, a DL code is sent
          Method             => LZ77_choice (method),
          Read_Byte          => Read_byte,
          More_Bytes         => More_bytes,
          Write_Literal      => LZ77_emits_literal_byte,
          Write_DL_Code      => LZ77_emits_DL_code,
          Estimate_DL_Codes  => Dummy_Estimate_DL_Codes
        );

  begin  --  Encode
    Read_Block (IO_buffers, input);
    R := Text_buffer_index (String_buffer_size - Look_Ahead_LZ77);
    if input_size_known then
      feedback_milestone := Zip_Streams.ZS_Size_Type (input_size / feedback_steps);
    end if;
    case method is
      when Deflate_Fixed =>  --  "Fixed" (predefined) compression structure
        --  We have only one compressed data block, then it is already the last one.
        Put_Bits (code => 1, code_size => 1);  --  Signals last block
        Put_Bits (code => 1, code_size => 2);  --  Signals a "fixed" block
      when Taillaule_Deflation_Method =>
        null;  --  No start data sent, all is delayed
    end case;
    ----------------------------------------------------------------
    --  The whole compression is happening in the following line: --
    ----------------------------------------------------------------
    My_LZ77;
    --  Done. Send the code signaling the end of compressed data block:
    case method is
      when Deflate_Fixed =>
        Put_Huffman_Code (curr_descr.lit_len (End_Of_Block));
      when Taillaule_Deflation_Method =>
        if lz_buffer_index * 2 = 0 then  --  Already flushed at latest Push, or empty data
          if block_to_finish and then last_block_type in fixed .. dynamic then
            Put_Huffman_Code (curr_descr.lit_len (End_Of_Block));
          end if;
        else
          Flush_half_buffer (last_flush => True);
          if last_block_type in fixed .. dynamic then
            Put_Huffman_Code (curr_descr.lit_len (End_Of_Block));
          end if;
        end if;
        if not last_block_marked then
          --  Add a fake fixed block, just to have a final block...
          Put_Bits (code => 1, code_size => 1);  --  Signals last block
          Put_Bits (code => 1, code_size => 2);  --  Signals a "fixed" block
          curr_descr := Deflate_fixed_descriptors;
          Put_Huffman_Code (curr_descr.lit_len (End_Of_Block));
        end if;
    end case;
  end Encode;

  procedure Deallocation is
  begin
    Dispose (lz_buffer);
    Deallocate_Buffers (IO_buffers);
  end Deallocation;

begin
  if trace then
    begin
      Open (log, Append_File, log_name);
    exception
      when Name_Error =>
        Create (log, Out_File, log_name);
    end;
    Put (log, "New stream" & sep & sep & sep & sep & sep & sep & sep & sep);
    if input_size_known then
      Put (log, sep & Zip_64_Data_Size_Type'Image (input_size) &
               sep & sep & sep & sep & sep & sep & "bytes input");
    end if;
    New_Line (log);
  end if;
  Allocate_Buffers (IO_buffers, input_size_known, input_size);
  output_size := 0;
  lz_buffer := new Full_range_LZ_buffer_type;
  begin
    Encode;
    compression_ok := True;
    Flush_bit_buffer;
    Flush_byte_buffer;
  exception
    when Compression_inefficient =>  --  Escaped from Encode
      compression_ok := False;
  end;
  if trace then
    Close (log);
  end if;
  Deallocation;
exception
  when others =>
    Deallocation;
    raise;
end Zip.Compress.Deflate;


Zip-Ada: Ada library for zip archive files (.zip). Ada programming.
Some news about Zip-Ada and other Ada projects on Gautier's blog.