Back to... Zip-Ada

Source file : unzip-decompress-huffman.adb



--  Legal licensing note:

--  Copyright (c) 1999 .. 2023 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

with Ada.Text_IO,
     Ada.Unchecked_Deallocation;
with Interfaces;

package body UnZip.Decompress.Huffman is

  --  Note from Pascal source:
  --  C code by info-zip group, translated to pascal by Christian Ghisler
  --  based on unz51g.zip

  --  Free huffman tables starting with table where t points to

  procedure HufT_free (tl : in out p_Table_list) is

    procedure  Dispose is new
      Ada.Unchecked_Deallocation (HufT_table, p_HufT_table);
    procedure  Dispose is new
      Ada.Unchecked_Deallocation (Table_list, p_Table_list);

    current : p_Table_list;
    tcount  : Natural := 0;  --  just a stat. Idea: replace table_list with an array

  begin
    if full_trace then
      Ada.Text_IO.Put ("[HufT_Free... ");
    end if;
    while tl /= null loop
      Dispose (tl.table);  --  destroy the Huffman table
      current := tl;
      tl     := tl.next;
      Dispose (current);   --  destroy the current node
      if full_trace then
        tcount := tcount + 1;
      end if;
    end loop;
    if full_trace then
      Ada.Text_IO.Put_Line (Integer'Image (tcount) & " tables]");
    end if;
  end HufT_free;

  --  Build huffman table from code lengths given by array b

  procedure HufT_build (b    : Length_array;
                        s    : Integer;
                        d, e : Length_array;
                        tl   :    out p_Table_list;
                        m    : in out Integer;
             huft_incomplete :    out Boolean)
  is
    use Interfaces;

    b_max   : constant := 16;
    b_maxp1 : constant := b_max + 1;

    --  bit length count table
    count : array (0 .. b_maxp1) of Integer := (others => 0);

    f   : Integer;                    --  i repeats in table every f entries
    g   : Integer;                    --  max. code length
    i,                                --  counter, current code
      j : Integer;                    --  counter
    kcc : Integer;                    --  number of bits in current code

    c_idx, v_idx : Natural;           --  array indices

    current_table_ptr : p_HufT_table := null;
    current_node_ptr  : p_Table_list := null;  --  curr. node for the curr. table
    new_node_ptr      : p_Table_list;          --  new node for the new table

    new_entry : HufT;                  --  table entry for structure assignment

    u : array (0 .. b_max) of p_HufT_table;   --  table stack

    n_max : constant := 288;
    --  values in order of bit length
    v : array (0 .. n_max) of Integer := (others => 0);
    el_v, el_v_m_s : Integer;

    w : Natural := 0;                        -- bits before this table

    offset, code_stack : array (0 .. b_maxp1) of Integer;

    table_level : Integer := -1;
    bits : array (Integer'(-1) .. b_maxp1) of Integer;
    --  ^bits (table_level) = # bits in table of level table_level

    y  : Integer;                     --  number of dummy codes added
    z  : Natural := 0;                --  number of entries in current table
    el : Integer;                     --  length of eob code=code 256

    no_copy_length_array : constant Boolean := d'Length = 0 or e'Length = 0;

  begin
    if full_trace then
      Ada.Text_IO.Put ("[HufT_Build...");
    end if;
    tl := null;

    if b'Length > 256 then -- set length of EOB code, if any
      el := b (256);
    else
      el := b_max;
    end if;

    --  Generate counts for each bit length

    for k in b'Range loop
      if b (k) > b_max then
        --  m := 0;  --  GNAT 2005 doesn't like it (warning).
        raise huft_error;
      end if;
      count (b (k)) := count (b (k)) + 1;
    end loop;

    if count (0) = b'Length then
      m := 0;
      huft_incomplete := False;  --  spotted by Tucker Taft, 19-Aug-2004
      return;  --  complete
    end if;

    --  Find minimum and maximum length, bound m by those

    j := 1;
    while j <= b_max and then count (j) = 0 loop
      j := j + 1;
    end loop;
    kcc := j;
    if m < j then
      m := j;
    end if;
    i := b_max;
    while i > 0 and then count (i) = 0 loop
      i := i - 1;
    end loop;
    g := i;
    if m > i then
      m := i;
    end if;

    --  Adjust last length count to fill out codes, if needed

    y := Integer (Shift_Left (Unsigned_32'(1), j)); -- y:= 2 ** j;
    while j < i loop
      y := y - count (j);
      if y < 0 then
        raise huft_error;
      end if;
      y := y * 2;
      j := j + 1;
    end loop;

    y := y - count (i);
    if y < 0 then
      raise huft_error;
    end if;
    count (i) := count (i) + y;

    --  Generate starting offsets into the value table for each length

    offset (1) := 0;
    j := 0;
    for idx in 2 .. i loop
      j := j + count (idx - 1);
      offset (idx) := j;
    end loop;

    --  Make table of values in order of bit length

    for idx in b'Range loop
      j := b (idx);
      if j /= 0 then
        v (offset (j)) := idx - b'First;
        offset (j) := offset (j) + 1;
      end if;
    end loop;

    --  Generate huffman codes and for each, make the table entries

    code_stack (0) := 0;
    i := 0;
    v_idx := v'First;
    bits (-1) := 0;

    --  go through the bit lengths (kcc already is bits in shortest code)
    for k in kcc .. g loop

      for am1 in reverse 0 .. count (k) - 1 loop  --  a counts codes of length k

        --  here i is the huffman code of length k bits for value v(v_idx)
        while k > w + bits (table_level) loop

          w := w + bits (table_level);    --  Length of tables to this position
          table_level := table_level + 1;
          z := g - w;                     --  Compute min size table <= m bits
          if z > m then
            z := m;
          end if;
          j := k - w;
          f := Integer (Shift_Left (Unsigned_32'(1), j)); -- f:= 2 ** j;
          if f > am1 + 2 then   --  Try a k-w bit table
            f := f - (am1 + 2);
            c_idx := k;
            loop                --  Try smaller tables up to z bits
              j := j + 1;
              exit when j >= z;
              f := f * 2;
              c_idx := c_idx + 1;
              exit when f - count (c_idx) <= 0;
              f := f - count (c_idx);
            end loop;
          end if;

          if w + j > el and then  w < el  then
            j := el - w;       --  Make EOB code end at table
          end if;
          if w = 0 then
            j := m;  --  Fix: main table always m bits!
          end if;
          z := Integer (Shift_Left (Unsigned_32'(1), j)); -- z:= 2 ** j;
          bits (table_level) := j;

          --  Allocate and link new table

          begin
            current_table_ptr := new HufT_table (0 .. z);
            new_node_ptr      := new Table_list'(current_table_ptr, null);
          exception
            when Storage_Error =>
              raise huft_out_of_memory;
          end;

          if current_node_ptr = null then  --  first table
            tl := new_node_ptr;
          else
            current_node_ptr.next := new_node_ptr;   --  not my first...
          end if;

          current_node_ptr := new_node_ptr;  --  always non-Null from there

          u (table_level) := current_table_ptr;

          --  Connect to last table, if there is one

          if table_level > 0 then
            code_stack (table_level) := i;
            new_entry.bits           := bits (table_level - 1);
            new_entry.extra_bits     := 16 + j;
            new_entry.next_table     := current_table_ptr;

            j := Integer (
              Shift_Right (Unsigned_32 (i) and
                (Shift_Left (Unsigned_32'(1), w) - 1),
                w - bits (table_level - 1))
              );

            --  Test against bad input!

            if j > u (table_level - 1)'Last then
              raise huft_error;
            end if;
            u (table_level - 1) (j) := new_entry;
          end if;

        end loop;

        --  Set up table entry in new_entry

        new_entry.bits      := k - w;
        new_entry.next_table := null;   --  Unused

        if v_idx >= b'Length then
          new_entry.extra_bits := invalid;
        else
          el_v := v (v_idx);
          el_v_m_s := el_v - s;
          if el_v_m_s < 0 then    --  Simple code, raw value
            if el_v < 256 then
              new_entry.extra_bits := 16;
            else
              new_entry.extra_bits := 15;
            end if;
            new_entry.n := el_v;
          else                    --  Non-simple -> lookup in lists
            if no_copy_length_array then
              raise huft_error;
            end if;
            new_entry.extra_bits := e (el_v_m_s);
            new_entry.n          := d (el_v_m_s);
          end if;
          v_idx := v_idx + 1;
        end if;

        --  fill code-like entries with new_entry
        f := Integer (Shift_Left (Unsigned_32'(1), k - w));
        --  i.e. f := 2 ** (k-w);
        j := Integer (Shift_Right (Unsigned_32 (i), w));
        while j < z loop
          current_table_ptr (j) := new_entry;
          j := j + f;
        end loop;

        --  backwards increment the k-bit code i
        j := Integer (Shift_Left (Unsigned_32'(1), k - 1));
        --  i.e.: j:= 2 ** (k-1)
        while (Unsigned_32 (i) and Unsigned_32 (j)) /= 0 loop
          i := Integer (Unsigned_32 (i) xor Unsigned_32 (j));
          j :=  j / 2;
        end loop;
        i := Integer (Unsigned_32 (i) xor Unsigned_32 (j));

        --  backup over finished tables
        while
          Integer (Unsigned_32 (i) and (Shift_Left (1, w) - 1)) /=
          code_stack (table_level)
        loop
          table_level := table_level - 1;
          w := w - bits (table_level);  --  Size of previous table!
        end loop;

      end loop;  --  am1
    end loop;  --  k

    if full_trace then
      Ada.Text_IO.Put_Line ("finished]");
    end if;

    huft_incomplete := y /= 0 and g /= 1;

  exception
    when others =>
      HufT_free (tl);
      raise;
  end HufT_build;

end UnZip.Decompress.Huffman;


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