Skip to content

Commit aefd58b

Browse files
author
Yuta Imaya
committed
fix issue#35: HLIT and HDIST are continuous
1 parent 099fed3 commit aefd58b

File tree

4 files changed

+173
-114
lines changed

4 files changed

+173
-114
lines changed

‎src/rawinflate.js

+45-60
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ Zlib.RawInflate = function(input, opt_params) {
5252
this.bufferType = Zlib.RawInflate.BufferType.ADAPTIVE;
5353
/** @type {boolean} resize flag for memory size optimization. */
5454
this.resize = false;
55-
/** @type {number} previous RLE value */
56-
this.prev;
5755

5856
// option parameters
5957
if (opt_params || !(opt_params = {})) {
@@ -457,12 +455,22 @@ Zlib.RawInflate.prototype.parseDynamicHuffmanBlock = function() {
457455
new (USE_TYPEDARRAY ? Uint8Array : Array)(Zlib.RawInflate.Order.length);
458456
/** @type {!Array} code lengths table. */
459457
var codeLengthsTable;
460-
/** @type {!(Uint8Array|Array.<number>)} literal and length code lengths. */
461-
var litlenLengths;
462-
/** @type {!(Uint8Array|Array.<number>)} distance code lengths. */
463-
var distLengths;
458+
/** @type {!(Uint8Array|Array.<number>)} literal and length code table. */
459+
var litlenTable;
460+
/** @type {!(Uint8Array|Array.<number>)} distance code table. */
461+
var distTable;
462+
/** @type {!(Uint8Array|Array.<number>)} code length table. */
463+
var lengthTable;
464+
/** @type {number} */
465+
var code;
466+
/** @type {number} */
467+
var prev;
468+
/** @type {number} */
469+
var repeat;
464470
/** @type {number} loop counter. */
465471
var i;
472+
/** @type {number} loop limit. */
473+
var il;
466474

467475
// decode code lengths
468476
for (i = 0; i < hclen; ++i) {
@@ -473,65 +481,42 @@ Zlib.RawInflate.prototype.parseDynamicHuffmanBlock = function() {
473481
codeLengths[Zlib.RawInflate.Order[i]] = 0;
474482
}
475483
}
476-
codeLengthsTable = buildHuffmanTable(codeLengths);
477484

478-
/**
479-
* decode function
480-
* @param {number} num number of lengths.
481-
* @param {!Array} table code lengths table.
482-
* @param {!(Uint8Array|Array.<number>)} lengths code lengths buffer.
483-
* @return {!(Uint8Array|Array.<number>)} code lengths buffer.
484-
*/
485-
function decode(num, table, lengths) {
486-
/** @type {number} */
487-
var code;
488-
/** @type {number} */
489-
var prev = this.prev;
490-
/** @type {number} */
491-
var repeat;
492-
/** @type {number} */
493-
var i;
494-
495-
for (i = 0; i < num;) {
496-
code = this.readCodeByTable(table);
497-
switch (code) {
498-
case 16:
499-
repeat = 3 + this.readBits(2);
500-
while (repeat--) { lengths[i++] = prev; }
501-
break;
502-
case 17:
503-
repeat = 3 + this.readBits(3);
504-
while (repeat--) { lengths[i++] = 0; }
505-
prev = 0;
506-
break;
507-
case 18:
508-
repeat = 11 + this.readBits(7);
509-
while (repeat--) { lengths[i++] = 0; }
510-
prev = 0;
511-
break;
512-
default:
513-
lengths[i++] = code;
514-
prev = code;
515-
break;
516-
}
485+
// decode length table
486+
codeLengthsTable = buildHuffmanTable(codeLengths);
487+
lengthTable = new (USE_TYPEDARRAY ? Uint8Array : Array)(hlit + hdist);
488+
for (i = 0, il = hlit + hdist; i < il;) {
489+
code = this.readCodeByTable(codeLengthsTable);
490+
switch (code) {
491+
case 16:
492+
repeat = 3 + this.readBits(2);
493+
while (repeat--) { lengthTable[i++] = prev; }
494+
break;
495+
case 17:
496+
repeat = 3 + this.readBits(3);
497+
while (repeat--) { lengthTable[i++] = 0; }
498+
prev = 0;
499+
break;
500+
case 18:
501+
repeat = 11 + this.readBits(7);
502+
while (repeat--) { lengthTable[i++] = 0; }
503+
prev = 0;
504+
break;
505+
default:
506+
lengthTable[i++] = code;
507+
prev = code;
508+
break;
517509
}
518-
519-
this.prev = prev;
520-
521-
return lengths;
522510
}
523511

524-
// literal and length code
525-
litlenLengths = new (USE_TYPEDARRAY ? Uint8Array : Array)(hlit);
512+
litlenTable = USE_TYPEDARRAY
513+
? buildHuffmanTable(lengthTable.subarray(0, hlit))
514+
: buildHuffmanTable(lengthTable.slice(0, hlit));
515+
distTable = USE_TYPEDARRAY
516+
? buildHuffmanTable(lengthTable.subarray(hlit))
517+
: buildHuffmanTable(lengthTable.slice(hlit));
526518

527-
// distance code
528-
distLengths = new (USE_TYPEDARRAY ? Uint8Array : Array)(hdist);
529-
530-
this.prev = 0;
531-
this.decodeHuffman(
532-
buildHuffmanTable(decode.call(this, hlit, codeLengthsTable, litlenLengths)),
533-
buildHuffmanTable(decode.call(this, hdist, codeLengthsTable, distLengths))
534-
);
519+
this.decodeHuffman(litlenTable, distTable);
535520
};
536521

537522
/**

‎src/rawinflate_stream.js

+51-54
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ Zlib.RawInflateStream = function(input, ip, opt_buffersize) {
5454
this.sp = 0; // stream pointer
5555
/** @type {Zlib.RawInflateStream.Status} */
5656
this.status = Zlib.RawInflateStream.Status.INITIALIZED;
57-
/** @type {number} previous RLE value */
58-
this.prev;
5957

6058
//
6159
// backup
@@ -529,8 +527,6 @@ Zlib.RawInflateStream.prototype.parseDynamicHuffmanBlock = function() {
529527
var litlenLengths;
530528
/** @type {!(Uint32Array|Array)} distance code lengths. */
531529
var distLengths;
532-
/** @type {number} loop counter. */
533-
var i = 0;
534530

535531
this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_START;
536532

@@ -553,6 +549,15 @@ Zlib.RawInflateStream.prototype.parseDynamicHuffmanBlock = function() {
553549
function parseDynamicHuffmanBlockImpl() {
554550
/** @type {number} */
555551
var bits;
552+
var code;
553+
var prev = 0;
554+
var repeat;
555+
/** @type {!(Uint8Array|Array.<number>)} code length table. */
556+
var lengthTable;
557+
/** @type {number} loop counter. */
558+
var i;
559+
/** @type {number} loop limit. */
560+
var il;
556561

557562
// decode code lengths
558563
for (i = 0; i < hclen; ++i) {
@@ -561,55 +566,44 @@ Zlib.RawInflateStream.prototype.parseDynamicHuffmanBlock = function() {
561566
}
562567
codeLengths[Zlib.RawInflateStream.Order[i]] = bits;
563568
}
564-
codeLengthsTable = buildHuffmanTable(codeLengths);
565569

566-
// decode function
567-
function decode(num, table, lengths) {
568-
var code;
569-
var prev = this.prev;
570-
var repeat;
571-
var i;
572-
var bits;
573-
574-
for (i = 0; i < num;) {
575-
code = this.readCodeByTable(table);
576-
if (code < 0) {
577-
throw new Error('not enough input');
578-
}
579-
switch (code) {
580-
case 16:
581-
if ((bits = this.readBits(2)) < 0) {
582-
throw new Error('not enough input');
583-
}
584-
repeat = 3 + bits;
585-
while (repeat--) { lengths[i++] = prev; }
586-
break;
587-
case 17:
588-
if ((bits = this.readBits(3)) < 0) {
589-
throw new Error('not enough input');
590-
}
591-
repeat = 3 + bits;
592-
while (repeat--) { lengths[i++] = 0; }
593-
prev = 0;
594-
break;
595-
case 18:
596-
if ((bits = this.readBits(7)) < 0) {
597-
throw new Error('not enough input');
598-
}
599-
repeat = 11 + bits;
600-
while (repeat--) { lengths[i++] = 0; }
601-
prev = 0;
602-
break;
603-
default:
604-
lengths[i++] = code;
605-
prev = code;
606-
break;
607-
}
570+
// decode length table
571+
codeLengthsTable = buildHuffmanTable(codeLengths);
572+
lengthTable = new (USE_TYPEDARRAY ? Uint8Array : Array)(hlit + hdist);
573+
for (i = 0, il = hlit + hdist; i < il;) {
574+
code = this.readCodeByTable(codeLengthsTable);
575+
if (code < 0) {
576+
throw new Error('not enough input');
577+
}
578+
switch (code) {
579+
case 16:
580+
if ((bits = this.readBits(2)) < 0) {
581+
throw new Error('not enough input');
582+
}
583+
repeat = 3 + bits;
584+
while (repeat--) { lengthTable[i++] = prev; }
585+
break;
586+
case 17:
587+
if ((bits = this.readBits(3)) < 0) {
588+
throw new Error('not enough input');
589+
}
590+
repeat = 3 + bits;
591+
while (repeat--) { lengthTable[i++] = 0; }
592+
prev = 0;
593+
break;
594+
case 18:
595+
if ((bits = this.readBits(7)) < 0) {
596+
throw new Error('not enough input');
597+
}
598+
repeat = 11 + bits;
599+
while (repeat--) { lengthTable[i++] = 0; }
600+
prev = 0;
601+
break;
602+
default:
603+
lengthTable[i++] = code;
604+
prev = code;
605+
break;
608606
}
609-
610-
this.prev = prev;
611-
612-
return lengths;
613607
}
614608

615609
// literal and length code
@@ -618,9 +612,12 @@ Zlib.RawInflateStream.prototype.parseDynamicHuffmanBlock = function() {
618612
// distance code
619613
distLengths = new (USE_TYPEDARRAY ? Uint8Array : Array)(hdist);
620614

621-
this.prev = 0;
622-
this.litlenTable = buildHuffmanTable(decode.call(this, hlit, codeLengthsTable, litlenLengths));
623-
this.distTable = buildHuffmanTable(decode.call(this, hdist, codeLengthsTable, distLengths));
615+
this.litlenTable = USE_TYPEDARRAY
616+
? buildHuffmanTable(lengthTable.subarray(0, hlit))
617+
: buildHuffmanTable(lengthTable.slice(0, hlit));
618+
this.distTable = USE_TYPEDARRAY
619+
? buildHuffmanTable(lengthTable.subarray(hlit))
620+
: buildHuffmanTable(lengthTable.slice(hlit));
624621
}
625622

626623
this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_END;

‎test/browser-inflate-test.js

+37
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ var fixedData =
2626
'/8//5//z//n//H/+P/+f/8//5//z//n//H/+P/+f/8//5//z//n//H/+P/+f/8//5//z' +
2727
'//n//H/+P/+f/8//5//z//n//H/+3+P/Ba1OJPE=';
2828

29+
function inflateOnlyTest(done, compressed, plain) {
30+
var inflated = zlib.inflateSync(compressed);
31+
32+
buster.assert.equals(inflated.length, plain.length);
33+
buster.assert(arrayEquals(inflated, plain));
34+
}
35+
2936
buster.testCase(
3037
"inflate only",
3138
{
@@ -146,6 +153,36 @@ buster.testCase(
146153
buster.assert.equals(inflated.length, size, "inflated data size");
147154
buster.assert.equals(inflated.buffer.byteLength, 123456, "inflated data buffer size");
148155
buster.assert(arrayEquals(inflated, plain));
156+
},
157+
"issue#35 wrong inflate 1":
158+
function() {
159+
var compressed = decodeB64(
160+
"eJx9jq0NQCEMhG8YJMOwAgJdhWYA9kAyAIuQoBmkvNQ80Z+kP/l6uRy8urIysOVoprTaLlOlcXyzvE7qP3jBuo4XCDg/QAyZCQjI"
161+
);
162+
var plain =
163+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,0,0,0,0,0,0,97,0,0,215,0,0,0,0,0,97,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,166,0,0,0,0,101,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186,0,111,0,0,218,0,0,0,0,0,0,0,0,0,0,0,111];
164+
165+
inflateOnlyTest(compressed, plain);
166+
},
167+
"issue#35 wrong inflate 2":
168+
function() {
169+
var compressed = decodeB64(
170+
"eJxtjD0NgEAMhd8IBlDAigNMsDCxsSIIAyScikvOzAk4A02Tdujf8r2/FPl1Fctk8jufDzSGbGdJ17A7fbAZd6iowMMswM78tLjCy9+417UEYNsIeA=="
171+
);
172+
var plain =
173+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,0,0,0,0,0,0,0,0,22,8,0,0,0,0,0,0,0,0,0,0,0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,243,0,194,0,0,0,0,8,0,0,0,0,0,0,9,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,81,0,0,0,0,0,0,0,0,191,0,0,108,0,0,0,0,169,0,0,60,0,0,0,0,165,0,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,0,0,0,0,0,0,0,0,0,0,0,0,0,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
174+
175+
inflateOnlyTest(compressed, plain);
176+
},
177+
"issue#35 take a long time and throw exception":
178+
function(done) {
179+
var compressed = decodeB64(
180+
"eJx1jb8NQFAQxj+J2MAASo1EJGIEM1hAhV6iVEg0GgvozGEHG+jscDm5Q/L+FL/3+y73vjfAfVbciDXkqFR9lKretz3XWHbxxl44RSKb+9PWmFw/7+U+MvNlgtTa2D0s3jwyAsYZMgl65Qwx"
181+
);
182+
var plain =
183+
[125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,237,0,41,0,0,0,0,0,0,0,0,54,0,90,0,0,0,0,0,0,0,0,4,0,72,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,140,103,0,145,170,0,0,0,0,0,0,0,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,137,33,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,230,0,0,0,0,0,0,0,0,0,0,0,122,0,0,0,0,0,0,0,196,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,133,0,0,0,0,6,0,0,0,0,215,21,0,0,0,0];
184+
185+
inflateOnlyTest(compressed, plain);
149186
}
150187
}
151188
);

‎test/node-test.js

+40
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,39 @@ buster.testCase(
162162
"bitbuflen error": function(done) {
163163
makeRandomSequentialData(this.testData, 1339494909128);
164164
gunzipTest(done, this.testData);
165+
},
166+
"issue#35 wrong inflate 1": function() {
167+
var compressed = new Buffer(
168+
"eJx9jq0NQCEMhG8YJMOwAgJdhWYA9kAyAIuQoBmkvNQ80Z+kP/l6uRy8urIysOVoprTaLlOlcXyzvE7qP3jBuo4XCDg/QAyZCQjI",
169+
"base64"
170+
);
171+
var plain = new Buffer(
172+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,0,0,0,0,0,0,97,0,0,215,0,0,0,0,0,97,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,166,0,0,0,0,101,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186,0,111,0,0,218,0,0,0,0,0,0,0,0,0,0,0,111]
173+
);
174+
175+
inflateOnlyTest(compressed, plain);
176+
},
177+
"issue#35 wrong inflate 2": function() {
178+
var compressed = new Buffer(
179+
"eJxtjD0NgEAMhd8IBlDAigNMsDCxsSIIAyScikvOzAk4A02Tdujf8r2/FPl1Fctk8jufDzSGbGdJ17A7fbAZd6iowMMswM78tLjCy9+417UEYNsIeA==",
180+
"base64"
181+
);
182+
var plain = new Buffer(
183+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,0,0,0,0,0,0,0,0,22,8,0,0,0,0,0,0,0,0,0,0,0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,243,0,194,0,0,0,0,8,0,0,0,0,0,0,9,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,81,0,0,0,0,0,0,0,0,191,0,0,108,0,0,0,0,169,0,0,60,0,0,0,0,165,0,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,0,0,0,0,0,0,0,0,0,0,0,0,0,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
184+
);
185+
186+
inflateOnlyTest(compressed, plain);
187+
},
188+
"issue#35 take a long time and throw exception": function() {
189+
var compressed = new Buffer(
190+
"eJx1jb8NQFAQxj+J2MAASo1EJGIEM1hAhV6iVEg0GgvozGEHG+jscDm5Q/L+FL/3+y73vjfAfVbciDXkqFR9lKretz3XWHbxxl44RSKb+9PWmFw/7+U+MvNlgtTa2D0s3jwyAsYZMgl65Qwx",
191+
"base64"
192+
);
193+
var plain = new Buffer(
194+
[125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,237,0,41,0,0,0,0,0,0,0,0,54,0,90,0,0,0,0,0,0,0,0,4,0,72,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,140,103,0,145,170,0,0,0,0,0,0,0,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,137,33,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,230,0,0,0,0,0,0,0,0,0,0,0,122,0,0,0,0,0,0,0,196,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,133,0,0,0,0,6,0,0,0,0,215,21,0,0,0,0]
195+
);
196+
197+
inflateOnlyTest(compressed, plain);
165198
}
166199
}
167200
);
@@ -186,6 +219,13 @@ function inflateTest(done, testData) {
186219
});
187220
}
188221

222+
function inflateOnlyTest(compressed, plain) {
223+
var inflated = zlib.inflateSync(compressed);
224+
225+
buster.assert.equals(inflated.length, plain.length);
226+
buster.assert.equals(inflated, plain);
227+
}
228+
189229
// gzip test
190230
function gzipTest(done, testData) {
191231
var deflated = zlib.gzipSync(testData);

0 commit comments

Comments
 (0)