paslznonslide.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. { Renewed copyright, with permission of the author:
  2. Copyright (C) 2002 Matthew T. Russotto
  3. This library is free software; you can redistribute it and/or modify it
  4. under the terms of the GNU Library General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at your
  6. option) any later version.
  7. This program is distributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  10. for more details.
  11. You should have received a copy of the GNU Library General Public License
  12. along with this library; if not, write to the Free Software Foundation,
  13. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. }
  15. {
  16. See the file COPYING.FPC, included in this distribution,
  17. for details about the copyright.
  18. }
  19. unit paslznonslide;
  20. {$MODE OBJFPC}
  21. interface
  22. {$IFDEF FPC}
  23. {$PACKRECORDS C}
  24. {$ENDIF}
  25. {$DEFINE LAZY}
  26. {$DEFINE DEBUG_LZ}
  27. type
  28. u_char = Byte;
  29. Pu_char = ^u_char;
  30. PPu_char = ^Pu_char;
  31. Plz_info = ^lz_info;
  32. get_chars_t = function (lzi:plz_info; n:longint; buf:pu_char):longint; cdecl;
  33. output_match_t = function (lzi:plz_info; match_pos:longint; match_len:longint):longint; cdecl;
  34. output_literal_t = procedure (lzi:plz_info; ch:u_char); cdecl;
  35. { window size in bytes }
  36. { size of longest match in bytes }
  37. { location within stream }
  38. lz_info = record
  39. wsize : longint;
  40. max_match : longint;
  41. min_match : longint;
  42. block_buf : pu_char;
  43. block_bufe : pu_char;
  44. block_buf_size : longint;
  45. chars_in_buf : longint;
  46. cur_loc : longint;
  47. block_loc : longint;
  48. frame_size : longint;
  49. max_dist : longint;
  50. prevtab : ppu_char;
  51. lentab : plongint;
  52. eofcount : smallint;
  53. stop : smallint;
  54. analysis_valid : smallint;
  55. get_chars : get_chars_t;
  56. output_match : output_match_t;
  57. output_literal : output_literal_t;
  58. user_data : pointer;
  59. end;
  60. procedure lz_init(lzi:plz_info; wsize:longint; max_dist:longint; max_match:longint; min_match:longint;
  61. frame_size:longint; get_chars:get_chars_t; output_match:output_match_t; output_literal:output_literal_t; user_data:pointer);
  62. procedure lz_release(lzi:plz_info);
  63. procedure lz_reset(lzi:plz_info);
  64. procedure lz_stop_compressing(lzi:plz_info);
  65. function lz_left_to_process(lzi:plz_info):longint;
  66. { returns # chars read in but unprocessed }
  67. function lz_compress(lzi:plz_info; nchars:longint):longint;
  68. implementation
  69. {$IFDEF DEBUG_LZ}
  70. uses Sysutils;
  71. {$ENDIF}
  72. const
  73. MAX_MATCH = 253;
  74. MIN_MATCH = 2;
  75. procedure lz_init(lzi:plz_info; wsize:longint; max_dist:longint; max_match:longint; min_match:longint;
  76. frame_size:longint; get_chars:get_chars_t; output_match:output_match_t; output_literal:output_literal_t; user_data:pointer);
  77. begin
  78. { the reason for the separate max_dist value is LZX can't reach the
  79. first three characters in its nominal window. But using a smaller
  80. window results in inefficiency when dealing with reset intervals
  81. which are the length of the nominal window }
  82. lzi^.wsize := wsize;
  83. if (max_match > wsize) then
  84. lzi^.max_match := wsize
  85. else
  86. lzi^.max_match := max_match;
  87. lzi^.min_match := min_match;
  88. if (lzi^.min_match < 3) then lzi^.min_match := 3;
  89. lzi^.max_dist := max_dist;
  90. lzi^.block_buf_size := wsize + lzi^.max_dist;
  91. lzi^.block_buf := GetMem(lzi^.block_buf_size);
  92. lzi^.block_bufe := lzi^.block_buf + lzi^.block_buf_size;
  93. //assert(lzi^.block_buf != NULL);
  94. lzi^.cur_loc := 0;
  95. lzi^.block_loc := 0;
  96. lzi^.chars_in_buf := 0;
  97. lzi^.eofcount := 0;
  98. lzi^.get_chars := get_chars;
  99. lzi^.output_match := output_match;
  100. lzi^.output_literal := output_literal;
  101. lzi^.user_data := user_data;
  102. lzi^.frame_size := frame_size;
  103. lzi^.lentab := AllocMem(sizeof(longint)* lzi^.block_buf_size);
  104. lzi^.prevtab := AllocMem(sizeof(pu_char)* lzi^.block_buf_size);
  105. lzi^.analysis_valid := 0;
  106. end;
  107. procedure lz_release(lzi:plz_info);
  108. begin
  109. freemem(lzi^.block_buf);
  110. freemem(lzi^.lentab);
  111. freemem(lzi^.prevtab);
  112. end;
  113. procedure lz_reset(lzi: plz_info);
  114. var
  115. residual: longint;
  116. begin
  117. residual := lzi^.chars_in_buf - lzi^.block_loc;
  118. move(PByte(lzi^.block_buf)[lzi^.block_loc], lzi^.block_buf[0], residual);
  119. lzi^.chars_in_buf := residual;
  120. lzi^.block_loc := 0;
  121. lzi^.analysis_valid := 0;
  122. end;
  123. function lz_left_to_process(lzi: plz_info): longint;
  124. begin
  125. lz_left_to_process := lzi^.chars_in_buf - lzi^.block_loc;
  126. end;
  127. procedure fill_blockbuf(lzi: plz_info; maxchars: longint);
  128. var
  129. toread: longint;
  130. readhere: pu_char;
  131. nread: longint;
  132. begin
  133. if (lzi^.eofcount <> 0) then exit;
  134. Dec(maxchars, lz_left_to_process(lzi));
  135. toread := lzi^.block_buf_size - lzi^.chars_in_buf;
  136. if (toread > maxchars) then toread := maxchars;
  137. readhere := lzi^.block_buf + lzi^.chars_in_buf;
  138. nread := lzi^.get_chars(lzi, toread, readhere);
  139. Inc(lzi^.chars_in_buf, nread);
  140. if (nread <> toread) then
  141. Inc(lzi^.eofcount);
  142. end;
  143. procedure lz_analyze_block(lzi: plz_info);
  144. var
  145. lenp,
  146. lentab: plongint;
  147. prevtab, prevp: PPu_char;
  148. bbp, bbe: Pu_char;
  149. chartab: array [0..255] of pu_char;
  150. cursor: pu_char;
  151. prevlen,
  152. ch,
  153. maxlen: longint;
  154. maxcursor: PtrUInt;
  155. wasinc: Boolean;
  156. max_dist: longint;
  157. I: longint;
  158. begin
  159. max_dist := lzi^.max_dist;
  160. FillChar(chartab[0], sizeof(chartab), 0);
  161. prevtab := lzi^.prevtab;
  162. prevp := prevtab;
  163. lentab := lzi^.lentab;
  164. lenp := lentab;
  165. FillChar(prevtab[0], sizeof(prevtab) * lzi^.chars_in_buf, 0);
  166. FillChar(lentab[0], sizeof(prevtab) * lzi^.chars_in_buf, 0);
  167. bbp := lzi^.block_buf;
  168. bbe := bbp + lzi^.chars_in_buf;
  169. while (bbp < bbe) do begin
  170. ch := bbp^;
  171. if (chartab[ch] <> nil) then begin
  172. prevp^ := chartab[ch];
  173. lenp^ := 1;
  174. end;
  175. chartab[ch] := bbp;
  176. Inc(bbp);
  177. Inc(prevp);
  178. Inc(lenp);
  179. end;
  180. for maxlen := 1 to lzi^.max_match-1 do begin
  181. wasinc := False;
  182. bbp := bbe - maxlen;
  183. lenp := lentab + lzi^.chars_in_buf - maxlen;
  184. prevp := prevtab + lzi^.chars_in_buf - maxlen;
  185. //for I := 0 to (bbp-2 - lzi^.block_buf) do begin // we don't use the value of i
  186. while (bbp > lzi^.block_buf) do begin
  187. Dec(bbp);
  188. Dec(prevp);
  189. Dec(lenp);
  190. if lenp^ = maxlen then begin
  191. ch := bbp[maxlen];
  192. cursor := prevp^;
  193. while (cursor <> nil) and ((bbp - cursor) <= max_dist) do begin
  194. prevlen := (cursor - lzi^.block_buf + lentab)^;
  195. if (cursor[maxlen] = ch) then begin
  196. prevp^ := cursor;
  197. Inc(lenp^);
  198. wasinc := True;
  199. break;
  200. end;
  201. if (prevlen <> maxlen) then break;
  202. cursor := (cursor - lzi^.block_buf + prevtab)^;
  203. end;
  204. end;
  205. end;
  206. if not wasinc then break;
  207. end;
  208. lzi^.analysis_valid := 1;
  209. end;
  210. procedure lz_stop_compressing(lzi:plz_info);
  211. begin
  212. lzi^.stop := 1;
  213. end;
  214. function lz_compress(lzi:plz_info; nchars:longint):longint;
  215. var
  216. bbp, bbe: pu_char;
  217. lentab, lenp: plongint;
  218. prevtab, prevp: ppu_char;
  219. len: longint;
  220. holdback: longint;
  221. trimmed: smallint;
  222. residual: longint;
  223. bytes_to_move: longint;
  224. begin
  225. lzi^.stop := 0;
  226. while ((lz_left_to_process(lzi) <> 0) or (lzi^.eofcount =0)) and ((lzi^.stop =0) and (nchars > 0)) do begin
  227. if (lzi^.analysis_valid = 0)
  228. or ((lzi^.eofcount =0) and (lzi^.chars_in_buf- lzi^.block_loc < nchars)) then begin
  229. residual := lzi^.chars_in_buf - lzi^.block_loc;
  230. bytes_to_move := lzi^.max_dist + residual;
  231. if (bytes_to_move > lzi^.chars_in_buf) then
  232. bytes_to_move := lzi^.chars_in_buf;
  233. move(PByte(lzi^.block_buf)[lzi^.chars_in_buf - bytes_to_move], lzi^.block_buf, bytes_to_move);
  234. lzi^.block_loc := bytes_to_move - residual;
  235. lzi^.chars_in_buf := bytes_to_move;
  236. fill_blockbuf(lzi, nchars);
  237. lz_analyze_block(lzi);
  238. end;
  239. prevp := lzi^.prevtab + lzi^.block_loc;
  240. prevtab := prevp;
  241. lenp := lzi^.lentab + lzi^.block_loc;
  242. lentab := lenp;
  243. bbp := lzi^.block_buf + lzi^.block_loc;
  244. holdback := lzi^.max_match;
  245. if (lzi^.eofcount <> 0) then holdback := 0;
  246. if (lzi^.chars_in_buf < (nchars + lzi^.block_loc)) then
  247. bbe := lzi^.block_buf + lzi^.chars_in_buf - holdback
  248. else
  249. bbe := bbp + nchars;
  250. while ((bbp < bbe) and (lzi^.stop = 0)) do begin
  251. trimmed := 0;
  252. len := lenp^;
  253. if ((lzi^.frame_size <> 0) and (len > (lzi^.frame_size - lzi^.cur_loc mod lzi^.frame_size))) then begin
  254. trimmed := 1;
  255. len := (lzi^.frame_size - lzi^.cur_loc mod lzi^.frame_size);
  256. end;
  257. if (len > nchars) then begin
  258. trimmed := 1;
  259. len := nchars;
  260. end;
  261. if (len >= lzi^.min_match) then begin
  262. {$ifdef LAZY}
  263. if ((bbp < bbe -1) and (trimmed = 0) and
  264. ((lenp[1] > (len + 1)))) then begin
  265. len := 1;
  266. //* this is the lazy eval case */
  267. end
  268. else
  269. {$endif}
  270. if (lzi^.output_match(lzi, (prevp^ - lzi^.block_buf) - lzi^.block_loc, len) < 0) then begin
  271. len := 1; //* match rejected */
  272. end;
  273. end
  274. else
  275. len := 1;
  276. if (len < lzi^.min_match) then begin
  277. //assert(len == 1);
  278. lzi^.output_literal(lzi, bbp^);
  279. end;
  280. Inc(bbp,len);
  281. Inc(prevp, len);
  282. Inc(lenp, len);
  283. Inc(lzi^.cur_loc, len);
  284. Inc(lzi^.block_loc, len);
  285. //assert(nchars >= len);
  286. Dec(nchars, len);
  287. end;
  288. end;
  289. lz_compress := 0;
  290. end;
  291. end.