这是 Java 的 Inflater 中的错误还是什么?
Is this a bug in Java's Inflater or what?
我在一些单元测试中被这个咬伤了。
我想解压缩一些 ZLIB 压缩的数据,使用 Inflater,其中原始数据长度是预先知道的。
这(直接)按预期工作
/*
* Decompresses a zlib compressed buffer, with given size of raw data.
* All data is fed and inflated in full (one step)
*/
public static byte[] decompressFull(byte[] comp, int len) throws Exception {
byte[] res = new byte[len]; // result (uncompressed)
Inflater inf = new Inflater();
inf.setInput(comp);
int n = inf.inflate(res, 0, len);
if (n != len)
throw new RuntimeException("didn't inflate all data");
System.out.println("Data done (full). bytes in :" + inf.getBytesRead()
+ " out=" + inf.getBytesWritten()
+ " finished: " + inf.finished());
// done - the next is not needed, just for checking...
//try a final inflate just in case (might trigger ZLIB crc check)
byte[] buf2 = new byte[6];
int nx = inf.inflate(buf2);//should give 0
if (nx != 0)
throw new RuntimeException("nx=" + nx + " " + Arrays.toString(buf2));
if (!inf.finished())
throw new RuntimeException("not finished?");
inf.end();
return res;
}
现在,压缩后的输入可以是任意大小的块。下面的代码模拟了压缩输入除了最后 4 个字节外全部送入,然后其余字节一次送入一个的情况。
(据我了解,解压缩完整数据不需要 zlib 流的最后 4 个或 5 个字节,但需要它们来检查完整性 - Adler-32 CRC)。
public static byte[] decompressBytexByte(byte[] comp, int len) throws Exception {
byte[] res = new byte[len]; // result (uncompressed)
Inflater inf = new Inflater();
inf.setInput(comp, 0, comp.length - 4);
int n = inf.inflate(res, 0, len);
if (n != len)
throw new RuntimeException("didn't inflate all data");
// inf.setInput(comp, comp.length-4,4);
// !!! works if I uncomment the line befor and comment the next for
for (int p = comp.length - 4; p < comp.length; p++)
inf.setInput(comp, p, 1);
System.out.println("Data done (decompressBytexByte). bytes in :" + inf.getBytesRead()
+ " out=" + inf.getBytesWritten() + " finished: " + inf.finished());
// all data fed... try a final inflate (might -should?- trigger ZLIB crc check)
byte[] buf2 = new byte[6];
int nx = inf.inflate(buf2);//should give 0
if (nx != 0)
throw new RuntimeException("nx=" + nx + " " + Arrays.toString(buf2));
if (!inf.finished())
throw new RuntimeException("not finished?");
inf.end();
return res;
}
嗯,这对我不起作用 (Java 1.8.0_181)。似乎充气机还没有完成,阿德勒 CRC 校验也没有完成;更多:似乎字节没有被送入充气机。
更奇怪:如果尾部 4 个字节在一次调用中被送入,它就可以工作。
您可以在这里尝试:https://repl.it/@HernanJJ/Inflater-Test
当我一次输入一个字节时,甚至会发生更奇怪的事情:有时行 int nx= inf.inflate(buf2);//should give 0
return 非零
(当所有数据都已经膨胀时)。
这是预期的行为吗?我错过了什么吗?
正如 @SeanBright
已经注意到的,您应该只在 Inflater.needsInput()
returns 为真时才为其提供新输入。
顺序调用 setInput
会覆盖您之前传递的输入。
Javadoc of Inflater.needsInput()
:
Returns true if no data remains in the input buffer. This can be used to determine if #setInput should be called in order to provide more input.
只要你一个字节一个字节地输入它,情况总是如此,所以你可以跳过检查本身。
您可以将 decompressBytexByte
方法的输入设置部分替换为(对于逐字节馈送的完整输入):
byte[] res = new byte[len];
Inflater inf = new Inflater();
int a = 0; // number of bytes that have already been obtained
for (int p = 0; p < comp.length; p++) {
inf.setInput(comp, p, 1);
a += inf.inflate(res, a, len - a);
}
我在一些单元测试中被这个咬伤了。
我想解压缩一些 ZLIB 压缩的数据,使用 Inflater,其中原始数据长度是预先知道的。
这(直接)按预期工作
/*
* Decompresses a zlib compressed buffer, with given size of raw data.
* All data is fed and inflated in full (one step)
*/
public static byte[] decompressFull(byte[] comp, int len) throws Exception {
byte[] res = new byte[len]; // result (uncompressed)
Inflater inf = new Inflater();
inf.setInput(comp);
int n = inf.inflate(res, 0, len);
if (n != len)
throw new RuntimeException("didn't inflate all data");
System.out.println("Data done (full). bytes in :" + inf.getBytesRead()
+ " out=" + inf.getBytesWritten()
+ " finished: " + inf.finished());
// done - the next is not needed, just for checking...
//try a final inflate just in case (might trigger ZLIB crc check)
byte[] buf2 = new byte[6];
int nx = inf.inflate(buf2);//should give 0
if (nx != 0)
throw new RuntimeException("nx=" + nx + " " + Arrays.toString(buf2));
if (!inf.finished())
throw new RuntimeException("not finished?");
inf.end();
return res;
}
现在,压缩后的输入可以是任意大小的块。下面的代码模拟了压缩输入除了最后 4 个字节外全部送入,然后其余字节一次送入一个的情况。 (据我了解,解压缩完整数据不需要 zlib 流的最后 4 个或 5 个字节,但需要它们来检查完整性 - Adler-32 CRC)。
public static byte[] decompressBytexByte(byte[] comp, int len) throws Exception {
byte[] res = new byte[len]; // result (uncompressed)
Inflater inf = new Inflater();
inf.setInput(comp, 0, comp.length - 4);
int n = inf.inflate(res, 0, len);
if (n != len)
throw new RuntimeException("didn't inflate all data");
// inf.setInput(comp, comp.length-4,4);
// !!! works if I uncomment the line befor and comment the next for
for (int p = comp.length - 4; p < comp.length; p++)
inf.setInput(comp, p, 1);
System.out.println("Data done (decompressBytexByte). bytes in :" + inf.getBytesRead()
+ " out=" + inf.getBytesWritten() + " finished: " + inf.finished());
// all data fed... try a final inflate (might -should?- trigger ZLIB crc check)
byte[] buf2 = new byte[6];
int nx = inf.inflate(buf2);//should give 0
if (nx != 0)
throw new RuntimeException("nx=" + nx + " " + Arrays.toString(buf2));
if (!inf.finished())
throw new RuntimeException("not finished?");
inf.end();
return res;
}
嗯,这对我不起作用 (Java 1.8.0_181)。似乎充气机还没有完成,阿德勒 CRC 校验也没有完成;更多:似乎字节没有被送入充气机。
更奇怪:如果尾部 4 个字节在一次调用中被送入,它就可以工作。
您可以在这里尝试:https://repl.it/@HernanJJ/Inflater-Test
当我一次输入一个字节时,甚至会发生更奇怪的事情:有时行 int nx= inf.inflate(buf2);//should give 0
return 非零
(当所有数据都已经膨胀时)。
这是预期的行为吗?我错过了什么吗?
正如 @SeanBright
已经注意到的,您应该只在 Inflater.needsInput()
returns 为真时才为其提供新输入。
顺序调用 setInput
会覆盖您之前传递的输入。
Javadoc of Inflater.needsInput()
:
Returns true if no data remains in the input buffer. This can be used to determine if #setInput should be called in order to provide more input.
只要你一个字节一个字节地输入它,情况总是如此,所以你可以跳过检查本身。
您可以将 decompressBytexByte
方法的输入设置部分替换为(对于逐字节馈送的完整输入):
byte[] res = new byte[len];
Inflater inf = new Inflater();
int a = 0; // number of bytes that have already been obtained
for (int p = 0; p < comp.length; p++) {
inf.setInput(comp, p, 1);
a += inf.inflate(res, a, len - a);
}