zlib.adb
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:20k
- ----------------------------------------------------------------
- -- 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;