Back to... Zip-Ada

Source file : bzip2_decoding.adb


-- See bzip2_decoding.ads for legal stuff
--
-- Documentation pointers:
--
--   Burrows-Wheeler transform
--     http://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform
--   MTF Move-To-Front
--     http://fr.wikipedia.org/wiki/Move-To-Front
--
-- Translated on 20-Oct-2009 by (New) P2Ada v. 15-Nov-2006
--
--with Ada.Text_IO;                       use Ada.Text_IO;

with Ada.Unchecked_Deallocation;

package body BZip2_Decoding is

  procedure Decompress is

    max_groups    : constant:= 6;
    max_alpha_size: constant:= 258;
    max_code_len  : constant:= 23;
    group_size    : constant:= 50;
    max_selectors : constant:= 2 + (900_000 / group_size);

    sub_block_size: constant:= 100_000;

    type Length_array is array (Integer range <>) of Natural;

    block_randomized: Boolean:= False;
    block_size: Natural;

    use Interfaces;

    type Tcardinal_array is array (Integer range <>) of Unsigned_32;
    type Pcardinal_array is access Tcardinal_array;
    procedure Dispose is new Ada.Unchecked_Deallocation(Tcardinal_array, Pcardinal_array);
    tt: Pcardinal_array;
    tt_count: Natural;

    rle_run_left: Natural:= 0;
    rle_run_data: Unsigned_8:= 0;
    decode_available: Natural:= Natural'Last;
    block_origin: Natural:= 0;
    read_data: Unsigned_8:= 0;
    bits_available: Natural:= 0;
    inuse_count: Natural;
    seq_to_unseq: array (0 .. 255 ) of Natural;
    alpha_size: Natural;
    group_count: Natural;
    --
    selector_count: Natural;
    selector, selector_mtf: array (0 .. max_selectors) of Unsigned_8;
    --
    type Alpha_U32_array is array (0 .. max_alpha_size) of Unsigned_32;
    type Alpha_Nat_array is array (0 .. max_alpha_size) of Natural;

    len  : array (0 .. max_groups) of Alpha_Nat_array;
    limit,
    base ,
    perm : array (0 .. max_groups) of Alpha_U32_array;
    --
    minlens: Length_array(0 .. max_groups);
    cftab: array (0 .. 257) of Natural;
    --
    end_reached: Boolean:= False;

    in_buf: Buffer(1 .. input_buffer_size);
    in_idx: Natural:= in_buf'Last + 1;

    function Read_byte return Unsigned_8 is
      res: Unsigned_8;
    begin
      if in_idx > in_buf'Last then
        Read(in_buf);
        in_idx:= in_buf'First;
      end if;
      res:= in_buf(in_idx);
      in_idx:= in_idx + 1;
      return res;
    end Read_byte;

    procedure hb_create_decode_tables(
       limit, base, perm: in out Alpha_U32_array;
       length           : in     Alpha_Nat_array;
       min_len, max_len : Natural;
       alpha_size       : Integer
    )
    is
      pp, idx: Integer;
      vec: Unsigned_32;
    begin
      pp:=0;
      for i in min_len .. max_len loop
        for j in 0 .. alpha_size-1 loop
          if length(j)=i then
            perm(pp):= Unsigned_32(j);
            pp:= pp + 1;
          end if;
        end loop;
      end loop;
      for i in 0 .. max_code_len-1 loop
        base(i):=0;
        limit(i):=0;
      end loop;
      for i in 0 .. alpha_size-1 loop
        idx:= length(i)+1;
        base(idx):= base(idx) + 1;
      end loop;
      for i in 1 .. max_code_len-1 loop
        base(i):= base(i) + base(i-1);
      end loop;
      vec:=0;
      for i in min_len .. max_len loop
          vec:= vec + base(i+1)-base(i);
          limit(i):= vec-1;
          vec:= vec * 2;
      end loop;
      for i in min_len+1 .. max_len loop
        base(i):=(limit(i-1)+1) * 2 - base(i);
      end loop;
    end hb_create_decode_tables;

    procedure Init is
      magic: String(1..3);
      b: Unsigned_8;
    begin
      --  Read the magic.
      for i in magic'Range loop
        b:= Read_byte;
        magic(i):= Character'Val(b);
      end loop;
      if magic /= "BZh" then
        raise bad_header_magic;
      end if;
      --  Read the block size and allocate the working array.
      b:= Read_byte;
      block_size:= Natural(b) - Character'Pos('0');
      tt:= new Tcardinal_array(0 .. block_size * sub_block_size);
    end Init;

    function get_bits(n: Natural) return Unsigned_8 is
      Result_get_bits : Unsigned_8;
      data: Unsigned_8;
    begin
      if n > bits_available then
        data:= Read_byte;
        Result_get_bits:= Shift_Right(read_data, 8-n) or Shift_Right(data, 8-(n-bits_available));
        read_data:= Shift_Left(data, n-bits_available);
        bits_available:= bits_available + 8;
      else
        Result_get_bits:= Shift_Right(read_data, 8-n);
        read_data:= Shift_Left(read_data, n);
      end if;
      bits_available:= bits_available - n;
      return Result_get_bits;
    end get_bits;

    function get_bits_32(n: Natural) return Unsigned_32 is
    begin
      return Unsigned_32(get_bits(n));
    end get_bits_32;

    function get_boolean return Boolean is
    begin
      return Boolean'Val(get_bits(1));
    end get_boolean;

    function get_byte return Unsigned_8 is
    begin
      return get_bits(8);
    end get_byte;

    function get_cardinal24 return Unsigned_32 is
    begin
      return Shift_Left(get_bits_32(8),16) or Shift_Left(get_bits_32(8),8) or get_bits_32(8);
    end get_cardinal24;

    function get_cardinal return Unsigned_32 is
    begin
      return Shift_Left(get_bits_32(8),24)  or
             Shift_Left(get_bits_32(8),16)  or
             Shift_Left(get_bits_32(8), 8)  or
             get_bits_32(8);
    end get_cardinal;

    --  Receive the mapping table. To save space, the inuse set is stored in pieces
    --  of 16 bits. First 16 bits are stored which pieces of 16 bits are used, then
    --  the pieces follow.
    procedure receive_mapping_table is
      inuse16: array(0 .. 15) of Boolean;
      --* inuse: array(0 .. 255) of Boolean; -- for dump purposes
    begin
      inuse16:= (others => False);
      --  Receive the first 16 bits which tell which pieces are stored.
      for i in 0 .. 15 loop
        inuse16(i):= get_boolean;
      end loop;
      --  Receive the used pieces.
      --* inuse:= (others => False);
      inuse_count:= 0;
      for i in 0 .. 15 loop
        if inuse16(i) then
          for j in 0 .. 15 loop
            if get_boolean then
              --* inuse(16*i+j):= True;
              seq_to_unseq(inuse_count):=16*i+j;
              inuse_count:= inuse_count + 1;
            end if;
          end loop;
        end if;
      end loop;
    end receive_mapping_table;

    --  Receives the selectors.
    procedure receive_selectors is
      j:Unsigned_8;
    begin
      group_count:= Natural(get_bits(3));
      selector_count:= Natural(Shift_Left(get_bits_32(8), 7) or get_bits_32(7));
      for i in 0 .. selector_count-1 loop
        j:=0;
        while get_boolean loop
          j:= j + 1;
          if j > 5 then
            raise data_error;
          end if;
        end loop;
        selector_mtf(i):=j;
      end loop;
    end receive_selectors;

    --  Undo the MTF values for the selectors.
    procedure undo_mtf_values is
      pos: array (0 .. max_groups) of Natural;
      v, tmp: Natural;
    begin
      for w in 0 .. group_count-1 loop
        pos(w):=w;
      end loop;
      for i in 0 .. selector_count-1 loop
        v:= Natural(selector_mtf(i));
        tmp:=pos(v);
        while v/=0 loop
          pos(v):= pos(v-1);
          v:= v - 1;
        end loop;
        pos(0):= tmp;
        selector(i):= Unsigned_8(tmp);
      end loop;
    end undo_mtf_values;

    procedure receive_coding_tables is
      curr: Natural;
    begin
      for t in 0 .. group_count-1 loop
        curr:= Natural(get_bits(5));
        for i in 0 .. alpha_size-1 loop
          loop
            if curr not in 1..20 then
              raise data_error;
            end if;
            exit when not get_boolean;
            if get_boolean then
              curr:= curr - 1;
            else
              curr:= curr + 1;
            end if;
          end loop;
          len(t)(i):=curr;
        end loop;
      end loop;
    end receive_coding_tables;

    --  Builds the Huffman tables.
    procedure make_hufftab is
      minlen, maxlen: Natural;
    begin
      for t in 0 .. group_count-1 loop
        minlen:= 32;
        maxlen:= 0;
        for i in 0 .. alpha_size-1 loop
          if len(t)(i) > maxlen then
            maxlen:= len(t)(i);
          end if;
          if len(t)(i) < minlen then
            minlen:= len(t)(i);
          end if;
        end loop;
        hb_create_decode_tables(
          limit(t), base(t), perm(t), len(t),
          minlen, maxlen, alpha_size
        );
        minlens(t):= minlen;
      end loop;
    end make_hufftab;

    -------------------------
    -- MTF - Move To Front --
    -------------------------

    procedure receive_mtf_values is
      --
      mtfa_size: constant:= 4096;
      mtfl_size: constant:= 16;
      mtfbase: array (0 .. 256 / mtfl_size-1) of Natural;
      mtfa: array (0 .. mtfa_size-1) of Natural;
      --
      procedure init_mtf is
        k: Natural:= mtfa_size-1;
      begin
        for i in reverse 0 .. 256  /  mtfl_size-1 loop
          for j in reverse 0 .. mtfl_size-1 loop
            mtfa(k):= i*mtfl_size + j;
            k:= k - 1;
          end loop;
          mtfbase(i):= k+1;
        end loop;
      end init_mtf;
      --
      group_pos, group_no: Integer;
      gminlen, gsel: Natural;
      --
      function get_mtf_value return Unsigned_32 is
        zn: Natural;
        zvec: Unsigned_32;
      begin
        if group_pos = 0 then
          group_no:= group_no + 1;
          group_pos:= group_size;
          gsel:= Natural(selector(group_no));
          gminlen:= minlens(gsel);
        end if;
        group_pos:= group_pos - 1;
        zn:= gminlen;
        zvec:= get_bits_32(zn);
        while zvec > limit(gsel)(zn) loop
          zn:= zn + 1;
          zvec:= Shift_Left(zvec, 1) or get_bits_32(1);
        end loop;
        return perm(gsel)(Natural(zvec-base(gsel)(zn)));
      end get_mtf_value;
      --
      procedure move_mtf_block is
        j, k: Natural;
      begin
        k:= mtfa_size;
        for i in reverse 0 .. 256  /  mtfl_size-1 loop
          j:= mtfbase(i);
          mtfa(k-16..k-1):= mtfa(j..j+15);
          k:= k - 16;
          mtfbase(i):= k;
        end loop;
      end move_mtf_block;
      --
      run_b: constant:= 1;
      t: Natural;
      next_sym: Unsigned_32;
      es: Natural;
      n, nn: Natural;
      p,q: Natural; -- indexes mtfa
      u,v: Natural; -- indexes mtfbase
      lno, off: Natural;
    begin -- receive_mtf_values
      group_no:= -1;
      group_pos:= 0;
      t:= 0;
      cftab:= (others => 0);
      init_mtf;
      next_sym:= get_mtf_value;
      --
      while Natural(next_sym) /= inuse_count+1 loop
        if next_sym <= run_b then
          es:= 0;
          n:= 0;
          loop
            es:= es + Natural(Shift_Left(next_sym+1, n));
            n:= n + 1;
            next_sym:= get_mtf_value;
            exit when next_sym > run_b;
          end loop;
          n:= seq_to_unseq( mtfa(mtfbase(0)) );
          cftab(n):= cftab(n) + es;
          if t+es > sub_block_size * block_size then
            raise data_error;
          end if;
          while es > 0 loop
            tt(t):= Unsigned_32(n);
            es:= es - 1;
            t:= t + 1;
          end loop;
        else
          nn:= Natural(next_sym - 1);
          if nn < mtfl_size then
            -- Avoid the costs of the general case.
            p:= mtfbase(0);
            q:= p + nn;
            n:= mtfa(q);
            loop
              mtfa(q):= mtfa(q-1);
              q:= q - 1;
              exit when q = p;
            end loop;
            mtfa(q):= n;
          else
            --  General case.
            lno:= nn   /   mtfl_size;
            off:= nn  mod  mtfl_size;
            p:= mtfbase(lno);
            q:= p + off;
            n:= mtfa(q);
            while q /= p loop
              mtfa(q):= mtfa(q-1);
              q:= q - 1;
            end loop;
            u:= mtfbase'First;
            v:= u + lno;
            loop
              mtfa(mtfbase(v)):= mtfa(mtfbase(v-1)+mtfl_size-1);
              v:= v - 1;
              mtfbase(v):= mtfbase(v) - 1;
              exit when v = u;
            end loop;
            mtfa( mtfbase(v) ):= n;
            if mtfbase(v) = 0 then
              move_mtf_block;
            end if;
          end if;
          cftab(seq_to_unseq(n)):= cftab(seq_to_unseq(n)) + 1;
          tt(t):= Unsigned_32(seq_to_unseq(n));
          t:= t + 1;
          if t > sub_block_size * block_size then
            raise data_error;
          end if;
          next_sym:= get_mtf_value;
        end if;
      end loop;
      tt_count:= t;
      --  Setup cftab to facilitate generation of T^(-1).
      t:= 0;
      for i in 0 .. 256 loop
        nn:= cftab(i);
        cftab(i):= t;
        t:= t + nn;
      end loop;
    end receive_mtf_values;

    procedure detransform is
      a: Unsigned_32;
      p,q,r,i255: Natural;
    begin
      a:= 0;
      p:= tt'First;
      q:= p + tt_count;
      while p /= q loop
        i255:= Natural(tt(p) and 16#ff#);
        r:= cftab(i255);
        cftab(i255):= cftab(i255) + 1;
        tt(r):= tt(r) or a;
        a:= a + 256;
        p:= p + 1;
      end loop;
    end detransform;

    -- Cyclic redundancy check to verify uncompressed block data integrity

    package CRC is
      procedure Init( CRC: out Unsigned_32 );
      function  Final( CRC: Unsigned_32 ) return Unsigned_32;
      procedure Update( CRC: in out Unsigned_32; val: Unsigned_8 );
        pragma Inline( Update );
    end CRC;

    package body CRC is

      CRC32_Table :
        constant array( Unsigned_32'(0)..255 ) of Unsigned_32:= (
           16#00000000#, 16#04c11db7#, 16#09823b6e#, 16#0d4326d9#,
           16#130476dc#, 16#17c56b6b#, 16#1a864db2#, 16#1e475005#,
           16#2608edb8#, 16#22c9f00f#, 16#2f8ad6d6#, 16#2b4bcb61#,
           16#350c9b64#, 16#31cd86d3#, 16#3c8ea00a#, 16#384fbdbd#,
           16#4c11db70#, 16#48d0c6c7#, 16#4593e01e#, 16#4152fda9#,
           16#5f15adac#, 16#5bd4b01b#, 16#569796c2#, 16#52568b75#,
           16#6a1936c8#, 16#6ed82b7f#, 16#639b0da6#, 16#675a1011#,
           16#791d4014#, 16#7ddc5da3#, 16#709f7b7a#, 16#745e66cd#,
           16#9823b6e0#, 16#9ce2ab57#, 16#91a18d8e#, 16#95609039#,
           16#8b27c03c#, 16#8fe6dd8b#, 16#82a5fb52#, 16#8664e6e5#,
           16#be2b5b58#, 16#baea46ef#, 16#b7a96036#, 16#b3687d81#,
           16#ad2f2d84#, 16#a9ee3033#, 16#a4ad16ea#, 16#a06c0b5d#,
           16#d4326d90#, 16#d0f37027#, 16#ddb056fe#, 16#d9714b49#,
           16#c7361b4c#, 16#c3f706fb#, 16#ceb42022#, 16#ca753d95#,
           16#f23a8028#, 16#f6fb9d9f#, 16#fbb8bb46#, 16#ff79a6f1#,
           16#e13ef6f4#, 16#e5ffeb43#, 16#e8bccd9a#, 16#ec7dd02d#,
           16#34867077#, 16#30476dc0#, 16#3d044b19#, 16#39c556ae#,
           16#278206ab#, 16#23431b1c#, 16#2e003dc5#, 16#2ac12072#,
           16#128e9dcf#, 16#164f8078#, 16#1b0ca6a1#, 16#1fcdbb16#,
           16#018aeb13#, 16#054bf6a4#, 16#0808d07d#, 16#0cc9cdca#,
           16#7897ab07#, 16#7c56b6b0#, 16#71159069#, 16#75d48dde#,
           16#6b93dddb#, 16#6f52c06c#, 16#6211e6b5#, 16#66d0fb02#,
           16#5e9f46bf#, 16#5a5e5b08#, 16#571d7dd1#, 16#53dc6066#,
           16#4d9b3063#, 16#495a2dd4#, 16#44190b0d#, 16#40d816ba#,
           16#aca5c697#, 16#a864db20#, 16#a527fdf9#, 16#a1e6e04e#,
           16#bfa1b04b#, 16#bb60adfc#, 16#b6238b25#, 16#b2e29692#,
           16#8aad2b2f#, 16#8e6c3698#, 16#832f1041#, 16#87ee0df6#,
           16#99a95df3#, 16#9d684044#, 16#902b669d#, 16#94ea7b2a#,
           16#e0b41de7#, 16#e4750050#, 16#e9362689#, 16#edf73b3e#,
           16#f3b06b3b#, 16#f771768c#, 16#fa325055#, 16#fef34de2#,
           16#c6bcf05f#, 16#c27dede8#, 16#cf3ecb31#, 16#cbffd686#,
           16#d5b88683#, 16#d1799b34#, 16#dc3abded#, 16#d8fba05a#,
           16#690ce0ee#, 16#6dcdfd59#, 16#608edb80#, 16#644fc637#,
           16#7a089632#, 16#7ec98b85#, 16#738aad5c#, 16#774bb0eb#,
           16#4f040d56#, 16#4bc510e1#, 16#46863638#, 16#42472b8f#,
           16#5c007b8a#, 16#58c1663d#, 16#558240e4#, 16#51435d53#,
           16#251d3b9e#, 16#21dc2629#, 16#2c9f00f0#, 16#285e1d47#,
           16#36194d42#, 16#32d850f5#, 16#3f9b762c#, 16#3b5a6b9b#,
           16#0315d626#, 16#07d4cb91#, 16#0a97ed48#, 16#0e56f0ff#,
           16#1011a0fa#, 16#14d0bd4d#, 16#19939b94#, 16#1d528623#,
           16#f12f560e#, 16#f5ee4bb9#, 16#f8ad6d60#, 16#fc6c70d7#,
           16#e22b20d2#, 16#e6ea3d65#, 16#eba91bbc#, 16#ef68060b#,
           16#d727bbb6#, 16#d3e6a601#, 16#dea580d8#, 16#da649d6f#,
           16#c423cd6a#, 16#c0e2d0dd#, 16#cda1f604#, 16#c960ebb3#,
           16#bd3e8d7e#, 16#b9ff90c9#, 16#b4bcb610#, 16#b07daba7#,
           16#ae3afba2#, 16#aafbe615#, 16#a7b8c0cc#, 16#a379dd7b#,
           16#9b3660c6#, 16#9ff77d71#, 16#92b45ba8#, 16#9675461f#,
           16#8832161a#, 16#8cf30bad#, 16#81b02d74#, 16#857130c3#,
           16#5d8a9099#, 16#594b8d2e#, 16#5408abf7#, 16#50c9b640#,
           16#4e8ee645#, 16#4a4ffbf2#, 16#470cdd2b#, 16#43cdc09c#,
           16#7b827d21#, 16#7f436096#, 16#7200464f#, 16#76c15bf8#,
           16#68860bfd#, 16#6c47164a#, 16#61043093#, 16#65c52d24#,
           16#119b4be9#, 16#155a565e#, 16#18197087#, 16#1cd86d30#,
           16#029f3d35#, 16#065e2082#, 16#0b1d065b#, 16#0fdc1bec#,
           16#3793a651#, 16#3352bbe6#, 16#3e119d3f#, 16#3ad08088#,
           16#2497d08d#, 16#2056cd3a#, 16#2d15ebe3#, 16#29d4f654#,
           16#c5a92679#, 16#c1683bce#, 16#cc2b1d17#, 16#c8ea00a0#,
           16#d6ad50a5#, 16#d26c4d12#, 16#df2f6bcb#, 16#dbee767c#,
           16#e3a1cbc1#, 16#e760d676#, 16#ea23f0af#, 16#eee2ed18#,
           16#f0a5bd1d#, 16#f464a0aa#, 16#f9278673#, 16#fde69bc4#,
           16#89b8fd09#, 16#8d79e0be#, 16#803ac667#, 16#84fbdbd0#,
           16#9abc8bd5#, 16#9e7d9662#, 16#933eb0bb#, 16#97ffad0c#,
           16#afb010b1#, 16#ab710d06#, 16#a6322bdf#, 16#a2f33668#,
           16#bcb4666d#, 16#b8757bda#, 16#b5365d03#, 16#b1f740b4#
      );

      procedure Update( CRC: in out Unsigned_32; val: Unsigned_8 ) is
      begin
        CRC :=
          CRC32_Table( 16#FF# and ( Shift_Right(CRC, 24) xor Unsigned_32( val ) ) )
          xor
          Shift_Left( CRC , 8 );
      end Update;

      procedure Init( CRC: out Unsigned_32 ) is
      begin
        CRC:= 16#FFFF_FFFF#;
      end Init;

      function Final( CRC: Unsigned_32 ) return Unsigned_32 is
      begin
        return not CRC;
      end Final;

    end CRC;

    compare_final_CRC: Boolean:= False;
    stored_blockcrc, mem_stored_blockcrc, computed_crc: Unsigned_32;

    -- Decode a new compressed block.
    function decode_block return Boolean is
      magic: String(1 .. 6);
    begin
      for i in 1 .. 6 loop
        magic(i):= Character'Val(get_byte);
      end loop;
      if magic = "1AY&SY" then
        if check_CRC then
          if compare_final_CRC then
            null; -- initialisation is delayed until the rle buffer is empty
          else
            CRC.Init(computed_crc); -- Initialize for next block.
          end if;
        end if;
        stored_blockcrc:= get_cardinal;
        block_randomized:= get_boolean;
        block_origin:= Natural(get_cardinal24);
        --  Receive the mapping table.
        receive_mapping_table;
        alpha_size:= inuse_count + 2;
        --  Receive the selectors.
        receive_selectors;
        --  Undo the MTF values for the selectors.
        undo_mtf_values;
        --  Receive the coding tables.
        receive_coding_tables;
        --  Build the Huffman tables.
        make_hufftab;
        --  Receive the MTF values.
        receive_mtf_values;
        --  Undo the Burrows Wheeler transformation.
        detransform;
        decode_available:= tt_count;
        return True;
      elsif magic = Character'Val(16#17#) & "rE8P" & Character'Val(16#90#) then
        return False;
      else
        raise bad_block_magic;
      end if;
    end decode_block;

    next_rle_idx: Integer:= -2;
    buf: Buffer(1 .. output_buffer_size);
    last: Natural;

    procedure Read is
      shorten: Natural:= 0;

      procedure rle_read is
        rle_len: Natural;
        data: Unsigned_8;
        idx: Integer:= buf'First;
        count: Integer:= buf'Length;
        --
        procedure rle_write is
          pragma Inline(rle_write);
        begin
          loop
            buf(idx):= data;
            idx:= idx + 1;
            count:= count - 1;
            rle_len:= rle_len - 1;
            if check_CRC then
              CRC.Update(computed_crc, data);
              if rle_len = 0 and then compare_final_CRC then
                if CRC.Final(computed_crc) /= mem_stored_blockcrc then
                  raise block_crc_check_failed;
                end if;
                compare_final_CRC:= False;
                CRC.Init(computed_crc); -- Initialize for next block.
              end if;
            end if;
            exit when rle_len = 0 or count = 0;
          end loop;
        end rle_write;
        --
        -- handle extreme cases of data of length 1, 2
        input_dried: exception;
        --
        -- Make next_rle_idx index to the next decoded byte.
        -- If next_rle_idx did index to the last
        -- byte in the current block, decode the next block.
        --
        procedure consume_rle is
          pragma Inline(consume_rle);
        begin
          next_rle_idx:= Integer(Shift_Right(tt(next_rle_idx),8));
          decode_available:= decode_available - 1;
          if decode_available = 0 then
            compare_final_CRC:= True;
            mem_stored_blockcrc:= stored_blockcrc;
            -- ^ There might be a new block when last block's
            --   rle is finally emptied.
            --
            -- ** New block
            if decode_block then
              next_rle_idx:= Natural(Shift_Right(tt(block_origin),8));
            else
              next_rle_idx:= -1;
              end_reached:= True;
            end if;
            -- **
            if end_reached then
              raise input_dried;
            end if;
          end if;
        end consume_rle;
        --
        function rle_byte return Unsigned_8 is
          pragma Inline(rle_byte);
        begin
          return Unsigned_8(tt(next_rle_idx) and 16#FF#);
        end rle_byte;
        --
        function rle_possible return Boolean is
          pragma Inline(rle_possible);
        begin
          return decode_available > 0 and then data = rle_byte;
        end rle_possible;
        --
      begin -- rle_read
        rle_len:= rle_run_left;
        data:= rle_run_data;
        if block_randomized then
          raise randomized_not_yet_implemented;
        end if;
        if rle_len /= 0 then
          rle_write;
          if count = 0 then
            shorten:= 0;
            rle_run_data:= data;
            rle_run_left:= rle_len;
            return;
          end if;
        end if;
        begin
          -- The big loop
          loop
            if decode_available = 0 or end_reached then
              exit;
            end if;
            rle_len:= 1;
            data:= rle_byte;
            consume_rle;
            if rle_possible then
              rle_len:= rle_len + 1;
              consume_rle;
              if rle_possible then
                rle_len:= rle_len + 1;
                consume_rle;
                if rle_possible then
                  consume_rle;
                  rle_len:= rle_len + Natural(rle_byte)+1;
                  consume_rle;
                end if;
              end if;
            end if;
            rle_write;
            exit when count = 0;
          end loop;
        exception
          when input_dried => rle_write;
        end;
        shorten:= count;
        rle_run_data:= data;
        rle_run_left:= rle_len;
      end rle_read;

    begin -- read
      last:= buf'Last;
      if decode_available = Natural'Last then
        --  Initialize the rle process:
        --       - Decode a block
        --       - Initialize pointer.
        if decode_block then
          next_rle_idx:= Natural(Shift_Right(tt(block_origin), 8));
        else
          next_rle_idx:= -1;
          end_reached:= True;
        end if;
      end if;
      rle_read;
      last:= last - shorten;
    end Read;

  begin
    Init;
    loop
      Read;
      Write( buf(1..last) );
      exit when end_reached and rle_run_left = 0;
    end loop;
    Dispose(tt);
  end Decompress;

end BZip2_Decoding;

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