【问题标题】:Deflate data into a fixed length buffer将数据压缩到固定长度的缓冲区中
【发布时间】:2017-07-28 09:42:58
【问题描述】:

对于一个项目,我经常处理大量使用大量读取的加密数据。由于解密比膨胀需要更长的时间,我愿意在加密和存储之前对数据进行放气。

我面临的困难是数据存储在固定长度的块或页面中。磁盘上的这些块需要保持固定长度才能进行快速页面查找。所以基本上我试图将尽可能多的数据压缩到一个固定大小的页面中。

目前我正在尝试找到一个好的方法来做到这一点。但是,此时我有点卡在每次添加数据时跟踪压缩大小并且未压缩大小接近页面限制。 (因为如果数据的熵非常高,理论上数据也会由于压缩而增长一点)。目前,我正在尝试以下方法:

    final Deflater deflater = new Deflater();//Deflater.HUFFMAN_ONLY);
    final Inflater inflater = new Inflater();

    long start;
    long duration;
    int freeSpace = size;
    int fill = 0;
    byte[] page;
    final byte[] buf = new byte[8];

    deflater.reset();
    try( ByteArrayOutputStream boas = new ByteArrayOutputStream(size);
            DeflaterOutputStream dos = new DeflaterOutputStream(boas, deflater, size, true)){
        start = System.currentTimeMillis();
        while(true){
            long compressable = (long) (Random.nextLong(30) + 100);

            fill += ByteTools.longToByteArray(compressable, buf, 0, 8);
            dos.write(buf);
            freeSpace = size - boas.size();

            if(freeSpace < 16){
                System.out.println(boas.size());
                dos.finish();
                System.out.println(boas.size());
                page = boas.toByteArray();
                break;
            }
        }
        duration = System.currentTimeMillis() - start;
    }

上面的代码可以用于放气,但是输出的长度在 dos.finished() 上显着增加。这并不奇怪,但是有没有什么好的方法来确定最终的输出大小,或者还有其他更适合该任务的压缩方案吗?

由于可以应用填充,因此不需要 100% 准确的输出大小,因此 95%-100% 的范围将是完美且足够高性能的。当然,应始终防止 100%+。

基于跟踪和错误,我稍微调整了例程,这给了我很好的结果。但是我对这个解决方案还不是很满意。

        while(true){
            long compressable = (long) (Random.nextLong(30) + 100);

            block += ByteTools.longToByteArray(compressable, buf, 0, 8);
            dos.write(buf);

            if(block >= check){
                //check /= 2;
                dos.flush();
                fill += block;
                block = 0;
                check = (size - boas.size()) - 8;
                System.out.println(check);
            }

            if(check < 16){
                fill += block;
                dos.finish();
                page = boas.toByteArray();
                break;
            }
        }

该解决方案的压缩比与原始压缩比(在一个块中)相差不远,并且保持在所需输出大小的 8 字节以内。支票大小的减少有以下几种形式:

 16384
 8088
 4259
 2207
 1110
 540
 246
 94
 32
 3

在页面生成期间导致 9 次刷新和 1 次完成。

【问题讨论】:

    标签: java deflate


    【解决方案1】:

    deflate 不太适合这种情况,但如果你让它尝试几次,它可能会被强制接近填充一个块。看看fitblk,它完全符合您的要求,通过执行三个压缩通道,包括它们之间的两个解压缩。

    这个想法是压缩超过您的块大小,仅解压缩您的块大小,然后仅重新压缩已解压缩的内容。您这样做两次是为了非常接近或在很多时候完全填满街区。

    【讨论】:

    • 这仍然需要对压缩率进行一些猜测,以便知道什么会压缩到超过填充的块大小。但是它给了我一个很好的提示,即使用部分解压缩。在我的场景中,我可以在“未分页”文件的“尾部”上使用一个缓冲区,并在缓冲区超过页面大小时对其进行分页。我将编写更多测试以查看它是否适合该场景。谢谢。
    • 无需猜测。第一遍,继续压缩,直到你把块填满。
    • 在 Java 中,当使用带有 OutputStream 的 Comressor 时,输出大小在刷新之前不会更新。据我了解,刷新会导致压缩循环,因此为了防止过于频繁地调用刷新,我需要估计。
    • 在执行一些性能测试时,我发现我上面的答案中使用的当前可用空间检查解决方案的效率大约是执行压缩/解压缩/压缩/解压缩/压缩循环的 2 倍。稍微降低压缩率。所以现在我会坚持这个惯例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    相关资源
    最近更新 更多