PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/nochump/util/zip/Inflater.as

http://github.com/mash/WonderflEditor
ActionScript | 265 lines | 166 code | 15 blank | 84 comment | 75 complexity | 109e506606f961fa393b76f0ffc4383b MD5 | raw file
  1. /*
  2. nochump.util.zip.Inflater
  3. Copyright (c) 2008 David Chang (dchang@nochump.com)
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. package nochump.util.zip {
  21. import flash.utils.Endian;
  22. import flash.utils.ByteArray;
  23. /**
  24. * Inflater is used to decompress data that has been compressed according
  25. * to the "deflate" standard described in rfc1950.
  26. *
  27. * The usage is as following. First you have to set some input with
  28. * <code>setInput()</code>, then inflate() it.
  29. *
  30. * This implementation is a port of Puff by Mark Addler that comes with
  31. * the zlip data compression library. It is not the fastest routine as
  32. * he intended it for learning purposes, his actual optimized inflater code
  33. * is very different. I went with this approach basically because I got a
  34. * headache looking at the optimized inflater code and porting this
  35. * was a breeze. The speed should be adequate but there is plenty of room
  36. * for improvements here.
  37. *
  38. * @author dchang
  39. */
  40. public class Inflater {
  41. private static const MAXBITS:int = 15; // maximum bits in a code
  42. private static const MAXLCODES:int = 286; // maximum number of literal/length codes
  43. private static const MAXDCODES:int = 30; // maximum number of distance codes
  44. private static const MAXCODES:int = MAXLCODES + MAXDCODES; // maximum codes lengths to read
  45. private static const FIXLCODES:int = 288; // number of fixed literal/length codes
  46. // Size base for length codes 257..285
  47. private static const LENS:Array = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
  48. // Extra bits for length codes 257..285
  49. private static const LEXT:Array = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
  50. // Offset base for distance codes 0..29
  51. private static const DISTS:Array = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
  52. // Extra bits for distance codes 0..29
  53. private static const DEXT:Array = [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
  54. private var inbuf:ByteArray; // input buffer
  55. private var incnt:uint; // bytes read so far
  56. private var bitbuf:int; // bit buffer
  57. private var bitcnt:int; // number of bits in bit buffer
  58. // Huffman code decoding tables
  59. private var lencode:Object;
  60. private var distcode:Object;
  61. /**
  62. * Sets the input.
  63. *
  64. * @param buf the input.
  65. */
  66. public function setInput(buf:ByteArray):void {
  67. inbuf = buf;
  68. inbuf.endian = Endian.LITTLE_ENDIAN;
  69. }
  70. /**
  71. * Inflates the compressed stream to the output buffer.
  72. *
  73. * @param buf the output buffer.
  74. */
  75. public function inflate(buf:ByteArray):uint {
  76. incnt = bitbuf = bitcnt = 0;
  77. var err:int = 0;
  78. do { // process blocks until last block or error
  79. var last:int = bits(1); // one if last block
  80. var type:int = bits(2); // block type 0..3
  81. //trace(' block type ' + type);
  82. if(type == 0) stored(buf); // uncompressed block
  83. else if(type == 3) throw new Error('invalid block type (type == 3)', -1);
  84. else { // compressed block
  85. lencode = {count:[], symbol:[]};
  86. distcode = {count:[], symbol:[]};
  87. if(type == 1) constructFixedTables();
  88. else if(type == 2) err = constructDynamicTables();
  89. if(err != 0) return err;
  90. err = codes(buf); // decode data until end-of-block code
  91. }
  92. if(err != 0) break; // return with error
  93. } while(!last);
  94. return err;
  95. }
  96. private function bits(need:int):int {
  97. // bit accumulator (can use up to 20 bits)
  98. // load at least need bits into val
  99. var val:int = bitbuf;
  100. while(bitcnt < need) {
  101. if (incnt == inbuf.length) throw new Error('available inflate data did not terminate', 2);
  102. val |= inbuf[incnt++] << bitcnt; // load eight bits
  103. bitcnt += 8;
  104. }
  105. // drop need bits and update buffer, always zero to seven bits left
  106. bitbuf = val >> need;
  107. bitcnt -= need;
  108. // return need bits, zeroing the bits above that
  109. return val & ((1 << need) - 1);
  110. }
  111. private function construct(h:Object, length:Array, n:int):int {
  112. var offs:Array = []; // offsets in symbol table for each length
  113. // count number of codes of each length
  114. for(var len:int = 0; len <= MAXBITS; len++) h.count[len] = 0;
  115. // assumes lengths are within bounds
  116. for(var symbol:int = 0; symbol < n; symbol++) h.count[length[symbol]]++;
  117. // no codes! complete, but decode() will fail
  118. if(h.count[0] == n) return 0;
  119. // check for an over-subscribed or incomplete set of lengths
  120. var left:int = 1; // one possible code of zero length
  121. for(len = 1; len <= MAXBITS; len++) {
  122. left <<= 1; // one more bit, double codes left
  123. left -= h.count[len]; // deduct count from possible codes
  124. if(left < 0) return left; // over-subscribed--return negative
  125. } // left > 0 means incomplete
  126. // generate offsets into symbol table for each length for sorting
  127. offs[1] = 0;
  128. for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h.count[len];
  129. // put symbols in table sorted by length, by symbol order within each length
  130. for(symbol = 0; symbol < n; symbol++)
  131. if(length[symbol] != 0) h.symbol[offs[length[symbol]]++] = symbol;
  132. // return zero for complete set, positive for incomplete set
  133. return left;
  134. }
  135. private function decode(h:Object):int {
  136. var code:int = 0; // len bits being decoded
  137. var first:int = 0; // first code of length len
  138. var index:int = 0; // index of first code of length len in symbol table
  139. for(var len:int = 1; len <= MAXBITS; len++) { // current number of bits in code
  140. code |= bits(1); // get next bit
  141. var count:int = h.count[len]; // number of codes of length len
  142. // if length len, return symbol
  143. if(code < first + count) return h.symbol[index + (code - first)];
  144. index += count; // else update for next length
  145. first += count;
  146. first <<= 1;
  147. code <<= 1;
  148. }
  149. return -9; // ran out of codes
  150. }
  151. private function codes(buf:ByteArray):int {
  152. // decode literals and length/distance pairs
  153. do {
  154. var symbol:int = decode(lencode);
  155. if(symbol < 0) return symbol; // invalid symbol
  156. if(symbol < 256) buf[buf.length] = symbol; // literal: symbol is the byte
  157. else if(symbol > 256) { // length
  158. // get and compute length
  159. symbol -= 257;
  160. if(symbol >= 29) throw new Error("invalid literal/length or distance code in fixed or dynamic block", -9);
  161. var len:int = LENS[symbol] + bits(LEXT[symbol]); // length for copy
  162. // get and check distance
  163. symbol = decode(distcode);
  164. if(symbol < 0) return symbol; // invalid symbol
  165. var dist:uint = DISTS[symbol] + bits(DEXT[symbol]); // distance for copy
  166. if(dist > buf.length) throw new Error("distance is too far back in fixed or dynamic block", -10);
  167. // copy length bytes from distance bytes back
  168. while(len--) buf[buf.length] = buf[buf.length - dist];
  169. }
  170. } while (symbol != 256); // end of block symbol
  171. return 0; // done with a valid fixed or dynamic block
  172. }
  173. private function stored(buf:ByteArray):void {
  174. // discard leftover bits from current byte (assumes s->bitcnt < 8)
  175. bitbuf = 0;
  176. bitcnt = 0;
  177. // get length and check against its one's complement
  178. if(incnt + 4 > inbuf.length) throw new Error('available inflate data did not terminate', 2);
  179. var len:uint = inbuf[incnt++]; // length of stored block
  180. len |= inbuf[incnt++] << 8;
  181. if(inbuf[incnt++] != (~len & 0xff) || inbuf[incnt++] != ((~len >> 8) & 0xff))
  182. throw new Error("stored block length did not match one's complement", -2);
  183. if(incnt + len > inbuf.length) throw new Error('available inflate data did not terminate', 2);
  184. while(len--) buf[buf.length] = inbuf[incnt++]; // copy len bytes from in to out
  185. }
  186. private function constructFixedTables():void {
  187. var lengths:Array = [];
  188. // literal/length table
  189. for(var symbol:int = 0; symbol < 144; symbol++) lengths[symbol] = 8;
  190. for(; symbol < 256; symbol++) lengths[symbol] = 9;
  191. for(; symbol < 280; symbol++) lengths[symbol] = 7;
  192. for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
  193. construct(lencode, lengths, FIXLCODES);
  194. // distance table
  195. for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
  196. construct(distcode, lengths, MAXDCODES);
  197. }
  198. private function constructDynamicTables():int {
  199. var lengths:Array = []; // descriptor code lengths
  200. // permutation of code length codes
  201. var order:Array = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
  202. // get number of lengths in each table, check lengths
  203. var nlen:int = bits(5) + 257;
  204. var ndist:int = bits(5) + 1;
  205. var ncode:int = bits(4) + 4; // number of lengths in descriptor
  206. if(nlen > MAXLCODES || ndist > MAXDCODES) throw new Error("dynamic block code description: too many length or distance codes", -3);
  207. // read code length code lengths (really), missing lengths are zero
  208. for(var index:int = 0; index < ncode; index++) lengths[order[index]] = bits(3);
  209. for(; index < 19; index++) lengths[order[index]] = 0;
  210. // build huffman table for code lengths codes (use lencode temporarily)
  211. var err:int = construct(lencode, lengths, 19);
  212. if(err != 0) throw new Error("dynamic block code description: code lengths codes incomplete", -4);
  213. // read length/literal and distance code length tables
  214. index = 0;
  215. while(index < nlen + ndist) {
  216. var symbol:int; // decoded value
  217. var len:int; // last length to repeat
  218. symbol = decode(lencode);
  219. if(symbol < 16) lengths[index++] = symbol; // length in 0..15
  220. else { // repeat instruction
  221. len = 0; // assume repeating zeros
  222. if(symbol == 16) { // repeat last length 3..6 times
  223. if(index == 0) throw new Error("dynamic block code description: repeat lengths with no first length", -5);
  224. len = lengths[index - 1]; // last length
  225. symbol = 3 + bits(2);
  226. }
  227. else if(symbol == 17) symbol = 3 + bits(3); // repeat zero 3..10 times
  228. else symbol = 11 + bits(7); // == 18, repeat zero 11..138 times
  229. if(index + symbol > nlen + ndist)
  230. throw new Error("dynamic block code description: repeat more than specified lengths", -6);
  231. while(symbol--) lengths[index++] = len; // repeat last or zero symbol times
  232. }
  233. }
  234. // build huffman table for literal/length codes
  235. err = construct(lencode, lengths, nlen);
  236. // only allow incomplete codes if just one code
  237. if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1))
  238. throw new Error("dynamic block code description: invalid literal/length code lengths", -7);
  239. // build huffman table for distance codes
  240. err = construct(distcode, lengths.slice(nlen), ndist);
  241. // only allow incomplete codes if just one code
  242. if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1))
  243. throw new Error("dynamic block code description: invalid distance code lengths", -8);
  244. return err;
  245. }
  246. }
  247. }