Fix gzip decompression

This commit is contained in:
LoveSy 2023-02-13 02:33:23 +08:00 committed by John Wu
parent 508a001753
commit 97ed1b16d0

View File

@ -43,13 +43,17 @@ public:
case ENCODE: case ENCODE:
deflateEnd(&strm); deflateEnd(&strm);
break; break;
default:
break;
} }
} }
protected: protected:
enum mode_t { enum mode_t {
DECODE, DECODE,
ENCODE ENCODE,
WAIT,
COPY
} mode; } mode;
gz_strm(mode_t mode, stream_ptr &&base) : gz_strm(mode_t mode, stream_ptr &&base) :
@ -61,6 +65,8 @@ protected:
case ENCODE: case ENCODE:
deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
break; break;
default:
break;
} }
} }
@ -69,6 +75,20 @@ private:
uint8_t outbuf[CHUNK]; uint8_t outbuf[CHUNK];
bool do_write(const void *buf, size_t len, int flush) { bool do_write(const void *buf, size_t len, int flush) {
if (mode == WAIT) {
if (len == 0) return true;
Bytef b[1] = {0x1f};
if (*(Bytef *)buf == 0x8b) {
mode = DECODE;
inflateReset(&strm);
strm.next_in = b;
strm.avail_in = 1;
inflate(&strm, flush);
} else {
mode = COPY;
bwrite(b, 1);
}
}
strm.next_in = (Bytef *) buf; strm.next_in = (Bytef *) buf;
strm.avail_in = len; strm.avail_in = len;
do { do {
@ -82,6 +102,11 @@ private:
case ENCODE: case ENCODE:
code = deflate(&strm, flush); code = deflate(&strm, flush);
break; break;
case COPY:
return bwrite(buf, len);
default:
// should have been handled
return false;
} }
if (code == Z_STREAM_ERROR) { if (code == Z_STREAM_ERROR) {
LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code); LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code);
@ -89,6 +114,31 @@ private:
} }
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out)) if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
return false; return false;
if (mode == DECODE && code == Z_STREAM_END) {
if (strm.avail_in > 1) {
if (strm.next_in[0] == 0x1f && strm.next_in[1] == 0x8b) {
// There is still data in the stream, we need to reset the stream
// and continue decoding
inflateReset(&strm);
strm.avail_out = 0;
continue;
}
} else if (strm.avail_in == 1) {
if (strm.next_in[0] == 0x1f) {
// If there is only one byte left, we need to wait for the next byte
// to determine if it is a gzip header
mode = WAIT;
return true;
}
} else {
// The next inflate won't consume any data but fallback
// to the previous two conditions
return true;
}
// There is still data in the stream, we need to copy it
mode = COPY;
bwrite(strm.next_in, strm.avail_in);
}
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return true; return true;
} }