zstream.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. unit zstream;
  2. {**********************************************************************
  3. This file is part of the Free Pascal free component library.
  4. Copyright (c) 2007 by Daniel Mantione
  5. member of the Free Pascal development team
  6. Implements a Tstream descendents that allow you to read and write
  7. compressed data according to the Deflate algorithm described in
  8. RFC1951.
  9. See the file COPYING.FPC, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. **********************************************************************}
  15. {$mode objfpc}
  16. {***************************************************************************}
  17. interface
  18. {***************************************************************************}
  19. uses classes,zbase,gzio;
  20. type
  21. Tcompressionlevel=(
  22. clnone, {Do not use compression, just copy data.}
  23. clfastest, {Use fast (but less) compression.}
  24. cldefault, {Use default compression}
  25. clmax {Use maximum compression}
  26. );
  27. Tgzopenmode=(
  28. gzopenread, {Open file for reading.}
  29. gzopenwrite {Open file for writing.}
  30. );
  31. Tcustomzlibstream=class(Townerstream)
  32. protected
  33. Fstream:z_stream;
  34. Fbuffer:pointer;
  35. Fonprogress:Tnotifyevent;
  36. procedure progress(sender:Tobject);
  37. property onprogress:Tnotifyevent read Fonprogress write Fonprogress;
  38. public
  39. constructor create(stream:Tstream);
  40. destructor destroy;override;
  41. end;
  42. Tcompressionstream=class(Tcustomzlibstream)
  43. protected
  44. raw_written,compressed_written:longint;
  45. public
  46. constructor create(level:Tcompressionlevel;
  47. dest:Tstream;
  48. Askipheader:boolean=false);
  49. destructor destroy;override;
  50. function write(const buffer;count:longint):longint;override;
  51. procedure flush;
  52. function get_compressionrate:single;
  53. end;
  54. Tdecompressionstream=class(Tcustomzlibstream)
  55. protected
  56. raw_read,compressed_read:longint;
  57. skipheader:boolean;
  58. procedure reset;
  59. function GetPosition() : Int64; override;
  60. public
  61. constructor create(Asource:Tstream;Askipheader:boolean=false);
  62. destructor destroy;override;
  63. function read(var buffer;count:longint):longint;override;
  64. function seek(offset:longint;origin:word):longint;override;
  65. function get_compressionrate:single;
  66. end;
  67. TGZFileStream = Class(TStream)
  68. protected
  69. Fgzfile:gzfile;
  70. Ffilemode:Tgzopenmode;
  71. public
  72. constructor create(filename:ansistring;filemode:Tgzopenmode);
  73. function read(var buffer;count:longint):longint;override;
  74. function write(const buffer;count:longint):longint;override;
  75. function seek(offset:longint;origin:word):longint;override;
  76. destructor destroy;override;
  77. end;
  78. Ezliberror=class(Estreamerror)
  79. end;
  80. Egzfileerror=class(Ezliberror)
  81. end;
  82. Ecompressionerror=class(Ezliberror)
  83. end;
  84. Edecompressionerror=class(Ezliberror)
  85. end;
  86. {***************************************************************************}
  87. implementation
  88. {***************************************************************************}
  89. uses zdeflate,zinflate;
  90. const bufsize=16384; {Size of the buffer used for temporarily storing
  91. data from the child stream.}
  92. resourcestring Sgz_open_error='Could not open gzip compressed file %s.';
  93. Sgz_read_only='Gzip compressed file was opened for reading.';
  94. Sgz_write_only='Gzip compressed file was opened for writing.';
  95. Sseek_failed='Seek in deflate compressed stream failed.';
  96. constructor Tcustomzlibstream.create(stream:Tstream);
  97. begin
  98. assert(stream<>nil);
  99. inherited create(stream);
  100. getmem(Fbuffer,bufsize);
  101. end;
  102. procedure Tcustomzlibstream.progress(sender:Tobject);
  103. begin
  104. if Fonprogress<>nil then
  105. Fonprogress(sender);
  106. end;
  107. destructor Tcustomzlibstream.destroy;
  108. begin
  109. freemem(Fbuffer);
  110. inherited destroy;
  111. end;
  112. {***************************************************************************}
  113. constructor Tcompressionstream.create(level:Tcompressionlevel;
  114. dest:Tstream;
  115. Askipheader:boolean=false);
  116. var err,l:smallint;
  117. begin
  118. inherited create(dest);
  119. Fstream.next_out:=Fbuffer;
  120. Fstream.avail_out:=bufsize;
  121. case level of
  122. clnone:
  123. l:=Z_NO_COMPRESSION;
  124. clfastest:
  125. l:=Z_BEST_SPEED;
  126. cldefault:
  127. l:=Z_DEFAULT_COMPRESSION;
  128. clmax:
  129. l:=Z_BEST_COMPRESSION;
  130. end;
  131. if Askipheader then
  132. err:=deflateInit2(Fstream,l,Z_DEFLATED,-MAX_WBITS,DEF_MEM_LEVEL,0)
  133. else
  134. err:=deflateInit(Fstream,l);
  135. if err<>Z_OK then
  136. raise Ecompressionerror.create(zerror(err));
  137. end;
  138. function Tcompressionstream.write(const buffer;count:longint):longint;
  139. var err:smallint;
  140. lastavail,
  141. written:longint;
  142. begin
  143. Fstream.next_in:=@buffer;
  144. Fstream.avail_in:=count;
  145. lastavail:=count;
  146. while Fstream.avail_in<>0 do
  147. begin
  148. if Fstream.avail_out=0 then
  149. begin
  150. { Flush the buffer to the stream and update progress }
  151. written:=source.write(Fbuffer^,bufsize);
  152. inc(compressed_written,written);
  153. inc(raw_written,lastavail-Fstream.avail_in);
  154. lastavail:=Fstream.avail_in;
  155. progress(self);
  156. { reset output buffer }
  157. Fstream.next_out:=Fbuffer;
  158. Fstream.avail_out:=bufsize;
  159. end;
  160. err:=deflate(Fstream,Z_NO_FLUSH);
  161. if err<>Z_OK then
  162. raise Ecompressionerror.create(zerror(err));
  163. end;
  164. inc(raw_written,lastavail-Fstream.avail_in);
  165. write:=count;
  166. end;
  167. function Tcompressionstream.get_compressionrate:single;
  168. begin
  169. get_compressionrate:=100*compressed_written/raw_written;
  170. end;
  171. procedure Tcompressionstream.flush;
  172. var err:smallint;
  173. written:longint;
  174. begin
  175. {Compress remaining data still in internal zlib data buffers.}
  176. repeat
  177. if Fstream.avail_out=0 then
  178. begin
  179. { Flush the buffer to the stream and update progress }
  180. written:=source.write(Fbuffer^,bufsize);
  181. inc(compressed_written,written);
  182. progress(self);
  183. { reset output buffer }
  184. Fstream.next_out:=Fbuffer;
  185. Fstream.avail_out:=bufsize;
  186. end;
  187. err:=deflate(Fstream,Z_FINISH);
  188. if err=Z_STREAM_END then
  189. break;
  190. if (err<>Z_OK) then
  191. raise Ecompressionerror.create(zerror(err));
  192. until false;
  193. if Fstream.avail_out<bufsize then
  194. begin
  195. source.writebuffer(FBuffer^,bufsize-Fstream.avail_out);
  196. inc(compressed_written,bufsize-Fstream.avail_out);
  197. progress(self);
  198. end;
  199. end;
  200. destructor Tcompressionstream.destroy;
  201. begin
  202. try
  203. Flush;
  204. finally
  205. deflateEnd(Fstream);
  206. inherited destroy;
  207. end;
  208. end;
  209. {***************************************************************************}
  210. constructor Tdecompressionstream.create(Asource:Tstream;Askipheader:boolean=false);
  211. var err:smallint;
  212. begin
  213. inherited create(Asource);
  214. skipheader:=Askipheader;
  215. if Askipheader then
  216. err:=inflateInit2(Fstream,-MAX_WBITS)
  217. else
  218. err:=inflateInit(Fstream);
  219. if err<>Z_OK then
  220. raise Ecompressionerror.create(zerror(err));
  221. end;
  222. function Tdecompressionstream.read(var buffer;count:longint):longint;
  223. var err:smallint;
  224. lastavail:longint;
  225. begin
  226. Fstream.next_out:=@buffer;
  227. Fstream.avail_out:=count;
  228. lastavail:=count;
  229. while Fstream.avail_out<>0 do
  230. begin
  231. if Fstream.avail_in=0 then
  232. begin
  233. {Refill the buffer.}
  234. Fstream.next_in:=Fbuffer;
  235. Fstream.avail_in:=source.read(Fbuffer^,bufsize);
  236. inc(compressed_read,Fstream.avail_in);
  237. inc(raw_read,lastavail-Fstream.avail_out);
  238. lastavail:=Fstream.avail_out;
  239. progress(self);
  240. end;
  241. err:=inflate(Fstream,Z_NO_FLUSH);
  242. if err=Z_STREAM_END then
  243. break;
  244. if err<>Z_OK then
  245. raise Edecompressionerror.create(zerror(err));
  246. end;
  247. if err=Z_STREAM_END then
  248. dec(compressed_read,Fstream.avail_in);
  249. inc(raw_read,lastavail-Fstream.avail_out);
  250. read:=count-Fstream.avail_out;
  251. end;
  252. procedure Tdecompressionstream.reset;
  253. var err:smallint;
  254. begin
  255. source.seek(-compressed_read,sofromcurrent);
  256. raw_read:=0;
  257. compressed_read:=0;
  258. inflateEnd(Fstream);
  259. if skipheader then
  260. err:=inflateInit2(Fstream,-MAX_WBITS)
  261. else
  262. err:=inflateInit(Fstream);
  263. if err<>Z_OK then
  264. raise Edecompressionerror.create(zerror(err));
  265. end;
  266. function Tdecompressionstream.GetPosition() : Int64;
  267. begin
  268. GetPosition := raw_read;
  269. end;
  270. function Tdecompressionstream.seek(offset:longint;origin:word):longint;
  271. var c:longint;
  272. begin
  273. if (origin=sofrombeginning) or
  274. ((origin=sofromcurrent) and (offset+raw_read>=0)) then
  275. begin
  276. if origin = sofromcurrent then
  277. seek := raw_read + offset
  278. else
  279. seek := offset;
  280. if origin=sofrombeginning then
  281. dec(offset,raw_read);
  282. if offset<0 then
  283. begin
  284. inc(offset,raw_read);
  285. reset;
  286. end;
  287. while offset>0 do
  288. begin
  289. c:=offset;
  290. if c>bufsize then
  291. c:=bufsize;
  292. c:=read(Fbuffer^,c);
  293. dec(offset,c);
  294. end;
  295. end
  296. else
  297. raise Edecompressionerror.create(Sseek_failed);
  298. end;
  299. function Tdecompressionstream.get_compressionrate:single;
  300. begin
  301. get_compressionrate:=100*compressed_read/raw_read;
  302. end;
  303. destructor Tdecompressionstream.destroy;
  304. begin
  305. inflateEnd(Fstream);
  306. inherited destroy;
  307. end;
  308. {***************************************************************************}
  309. constructor Tgzfilestream.create(filename:ansistring;filemode:Tgzopenmode);
  310. begin
  311. if filemode=gzopenread then
  312. Fgzfile:=gzopen(filename,'rb')
  313. else
  314. Fgzfile:=gzopen(filename,'wb');
  315. Ffilemode:=filemode;
  316. if Fgzfile=nil then
  317. raise Egzfileerror.createfmt(Sgz_open_error,[filename]);
  318. end;
  319. function Tgzfilestream.read(var buffer;count:longint):longint;
  320. begin
  321. if Ffilemode=gzopenwrite then
  322. raise Egzfileerror.create(Sgz_write_only);
  323. read:=gzread(Fgzfile,@buffer,count);
  324. end;
  325. function Tgzfilestream.write(const buffer;count:longint):longint;
  326. begin
  327. if Ffilemode=gzopenread then
  328. raise Egzfileerror.create(Sgz_write_only);
  329. write:=gzwrite(Fgzfile,@buffer,count);
  330. end;
  331. function Tgzfilestream.seek(offset:longint;origin:word):longint;
  332. begin
  333. seek:=gzseek(Fgzfile,offset,origin);
  334. if seek=-1 then
  335. raise egzfileerror.create(Sseek_failed);
  336. end;
  337. destructor Tgzfilestream.destroy;
  338. begin
  339. gzclose(Fgzfile);
  340. inherited destroy;
  341. end;
  342. end.