You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
701 lines
20 KiB
701 lines
20 KiB
---------------------------------------------------------------- |
|
-- ZLib for Ada thick binding. -- |
|
-- -- |
|
-- Copyright (C) 2002-2004 Dmitriy Anisimkov -- |
|
-- -- |
|
-- Open source license information is in the zlib.ads file. -- |
|
---------------------------------------------------------------- |
|
|
|
-- $Id: zlib.adb,v 1.31 2004/09/06 06:53:19 vagul Exp $ |
|
|
|
with Ada.Exceptions; |
|
with Ada.Unchecked_Conversion; |
|
with Ada.Unchecked_Deallocation; |
|
|
|
with Interfaces.C.Strings; |
|
|
|
with ZLib.Thin; |
|
|
|
package body ZLib is |
|
|
|
use type Thin.Int; |
|
|
|
type Z_Stream is new Thin.Z_Stream; |
|
|
|
type Return_Code_Enum is |
|
(OK, |
|
STREAM_END, |
|
NEED_DICT, |
|
ERRNO, |
|
STREAM_ERROR, |
|
DATA_ERROR, |
|
MEM_ERROR, |
|
BUF_ERROR, |
|
VERSION_ERROR); |
|
|
|
type Flate_Step_Function is access |
|
function (Strm : in Thin.Z_Streamp; Flush : in Thin.Int) return Thin.Int; |
|
pragma Convention (C, Flate_Step_Function); |
|
|
|
type Flate_End_Function is access |
|
function (Ctrm : in Thin.Z_Streamp) return Thin.Int; |
|
pragma Convention (C, Flate_End_Function); |
|
|
|
type Flate_Type is record |
|
Step : Flate_Step_Function; |
|
Done : Flate_End_Function; |
|
end record; |
|
|
|
subtype Footer_Array is Stream_Element_Array (1 .. 8); |
|
|
|
Simple_GZip_Header : constant Stream_Element_Array (1 .. 10) |
|
:= (16#1f#, 16#8b#, -- Magic header |
|
16#08#, -- Z_DEFLATED |
|
16#00#, -- Flags |
|
16#00#, 16#00#, 16#00#, 16#00#, -- Time |
|
16#00#, -- XFlags |
|
16#03# -- OS code |
|
); |
|
-- The simplest gzip header is not for informational, but just for |
|
-- gzip format compatibility. |
|
-- Note that some code below is using assumption |
|
-- Simple_GZip_Header'Last > Footer_Array'Last, so do not make |
|
-- Simple_GZip_Header'Last <= Footer_Array'Last. |
|
|
|
Return_Code : constant array (Thin.Int range <>) of Return_Code_Enum |
|
:= (0 => OK, |
|
1 => STREAM_END, |
|
2 => NEED_DICT, |
|
-1 => ERRNO, |
|
-2 => STREAM_ERROR, |
|
-3 => DATA_ERROR, |
|
-4 => MEM_ERROR, |
|
-5 => BUF_ERROR, |
|
-6 => VERSION_ERROR); |
|
|
|
Flate : constant array (Boolean) of Flate_Type |
|
:= (True => (Step => Thin.Deflate'Access, |
|
Done => Thin.DeflateEnd'Access), |
|
False => (Step => Thin.Inflate'Access, |
|
Done => Thin.InflateEnd'Access)); |
|
|
|
Flush_Finish : constant array (Boolean) of Flush_Mode |
|
:= (True => Finish, False => No_Flush); |
|
|
|
procedure Raise_Error (Stream : in Z_Stream); |
|
pragma Inline (Raise_Error); |
|
|
|
procedure Raise_Error (Message : in String); |
|
pragma Inline (Raise_Error); |
|
|
|
procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int); |
|
|
|
procedure Free is new Ada.Unchecked_Deallocation |
|
(Z_Stream, Z_Stream_Access); |
|
|
|
function To_Thin_Access is new Ada.Unchecked_Conversion |
|
(Z_Stream_Access, Thin.Z_Streamp); |
|
|
|
procedure Translate_GZip |
|
(Filter : in out Filter_Type; |
|
In_Data : in Ada.Streams.Stream_Element_Array; |
|
In_Last : out Ada.Streams.Stream_Element_Offset; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode); |
|
-- Separate translate routine for make gzip header. |
|
|
|
procedure Translate_Auto |
|
(Filter : in out Filter_Type; |
|
In_Data : in Ada.Streams.Stream_Element_Array; |
|
In_Last : out Ada.Streams.Stream_Element_Offset; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode); |
|
-- translate routine without additional headers. |
|
|
|
----------------- |
|
-- Check_Error -- |
|
----------------- |
|
|
|
procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int) is |
|
use type Thin.Int; |
|
begin |
|
if Code /= Thin.Z_OK then |
|
Raise_Error |
|
(Return_Code_Enum'Image (Return_Code (Code)) |
|
& ": " & Last_Error_Message (Stream)); |
|
end if; |
|
end Check_Error; |
|
|
|
----------- |
|
-- Close -- |
|
----------- |
|
|
|
procedure Close |
|
(Filter : in out Filter_Type; |
|
Ignore_Error : in Boolean := False) |
|
is |
|
Code : Thin.Int; |
|
begin |
|
if not Ignore_Error and then not Is_Open (Filter) then |
|
raise Status_Error; |
|
end if; |
|
|
|
Code := Flate (Filter.Compression).Done (To_Thin_Access (Filter.Strm)); |
|
|
|
if Ignore_Error or else Code = Thin.Z_OK then |
|
Free (Filter.Strm); |
|
else |
|
declare |
|
Error_Message : constant String |
|
:= Last_Error_Message (Filter.Strm.all); |
|
begin |
|
Free (Filter.Strm); |
|
Ada.Exceptions.Raise_Exception |
|
(ZLib_Error'Identity, |
|
Return_Code_Enum'Image (Return_Code (Code)) |
|
& ": " & Error_Message); |
|
end; |
|
end if; |
|
end Close; |
|
|
|
----------- |
|
-- CRC32 -- |
|
----------- |
|
|
|
function CRC32 |
|
(CRC : in Unsigned_32; |
|
Data : in Ada.Streams.Stream_Element_Array) |
|
return Unsigned_32 |
|
is |
|
use Thin; |
|
begin |
|
return Unsigned_32 (crc32 (ULong (CRC), |
|
Data'Address, |
|
Data'Length)); |
|
end CRC32; |
|
|
|
procedure CRC32 |
|
(CRC : in out Unsigned_32; |
|
Data : in Ada.Streams.Stream_Element_Array) is |
|
begin |
|
CRC := CRC32 (CRC, Data); |
|
end CRC32; |
|
|
|
------------------ |
|
-- Deflate_Init -- |
|
------------------ |
|
|
|
procedure Deflate_Init |
|
(Filter : in out Filter_Type; |
|
Level : in Compression_Level := Default_Compression; |
|
Strategy : in Strategy_Type := Default_Strategy; |
|
Method : in Compression_Method := Deflated; |
|
Window_Bits : in Window_Bits_Type := Default_Window_Bits; |
|
Memory_Level : in Memory_Level_Type := Default_Memory_Level; |
|
Header : in Header_Type := Default) |
|
is |
|
use type Thin.Int; |
|
Win_Bits : Thin.Int := Thin.Int (Window_Bits); |
|
begin |
|
if Is_Open (Filter) then |
|
raise Status_Error; |
|
end if; |
|
|
|
-- We allow ZLib to make header only in case of default header type. |
|
-- Otherwise we would either do header by ourselfs, or do not do |
|
-- header at all. |
|
|
|
if Header = None or else Header = GZip then |
|
Win_Bits := -Win_Bits; |
|
end if; |
|
|
|
-- For the GZip CRC calculation and make headers. |
|
|
|
if Header = GZip then |
|
Filter.CRC := 0; |
|
Filter.Offset := Simple_GZip_Header'First; |
|
else |
|
Filter.Offset := Simple_GZip_Header'Last + 1; |
|
end if; |
|
|
|
Filter.Strm := new Z_Stream; |
|
Filter.Compression := True; |
|
Filter.Stream_End := False; |
|
Filter.Header := Header; |
|
|
|
if Thin.Deflate_Init |
|
(To_Thin_Access (Filter.Strm), |
|
Level => Thin.Int (Level), |
|
method => Thin.Int (Method), |
|
windowBits => Win_Bits, |
|
memLevel => Thin.Int (Memory_Level), |
|
strategy => Thin.Int (Strategy)) /= Thin.Z_OK |
|
then |
|
Raise_Error (Filter.Strm.all); |
|
end if; |
|
end Deflate_Init; |
|
|
|
----------- |
|
-- Flush -- |
|
----------- |
|
|
|
procedure Flush |
|
(Filter : in out Filter_Type; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode) |
|
is |
|
No_Data : Stream_Element_Array := (1 .. 0 => 0); |
|
Last : Stream_Element_Offset; |
|
begin |
|
Translate (Filter, No_Data, Last, Out_Data, Out_Last, Flush); |
|
end Flush; |
|
|
|
----------------------- |
|
-- Generic_Translate -- |
|
----------------------- |
|
|
|
procedure Generic_Translate |
|
(Filter : in out ZLib.Filter_Type; |
|
In_Buffer_Size : in Integer := Default_Buffer_Size; |
|
Out_Buffer_Size : in Integer := Default_Buffer_Size) |
|
is |
|
In_Buffer : Stream_Element_Array |
|
(1 .. Stream_Element_Offset (In_Buffer_Size)); |
|
Out_Buffer : Stream_Element_Array |
|
(1 .. Stream_Element_Offset (Out_Buffer_Size)); |
|
Last : Stream_Element_Offset; |
|
In_Last : Stream_Element_Offset; |
|
In_First : Stream_Element_Offset; |
|
Out_Last : Stream_Element_Offset; |
|
begin |
|
Main : loop |
|
Data_In (In_Buffer, Last); |
|
|
|
In_First := In_Buffer'First; |
|
|
|
loop |
|
Translate |
|
(Filter => Filter, |
|
In_Data => In_Buffer (In_First .. Last), |
|
In_Last => In_Last, |
|
Out_Data => Out_Buffer, |
|
Out_Last => Out_Last, |
|
Flush => Flush_Finish (Last < In_Buffer'First)); |
|
|
|
if Out_Buffer'First <= Out_Last then |
|
Data_Out (Out_Buffer (Out_Buffer'First .. Out_Last)); |
|
end if; |
|
|
|
exit Main when Stream_End (Filter); |
|
|
|
-- The end of in buffer. |
|
|
|
exit when In_Last = Last; |
|
|
|
In_First := In_Last + 1; |
|
end loop; |
|
end loop Main; |
|
|
|
end Generic_Translate; |
|
|
|
------------------ |
|
-- Inflate_Init -- |
|
------------------ |
|
|
|
procedure Inflate_Init |
|
(Filter : in out Filter_Type; |
|
Window_Bits : in Window_Bits_Type := Default_Window_Bits; |
|
Header : in Header_Type := Default) |
|
is |
|
use type Thin.Int; |
|
Win_Bits : Thin.Int := Thin.Int (Window_Bits); |
|
|
|
procedure Check_Version; |
|
-- Check the latest header types compatibility. |
|
|
|
procedure Check_Version is |
|
begin |
|
if Version <= "1.1.4" then |
|
Raise_Error |
|
("Inflate header type " & Header_Type'Image (Header) |
|
& " incompatible with ZLib version " & Version); |
|
end if; |
|
end Check_Version; |
|
|
|
begin |
|
if Is_Open (Filter) then |
|
raise Status_Error; |
|
end if; |
|
|
|
case Header is |
|
when None => |
|
Check_Version; |
|
|
|
-- Inflate data without headers determined |
|
-- by negative Win_Bits. |
|
|
|
Win_Bits := -Win_Bits; |
|
when GZip => |
|
Check_Version; |
|
|
|
-- Inflate gzip data defined by flag 16. |
|
|
|
Win_Bits := Win_Bits + 16; |
|
when Auto => |
|
Check_Version; |
|
|
|
-- Inflate with automatic detection |
|
-- of gzip or native header defined by flag 32. |
|
|
|
Win_Bits := Win_Bits + 32; |
|
when Default => null; |
|
end case; |
|
|
|
Filter.Strm := new Z_Stream; |
|
Filter.Compression := False; |
|
Filter.Stream_End := False; |
|
Filter.Header := Header; |
|
|
|
if Thin.Inflate_Init |
|
(To_Thin_Access (Filter.Strm), Win_Bits) /= Thin.Z_OK |
|
then |
|
Raise_Error (Filter.Strm.all); |
|
end if; |
|
end Inflate_Init; |
|
|
|
------------- |
|
-- Is_Open -- |
|
------------- |
|
|
|
function Is_Open (Filter : in Filter_Type) return Boolean is |
|
begin |
|
return Filter.Strm /= null; |
|
end Is_Open; |
|
|
|
----------------- |
|
-- Raise_Error -- |
|
----------------- |
|
|
|
procedure Raise_Error (Message : in String) is |
|
begin |
|
Ada.Exceptions.Raise_Exception (ZLib_Error'Identity, Message); |
|
end Raise_Error; |
|
|
|
procedure Raise_Error (Stream : in Z_Stream) is |
|
begin |
|
Raise_Error (Last_Error_Message (Stream)); |
|
end Raise_Error; |
|
|
|
---------- |
|
-- Read -- |
|
---------- |
|
|
|
procedure Read |
|
(Filter : in out Filter_Type; |
|
Item : out Ada.Streams.Stream_Element_Array; |
|
Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode := No_Flush) |
|
is |
|
In_Last : Stream_Element_Offset; |
|
Item_First : Ada.Streams.Stream_Element_Offset := Item'First; |
|
V_Flush : Flush_Mode := Flush; |
|
|
|
begin |
|
pragma Assert (Rest_First in Buffer'First .. Buffer'Last + 1); |
|
pragma Assert (Rest_Last in Buffer'First - 1 .. Buffer'Last); |
|
|
|
loop |
|
if Rest_Last = Buffer'First - 1 then |
|
V_Flush := Finish; |
|
|
|
elsif Rest_First > Rest_Last then |
|
Read (Buffer, Rest_Last); |
|
Rest_First := Buffer'First; |
|
|
|
if Rest_Last < Buffer'First then |
|
V_Flush := Finish; |
|
end if; |
|
end if; |
|
|
|
Translate |
|
(Filter => Filter, |
|
In_Data => Buffer (Rest_First .. Rest_Last), |
|
In_Last => In_Last, |
|
Out_Data => Item (Item_First .. Item'Last), |
|
Out_Last => Last, |
|
Flush => V_Flush); |
|
|
|
Rest_First := In_Last + 1; |
|
|
|
exit when Stream_End (Filter) |
|
or else Last = Item'Last |
|
or else (Last >= Item'First and then Allow_Read_Some); |
|
|
|
Item_First := Last + 1; |
|
end loop; |
|
end Read; |
|
|
|
---------------- |
|
-- Stream_End -- |
|
---------------- |
|
|
|
function Stream_End (Filter : in Filter_Type) return Boolean is |
|
begin |
|
if Filter.Header = GZip and Filter.Compression then |
|
return Filter.Stream_End |
|
and then Filter.Offset = Footer_Array'Last + 1; |
|
else |
|
return Filter.Stream_End; |
|
end if; |
|
end Stream_End; |
|
|
|
-------------- |
|
-- Total_In -- |
|
-------------- |
|
|
|
function Total_In (Filter : in Filter_Type) return Count is |
|
begin |
|
return Count (Thin.Total_In (To_Thin_Access (Filter.Strm).all)); |
|
end Total_In; |
|
|
|
--------------- |
|
-- Total_Out -- |
|
--------------- |
|
|
|
function Total_Out (Filter : in Filter_Type) return Count is |
|
begin |
|
return Count (Thin.Total_Out (To_Thin_Access (Filter.Strm).all)); |
|
end Total_Out; |
|
|
|
--------------- |
|
-- Translate -- |
|
--------------- |
|
|
|
procedure Translate |
|
(Filter : in out Filter_Type; |
|
In_Data : in Ada.Streams.Stream_Element_Array; |
|
In_Last : out Ada.Streams.Stream_Element_Offset; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode) is |
|
begin |
|
if Filter.Header = GZip and then Filter.Compression then |
|
Translate_GZip |
|
(Filter => Filter, |
|
In_Data => In_Data, |
|
In_Last => In_Last, |
|
Out_Data => Out_Data, |
|
Out_Last => Out_Last, |
|
Flush => Flush); |
|
else |
|
Translate_Auto |
|
(Filter => Filter, |
|
In_Data => In_Data, |
|
In_Last => In_Last, |
|
Out_Data => Out_Data, |
|
Out_Last => Out_Last, |
|
Flush => Flush); |
|
end if; |
|
end Translate; |
|
|
|
-------------------- |
|
-- Translate_Auto -- |
|
-------------------- |
|
|
|
procedure Translate_Auto |
|
(Filter : in out Filter_Type; |
|
In_Data : in Ada.Streams.Stream_Element_Array; |
|
In_Last : out Ada.Streams.Stream_Element_Offset; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode) |
|
is |
|
use type Thin.Int; |
|
Code : Thin.Int; |
|
|
|
begin |
|
if not Is_Open (Filter) then |
|
raise Status_Error; |
|
end if; |
|
|
|
if Out_Data'Length = 0 and then In_Data'Length = 0 then |
|
raise Constraint_Error; |
|
end if; |
|
|
|
Set_Out (Filter.Strm.all, Out_Data'Address, Out_Data'Length); |
|
Set_In (Filter.Strm.all, In_Data'Address, In_Data'Length); |
|
|
|
Code := Flate (Filter.Compression).Step |
|
(To_Thin_Access (Filter.Strm), |
|
Thin.Int (Flush)); |
|
|
|
if Code = Thin.Z_STREAM_END then |
|
Filter.Stream_End := True; |
|
else |
|
Check_Error (Filter.Strm.all, Code); |
|
end if; |
|
|
|
In_Last := In_Data'Last |
|
- Stream_Element_Offset (Avail_In (Filter.Strm.all)); |
|
Out_Last := Out_Data'Last |
|
- Stream_Element_Offset (Avail_Out (Filter.Strm.all)); |
|
end Translate_Auto; |
|
|
|
-------------------- |
|
-- Translate_GZip -- |
|
-------------------- |
|
|
|
procedure Translate_GZip |
|
(Filter : in out Filter_Type; |
|
In_Data : in Ada.Streams.Stream_Element_Array; |
|
In_Last : out Ada.Streams.Stream_Element_Offset; |
|
Out_Data : out Ada.Streams.Stream_Element_Array; |
|
Out_Last : out Ada.Streams.Stream_Element_Offset; |
|
Flush : in Flush_Mode) |
|
is |
|
Out_First : Stream_Element_Offset; |
|
|
|
procedure Add_Data (Data : in Stream_Element_Array); |
|
-- Add data to stream from the Filter.Offset till necessary, |
|
-- used for add gzip headr/footer. |
|
|
|
procedure Put_32 |
|
(Item : in out Stream_Element_Array; |
|
Data : in Unsigned_32); |
|
pragma Inline (Put_32); |
|
|
|
-------------- |
|
-- Add_Data -- |
|
-------------- |
|
|
|
procedure Add_Data (Data : in Stream_Element_Array) is |
|
Data_First : Stream_Element_Offset renames Filter.Offset; |
|
Data_Last : Stream_Element_Offset; |
|
Data_Len : Stream_Element_Offset; -- -1 |
|
Out_Len : Stream_Element_Offset; -- -1 |
|
begin |
|
Out_First := Out_Last + 1; |
|
|
|
if Data_First > Data'Last then |
|
return; |
|
end if; |
|
|
|
Data_Len := Data'Last - Data_First; |
|
Out_Len := Out_Data'Last - Out_First; |
|
|
|
if Data_Len <= Out_Len then |
|
Out_Last := Out_First + Data_Len; |
|
Data_Last := Data'Last; |
|
else |
|
Out_Last := Out_Data'Last; |
|
Data_Last := Data_First + Out_Len; |
|
end if; |
|
|
|
Out_Data (Out_First .. Out_Last) := Data (Data_First .. Data_Last); |
|
|
|
Data_First := Data_Last + 1; |
|
Out_First := Out_Last + 1; |
|
end Add_Data; |
|
|
|
------------ |
|
-- Put_32 -- |
|
------------ |
|
|
|
procedure Put_32 |
|
(Item : in out Stream_Element_Array; |
|
Data : in Unsigned_32) |
|
is |
|
D : Unsigned_32 := Data; |
|
begin |
|
for J in Item'First .. Item'First + 3 loop |
|
Item (J) := Stream_Element (D and 16#FF#); |
|
D := Shift_Right (D, 8); |
|
end loop; |
|
end Put_32; |
|
|
|
begin |
|
Out_Last := Out_Data'First - 1; |
|
|
|
if not Filter.Stream_End then |
|
Add_Data (Simple_GZip_Header); |
|
|
|
Translate_Auto |
|
(Filter => Filter, |
|
In_Data => In_Data, |
|
In_Last => In_Last, |
|
Out_Data => Out_Data (Out_First .. Out_Data'Last), |
|
Out_Last => Out_Last, |
|
Flush => Flush); |
|
|
|
CRC32 (Filter.CRC, In_Data (In_Data'First .. In_Last)); |
|
end if; |
|
|
|
if Filter.Stream_End and then Out_Last <= Out_Data'Last then |
|
-- This detection method would work only when |
|
-- Simple_GZip_Header'Last > Footer_Array'Last |
|
|
|
if Filter.Offset = Simple_GZip_Header'Last + 1 then |
|
Filter.Offset := Footer_Array'First; |
|
end if; |
|
|
|
declare |
|
Footer : Footer_Array; |
|
begin |
|
Put_32 (Footer, Filter.CRC); |
|
Put_32 (Footer (Footer'First + 4 .. Footer'Last), |
|
Unsigned_32 (Total_In (Filter))); |
|
Add_Data (Footer); |
|
end; |
|
end if; |
|
end Translate_GZip; |
|
|
|
------------- |
|
-- Version -- |
|
------------- |
|
|
|
function Version return String is |
|
begin |
|
return Interfaces.C.Strings.Value (Thin.zlibVersion); |
|
end Version; |
|
|
|
----------- |
|
-- Write -- |
|
----------- |
|
|
|
procedure Write |
|
(Filter : in out Filter_Type; |
|
Item : in Ada.Streams.Stream_Element_Array; |
|
Flush : in Flush_Mode := No_Flush) |
|
is |
|
Buffer : Stream_Element_Array (1 .. Buffer_Size); |
|
In_Last : Stream_Element_Offset; |
|
Out_Last : Stream_Element_Offset; |
|
In_First : Stream_Element_Offset := Item'First; |
|
begin |
|
if Item'Length = 0 and Flush = No_Flush then |
|
return; |
|
end if; |
|
|
|
loop |
|
Translate |
|
(Filter => Filter, |
|
In_Data => Item (In_First .. Item'Last), |
|
In_Last => In_Last, |
|
Out_Data => Buffer, |
|
Out_Last => Out_Last, |
|
Flush => Flush); |
|
|
|
if Out_Last >= Buffer'First then |
|
Write (Buffer (1 .. Out_Last)); |
|
end if; |
|
|
|
exit when In_Last = Item'Last or Stream_End (Filter); |
|
|
|
In_First := In_Last + 1; |
|
end loop; |
|
end Write; |
|
|
|
end ZLib;
|
|
|