zlib.adb 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. ----------------------------------------------------------------
  2. -- ZLib for Ada thick binding. --
  3. -- --
  4. -- Copyright (C) 2002-2004 Dmitriy Anisimkov --
  5. -- --
  6. -- Open source license information is in the zlib.ads file. --
  7. ----------------------------------------------------------------
  8. -- $Id: zlib.adb,v 1.31 2004/09/06 06:53:19 vagul Exp $
  9. with Ada.Exceptions;
  10. with Ada.Unchecked_Conversion;
  11. with Ada.Unchecked_Deallocation;
  12. with Interfaces.C.Strings;
  13. with ZLib.Thin;
  14. package body ZLib is
  15. use type Thin.Int;
  16. type Z_Stream is new Thin.Z_Stream;
  17. type Return_Code_Enum is
  18. (OK,
  19. STREAM_END,
  20. NEED_DICT,
  21. ERRNO,
  22. STREAM_ERROR,
  23. DATA_ERROR,
  24. MEM_ERROR,
  25. BUF_ERROR,
  26. VERSION_ERROR);
  27. type Flate_Step_Function is access
  28. function (Strm : in Thin.Z_Streamp; Flush : in Thin.Int) return Thin.Int;
  29. pragma Convention (C, Flate_Step_Function);
  30. type Flate_End_Function is access
  31. function (Ctrm : in Thin.Z_Streamp) return Thin.Int;
  32. pragma Convention (C, Flate_End_Function);
  33. type Flate_Type is record
  34. Step : Flate_Step_Function;
  35. Done : Flate_End_Function;
  36. end record;
  37. subtype Footer_Array is Stream_Element_Array (1 .. 8);
  38. Simple_GZip_Header : constant Stream_Element_Array (1 .. 10)
  39. := (16#1f#, 16#8b#, -- Magic header
  40. 16#08#, -- Z_DEFLATED
  41. 16#00#, -- Flags
  42. 16#00#, 16#00#, 16#00#, 16#00#, -- Time
  43. 16#00#, -- XFlags
  44. 16#03# -- OS code
  45. );
  46. -- The simplest gzip header is not for informational, but just for
  47. -- gzip format compatibility.
  48. -- Note that some code below is using assumption
  49. -- Simple_GZip_Header'Last > Footer_Array'Last, so do not make
  50. -- Simple_GZip_Header'Last <= Footer_Array'Last.
  51. Return_Code : constant array (Thin.Int range <>) of Return_Code_Enum
  52. := (0 => OK,
  53. 1 => STREAM_END,
  54. 2 => NEED_DICT,
  55. -1 => ERRNO,
  56. -2 => STREAM_ERROR,
  57. -3 => DATA_ERROR,
  58. -4 => MEM_ERROR,
  59. -5 => BUF_ERROR,
  60. -6 => VERSION_ERROR);
  61. Flate : constant array (Boolean) of Flate_Type
  62. := (True => (Step => Thin.Deflate'Access,
  63. Done => Thin.DeflateEnd'Access),
  64. False => (Step => Thin.Inflate'Access,
  65. Done => Thin.InflateEnd'Access));
  66. Flush_Finish : constant array (Boolean) of Flush_Mode
  67. := (True => Finish, False => No_Flush);
  68. procedure Raise_Error (Stream : in Z_Stream);
  69. pragma Inline (Raise_Error);
  70. procedure Raise_Error (Message : in String);
  71. pragma Inline (Raise_Error);
  72. procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int);
  73. procedure Free is new Ada.Unchecked_Deallocation
  74. (Z_Stream, Z_Stream_Access);
  75. function To_Thin_Access is new Ada.Unchecked_Conversion
  76. (Z_Stream_Access, Thin.Z_Streamp);
  77. procedure Translate_GZip
  78. (Filter : in out Filter_Type;
  79. In_Data : in Ada.Streams.Stream_Element_Array;
  80. In_Last : out Ada.Streams.Stream_Element_Offset;
  81. Out_Data : out Ada.Streams.Stream_Element_Array;
  82. Out_Last : out Ada.Streams.Stream_Element_Offset;
  83. Flush : in Flush_Mode);
  84. -- Separate translate routine for make gzip header.
  85. procedure Translate_Auto
  86. (Filter : in out Filter_Type;
  87. In_Data : in Ada.Streams.Stream_Element_Array;
  88. In_Last : out Ada.Streams.Stream_Element_Offset;
  89. Out_Data : out Ada.Streams.Stream_Element_Array;
  90. Out_Last : out Ada.Streams.Stream_Element_Offset;
  91. Flush : in Flush_Mode);
  92. -- translate routine without additional headers.
  93. -----------------
  94. -- Check_Error --
  95. -----------------
  96. procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int) is
  97. use type Thin.Int;
  98. begin
  99. if Code /= Thin.Z_OK then
  100. Raise_Error
  101. (Return_Code_Enum'Image (Return_Code (Code))
  102. & ": " & Last_Error_Message (Stream));
  103. end if;
  104. end Check_Error;
  105. -----------
  106. -- Close --
  107. -----------
  108. procedure Close
  109. (Filter : in out Filter_Type;
  110. Ignore_Error : in Boolean := False)
  111. is
  112. Code : Thin.Int;
  113. begin
  114. if not Ignore_Error and then not Is_Open (Filter) then
  115. raise Status_Error;
  116. end if;
  117. Code := Flate (Filter.Compression).Done (To_Thin_Access (Filter.Strm));
  118. if Ignore_Error or else Code = Thin.Z_OK then
  119. Free (Filter.Strm);
  120. else
  121. declare
  122. Error_Message : constant String
  123. := Last_Error_Message (Filter.Strm.all);
  124. begin
  125. Free (Filter.Strm);
  126. Ada.Exceptions.Raise_Exception
  127. (ZLib_Error'Identity,
  128. Return_Code_Enum'Image (Return_Code (Code))
  129. & ": " & Error_Message);
  130. end;
  131. end if;
  132. end Close;
  133. -----------
  134. -- CRC32 --
  135. -----------
  136. function CRC32
  137. (CRC : in Unsigned_32;
  138. Data : in Ada.Streams.Stream_Element_Array)
  139. return Unsigned_32
  140. is
  141. use Thin;
  142. begin
  143. return Unsigned_32 (crc32 (ULong (CRC),
  144. Data'Address,
  145. Data'Length));
  146. end CRC32;
  147. procedure CRC32
  148. (CRC : in out Unsigned_32;
  149. Data : in Ada.Streams.Stream_Element_Array) is
  150. begin
  151. CRC := CRC32 (CRC, Data);
  152. end CRC32;
  153. ------------------
  154. -- Deflate_Init --
  155. ------------------
  156. procedure Deflate_Init
  157. (Filter : in out Filter_Type;
  158. Level : in Compression_Level := Default_Compression;
  159. Strategy : in Strategy_Type := Default_Strategy;
  160. Method : in Compression_Method := Deflated;
  161. Window_Bits : in Window_Bits_Type := Default_Window_Bits;
  162. Memory_Level : in Memory_Level_Type := Default_Memory_Level;
  163. Header : in Header_Type := Default)
  164. is
  165. use type Thin.Int;
  166. Win_Bits : Thin.Int := Thin.Int (Window_Bits);
  167. begin
  168. if Is_Open (Filter) then
  169. raise Status_Error;
  170. end if;
  171. -- We allow ZLib to make header only in case of default header type.
  172. -- Otherwise we would either do header by ourselfs, or do not do
  173. -- header at all.
  174. if Header = None or else Header = GZip then
  175. Win_Bits := -Win_Bits;
  176. end if;
  177. -- For the GZip CRC calculation and make headers.
  178. if Header = GZip then
  179. Filter.CRC := 0;
  180. Filter.Offset := Simple_GZip_Header'First;
  181. else
  182. Filter.Offset := Simple_GZip_Header'Last + 1;
  183. end if;
  184. Filter.Strm := new Z_Stream;
  185. Filter.Compression := True;
  186. Filter.Stream_End := False;
  187. Filter.Header := Header;
  188. if Thin.Deflate_Init
  189. (To_Thin_Access (Filter.Strm),
  190. Level => Thin.Int (Level),
  191. method => Thin.Int (Method),
  192. windowBits => Win_Bits,
  193. memLevel => Thin.Int (Memory_Level),
  194. strategy => Thin.Int (Strategy)) /= Thin.Z_OK
  195. then
  196. Raise_Error (Filter.Strm.all);
  197. end if;
  198. end Deflate_Init;
  199. -----------
  200. -- Flush --
  201. -----------
  202. procedure Flush
  203. (Filter : in out Filter_Type;
  204. Out_Data : out Ada.Streams.Stream_Element_Array;
  205. Out_Last : out Ada.Streams.Stream_Element_Offset;
  206. Flush : in Flush_Mode)
  207. is
  208. No_Data : Stream_Element_Array := (1 .. 0 => 0);
  209. Last : Stream_Element_Offset;
  210. begin
  211. Translate (Filter, No_Data, Last, Out_Data, Out_Last, Flush);
  212. end Flush;
  213. -----------------------
  214. -- Generic_Translate --
  215. -----------------------
  216. procedure Generic_Translate
  217. (Filter : in out ZLib.Filter_Type;
  218. In_Buffer_Size : in Integer := Default_Buffer_Size;
  219. Out_Buffer_Size : in Integer := Default_Buffer_Size)
  220. is
  221. In_Buffer : Stream_Element_Array
  222. (1 .. Stream_Element_Offset (In_Buffer_Size));
  223. Out_Buffer : Stream_Element_Array
  224. (1 .. Stream_Element_Offset (Out_Buffer_Size));
  225. Last : Stream_Element_Offset;
  226. In_Last : Stream_Element_Offset;
  227. In_First : Stream_Element_Offset;
  228. Out_Last : Stream_Element_Offset;
  229. begin
  230. Main : loop
  231. Data_In (In_Buffer, Last);
  232. In_First := In_Buffer'First;
  233. loop
  234. Translate
  235. (Filter => Filter,
  236. In_Data => In_Buffer (In_First .. Last),
  237. In_Last => In_Last,
  238. Out_Data => Out_Buffer,
  239. Out_Last => Out_Last,
  240. Flush => Flush_Finish (Last < In_Buffer'First));
  241. if Out_Buffer'First <= Out_Last then
  242. Data_Out (Out_Buffer (Out_Buffer'First .. Out_Last));
  243. end if;
  244. exit Main when Stream_End (Filter);
  245. -- The end of in buffer.
  246. exit when In_Last = Last;
  247. In_First := In_Last + 1;
  248. end loop;
  249. end loop Main;
  250. end Generic_Translate;
  251. ------------------
  252. -- Inflate_Init --
  253. ------------------
  254. procedure Inflate_Init
  255. (Filter : in out Filter_Type;
  256. Window_Bits : in Window_Bits_Type := Default_Window_Bits;
  257. Header : in Header_Type := Default)
  258. is
  259. use type Thin.Int;
  260. Win_Bits : Thin.Int := Thin.Int (Window_Bits);
  261. procedure Check_Version;
  262. -- Check the latest header types compatibility.
  263. procedure Check_Version is
  264. begin
  265. if Version <= "1.1.4" then
  266. Raise_Error
  267. ("Inflate header type " & Header_Type'Image (Header)
  268. & " incompatible with ZLib version " & Version);
  269. end if;
  270. end Check_Version;
  271. begin
  272. if Is_Open (Filter) then
  273. raise Status_Error;
  274. end if;
  275. case Header is
  276. when None =>
  277. Check_Version;
  278. -- Inflate data without headers determined
  279. -- by negative Win_Bits.
  280. Win_Bits := -Win_Bits;
  281. when GZip =>
  282. Check_Version;
  283. -- Inflate gzip data defined by flag 16.
  284. Win_Bits := Win_Bits + 16;
  285. when Auto =>
  286. Check_Version;
  287. -- Inflate with automatic detection
  288. -- of gzip or native header defined by flag 32.
  289. Win_Bits := Win_Bits + 32;
  290. when Default => null;
  291. end case;
  292. Filter.Strm := new Z_Stream;
  293. Filter.Compression := False;
  294. Filter.Stream_End := False;
  295. Filter.Header := Header;
  296. if Thin.Inflate_Init
  297. (To_Thin_Access (Filter.Strm), Win_Bits) /= Thin.Z_OK
  298. then
  299. Raise_Error (Filter.Strm.all);
  300. end if;
  301. end Inflate_Init;
  302. -------------
  303. -- Is_Open --
  304. -------------
  305. function Is_Open (Filter : in Filter_Type) return Boolean is
  306. begin
  307. return Filter.Strm /= null;
  308. end Is_Open;
  309. -----------------
  310. -- Raise_Error --
  311. -----------------
  312. procedure Raise_Error (Message : in String) is
  313. begin
  314. Ada.Exceptions.Raise_Exception (ZLib_Error'Identity, Message);
  315. end Raise_Error;
  316. procedure Raise_Error (Stream : in Z_Stream) is
  317. begin
  318. Raise_Error (Last_Error_Message (Stream));
  319. end Raise_Error;
  320. ----------
  321. -- Read --
  322. ----------
  323. procedure Read
  324. (Filter : in out Filter_Type;
  325. Item : out Ada.Streams.Stream_Element_Array;
  326. Last : out Ada.Streams.Stream_Element_Offset;
  327. Flush : in Flush_Mode := No_Flush)
  328. is
  329. In_Last : Stream_Element_Offset;
  330. Item_First : Ada.Streams.Stream_Element_Offset := Item'First;
  331. V_Flush : Flush_Mode := Flush;
  332. begin
  333. pragma Assert (Rest_First in Buffer'First .. Buffer'Last + 1);
  334. pragma Assert (Rest_Last in Buffer'First - 1 .. Buffer'Last);
  335. loop
  336. if Rest_Last = Buffer'First - 1 then
  337. V_Flush := Finish;
  338. elsif Rest_First > Rest_Last then
  339. Read (Buffer, Rest_Last);
  340. Rest_First := Buffer'First;
  341. if Rest_Last < Buffer'First then
  342. V_Flush := Finish;
  343. end if;
  344. end if;
  345. Translate
  346. (Filter => Filter,
  347. In_Data => Buffer (Rest_First .. Rest_Last),
  348. In_Last => In_Last,
  349. Out_Data => Item (Item_First .. Item'Last),
  350. Out_Last => Last,
  351. Flush => V_Flush);
  352. Rest_First := In_Last + 1;
  353. exit when Stream_End (Filter)
  354. or else Last = Item'Last
  355. or else (Last >= Item'First and then Allow_Read_Some);
  356. Item_First := Last + 1;
  357. end loop;
  358. end Read;
  359. ----------------
  360. -- Stream_End --
  361. ----------------
  362. function Stream_End (Filter : in Filter_Type) return Boolean is
  363. begin
  364. if Filter.Header = GZip and Filter.Compression then
  365. return Filter.Stream_End
  366. and then Filter.Offset = Footer_Array'Last + 1;
  367. else
  368. return Filter.Stream_End;
  369. end if;
  370. end Stream_End;
  371. --------------
  372. -- Total_In --
  373. --------------
  374. function Total_In (Filter : in Filter_Type) return Count is
  375. begin
  376. return Count (Thin.Total_In (To_Thin_Access (Filter.Strm).all));
  377. end Total_In;
  378. ---------------
  379. -- Total_Out --
  380. ---------------
  381. function Total_Out (Filter : in Filter_Type) return Count is
  382. begin
  383. return Count (Thin.Total_Out (To_Thin_Access (Filter.Strm).all));
  384. end Total_Out;
  385. ---------------
  386. -- Translate --
  387. ---------------
  388. procedure Translate
  389. (Filter : in out Filter_Type;
  390. In_Data : in Ada.Streams.Stream_Element_Array;
  391. In_Last : out Ada.Streams.Stream_Element_Offset;
  392. Out_Data : out Ada.Streams.Stream_Element_Array;
  393. Out_Last : out Ada.Streams.Stream_Element_Offset;
  394. Flush : in Flush_Mode) is
  395. begin
  396. if Filter.Header = GZip and then Filter.Compression then
  397. Translate_GZip
  398. (Filter => Filter,
  399. In_Data => In_Data,
  400. In_Last => In_Last,
  401. Out_Data => Out_Data,
  402. Out_Last => Out_Last,
  403. Flush => Flush);
  404. else
  405. Translate_Auto
  406. (Filter => Filter,
  407. In_Data => In_Data,
  408. In_Last => In_Last,
  409. Out_Data => Out_Data,
  410. Out_Last => Out_Last,
  411. Flush => Flush);
  412. end if;
  413. end Translate;
  414. --------------------
  415. -- Translate_Auto --
  416. --------------------
  417. procedure Translate_Auto
  418. (Filter : in out Filter_Type;
  419. In_Data : in Ada.Streams.Stream_Element_Array;
  420. In_Last : out Ada.Streams.Stream_Element_Offset;
  421. Out_Data : out Ada.Streams.Stream_Element_Array;
  422. Out_Last : out Ada.Streams.Stream_Element_Offset;
  423. Flush : in Flush_Mode)
  424. is
  425. use type Thin.Int;
  426. Code : Thin.Int;
  427. begin
  428. if not Is_Open (Filter) then
  429. raise Status_Error;
  430. end if;
  431. if Out_Data'Length = 0 and then In_Data'Length = 0 then
  432. raise Constraint_Error;
  433. end if;
  434. Set_Out (Filter.Strm.all, Out_Data'Address, Out_Data'Length);
  435. Set_In (Filter.Strm.all, In_Data'Address, In_Data'Length);
  436. Code := Flate (Filter.Compression).Step
  437. (To_Thin_Access (Filter.Strm),
  438. Thin.Int (Flush));
  439. if Code = Thin.Z_STREAM_END then
  440. Filter.Stream_End := True;
  441. else
  442. Check_Error (Filter.Strm.all, Code);
  443. end if;
  444. In_Last := In_Data'Last
  445. - Stream_Element_Offset (Avail_In (Filter.Strm.all));
  446. Out_Last := Out_Data'Last
  447. - Stream_Element_Offset (Avail_Out (Filter.Strm.all));
  448. end Translate_Auto;
  449. --------------------
  450. -- Translate_GZip --
  451. --------------------
  452. procedure Translate_GZip
  453. (Filter : in out Filter_Type;
  454. In_Data : in Ada.Streams.Stream_Element_Array;
  455. In_Last : out Ada.Streams.Stream_Element_Offset;
  456. Out_Data : out Ada.Streams.Stream_Element_Array;
  457. Out_Last : out Ada.Streams.Stream_Element_Offset;
  458. Flush : in Flush_Mode)
  459. is
  460. Out_First : Stream_Element_Offset;
  461. procedure Add_Data (Data : in Stream_Element_Array);
  462. -- Add data to stream from the Filter.Offset till necessary,
  463. -- used for add gzip headr/footer.
  464. procedure Put_32
  465. (Item : in out Stream_Element_Array;
  466. Data : in Unsigned_32);
  467. pragma Inline (Put_32);
  468. --------------
  469. -- Add_Data --
  470. --------------
  471. procedure Add_Data (Data : in Stream_Element_Array) is
  472. Data_First : Stream_Element_Offset renames Filter.Offset;
  473. Data_Last : Stream_Element_Offset;
  474. Data_Len : Stream_Element_Offset; -- -1
  475. Out_Len : Stream_Element_Offset; -- -1
  476. begin
  477. Out_First := Out_Last + 1;
  478. if Data_First > Data'Last then
  479. return;
  480. end if;
  481. Data_Len := Data'Last - Data_First;
  482. Out_Len := Out_Data'Last - Out_First;
  483. if Data_Len <= Out_Len then
  484. Out_Last := Out_First + Data_Len;
  485. Data_Last := Data'Last;
  486. else
  487. Out_Last := Out_Data'Last;
  488. Data_Last := Data_First + Out_Len;
  489. end if;
  490. Out_Data (Out_First .. Out_Last) := Data (Data_First .. Data_Last);
  491. Data_First := Data_Last + 1;
  492. Out_First := Out_Last + 1;
  493. end Add_Data;
  494. ------------
  495. -- Put_32 --
  496. ------------
  497. procedure Put_32
  498. (Item : in out Stream_Element_Array;
  499. Data : in Unsigned_32)
  500. is
  501. D : Unsigned_32 := Data;
  502. begin
  503. for J in Item'First .. Item'First + 3 loop
  504. Item (J) := Stream_Element (D and 16#FF#);
  505. D := Shift_Right (D, 8);
  506. end loop;
  507. end Put_32;
  508. begin
  509. Out_Last := Out_Data'First - 1;
  510. if not Filter.Stream_End then
  511. Add_Data (Simple_GZip_Header);
  512. Translate_Auto
  513. (Filter => Filter,
  514. In_Data => In_Data,
  515. In_Last => In_Last,
  516. Out_Data => Out_Data (Out_First .. Out_Data'Last),
  517. Out_Last => Out_Last,
  518. Flush => Flush);
  519. CRC32 (Filter.CRC, In_Data (In_Data'First .. In_Last));
  520. end if;
  521. if Filter.Stream_End and then Out_Last <= Out_Data'Last then
  522. -- This detection method would work only when
  523. -- Simple_GZip_Header'Last > Footer_Array'Last
  524. if Filter.Offset = Simple_GZip_Header'Last + 1 then
  525. Filter.Offset := Footer_Array'First;
  526. end if;
  527. declare
  528. Footer : Footer_Array;
  529. begin
  530. Put_32 (Footer, Filter.CRC);
  531. Put_32 (Footer (Footer'First + 4 .. Footer'Last),
  532. Unsigned_32 (Total_In (Filter)));
  533. Add_Data (Footer);
  534. end;
  535. end if;
  536. end Translate_GZip;
  537. -------------
  538. -- Version --
  539. -------------
  540. function Version return String is
  541. begin
  542. return Interfaces.C.Strings.Value (Thin.zlibVersion);
  543. end Version;
  544. -----------
  545. -- Write --
  546. -----------
  547. procedure Write
  548. (Filter : in out Filter_Type;
  549. Item : in Ada.Streams.Stream_Element_Array;
  550. Flush : in Flush_Mode := No_Flush)
  551. is
  552. Buffer : Stream_Element_Array (1 .. Buffer_Size);
  553. In_Last : Stream_Element_Offset;
  554. Out_Last : Stream_Element_Offset;
  555. In_First : Stream_Element_Offset := Item'First;
  556. begin
  557. if Item'Length = 0 and Flush = No_Flush then
  558. return;
  559. end if;
  560. loop
  561. Translate
  562. (Filter => Filter,
  563. In_Data => Item (In_First .. Item'Last),
  564. In_Last => In_Last,
  565. Out_Data => Buffer,
  566. Out_Last => Out_Last,
  567. Flush => Flush);
  568. if Out_Last >= Buffer'First then
  569. Write (Buffer (1 .. Out_Last));
  570. end if;
  571. exit when In_Last = Item'Last or Stream_End (Filter);
  572. In_First := In_Last + 1;
  573. end loop;
  574. end Write;
  575. end ZLib;