【问题标题】:Is this a bug in Java's Inflater or what?这是 Java 的 Inflater 中的错误还是什么?
【发布时间】:2020-01-21 19:50:32
【问题描述】:

在一些单元测试中我被这个咬了。

我想使用Inflater解压缩一些ZLIB压缩的数据,其中原始数据长度是预先知道的。

这(直截了当)按预期工作

    /*  
     * 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)。充气机没有完成,Adler CRC 校验没有完成,看起来;更多:似乎字节没有被送入充气机。

更奇怪的是:如果在一次调用中输入尾随的 4 个字节,它就可以工作。

你可以在这里试试:https://repl.it/@HernanJJ/Inflater-Test

当我一次输入一个字节的整个输入时,甚至会发生更奇怪的事情:有时int nx= inf.inflate(buf2);//should give 0 行返回非零 (当所有数据都已被夸大时)。

这是预期的行为吗?我错过了什么吗?

【问题讨论】:

  • @SeanBright 谢谢 - 你应该把它变成一个答案。在我看来,它仍然是一个奇怪的 API,inf.setInput(comp, comp.length-4,4) 有效而 for ... inf.setInput(comp, p, 1); 无效,并且在后一种情况下我需要插入对 inflate() 的调用,即使所有数据都已经被夸大了。叹息。

标签: java zlib inflate adler32


【解决方案1】:

正如@SeanBright 已经注意到的那样,您应该只在Inflater.needsInput() 返回true 时为其提供新的输入。

setInput 的顺序调用会覆盖您之前传递的输入。

Javadoc of Inflater.needsInput():

如果输入缓冲区中没有数据,则返回 true。这可用于确定是否应调用 #setInput 以提供更多输入。

只要你一个字节一个字节地输入它,你就可以跳过检查本身。

您可以用这个替换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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    相关资源
    最近更新 更多