Java: ошибка CRC при использовании setDictionary для Deflater GZIPOutputStream - PullRequest
2 голосов
/ 08 февраля 2012

Я пытаюсь взять поток данных из стандартного входа, сжать его по одному 128-байтовому блоку за раз, а затем вывести его в стандартный вывод.(Пример: «cat file.txt | java Dict | gzip -d | cmp file.txt», где file.txt просто содержит некоторые символы ASCII.)

Мне также нужно использовать 32-байтовый словарь, взятый изконец каждого предыдущего 128-байтового блока, для каждого последующего блока.(Первый блок использует свои первые 32 байта в качестве словаря.) Когда я вообще не устанавливаю словарь, сжатие работает нормально.Однако, когда я устанавливаю словарь, gzip выдает ошибку при попытке распаковать данные: «gzip: stdin: неверные сжатые данные - ошибка crc».

Я попытался добавить / изменить несколько частейкод, но пока ничего не получалось, и мне не повезло найти решения с Google.

Я пробовал ...

  • Добавление "def.reset() "before" def.setDictionary (b) "в нижней части кода не работает.
  • Не работает только установка словаря для блоков после первого блока.(Не используется словарь для первого блока.)
  • Вызов updateCRC с массивом «input» до или после compress.write (input, 0, bytesRead) не работает.

Я бы очень признателен за любые предложения - есть ли что-то очевидное, что я упускаю или делаю неправильно?

Это то, что у меня есть в моем файле Dict.java:

import java.io.*;
import java.util.zip.GZIPOutputStream;

public class Dict {
  protected static final int BLOCK_SIZE = 128;
  protected static final int DICT_SIZE = 32;

  public static void main(String[] args) {
    InputStream stdinBytes = System.in;
    byte[] input = new byte[BLOCK_SIZE];
    byte[] dict = new byte[DICT_SIZE];
    int bytesRead = 0;

    try {
        DictGZIPOuputStream compressor = new DictGZIPOuputStream(System.out);
        bytesRead = stdinBytes.read(input, 0, BLOCK_SIZE);
        if (bytesRead >= DICT_SIZE) {
            System.arraycopy(input, 0, dict, 0, DICT_SIZE);
            compressor.setDictionary(dict);
        }

        do {
            compressor.write(input, 0, bytesRead);
            compressor.flush();

            if (bytesRead == BLOCK_SIZE) {
                System.arraycopy(input, BLOCK_SIZE-DICT_SIZE-1, dict, 0, DICT_SIZE);
                compressor.setDictionary(dict);
            }
            bytesRead = stdinBytes.read(input, 0, BLOCK_SIZE);
        } while (bytesRead > 0);

        compressor.finish();
    }
    catch (IOException e) {e.printStackTrace();}
  }

  public static class DictGZIPOuputStream extends GZIPOutputStream {
    public DictGZIPOuputStream(OutputStream out) throws IOException {
        super(out);
    }

    public void setDictionary(byte[] b) {
        def.setDictionary(b);
    }
    public void updateCRC(byte[] input) {
        crc.update(input);
    }
  }
}

1 Ответ

1 голос
/ 20 марта 2012

Я точно не знаю внутреннюю работу алгоритма zlib, но, основываясь на моем понимании DictGZIPOutputStream, когда вы вызываете метод write (), после его записи он обновит свой crc для этого байтового массива. Поэтому, если вы снова вызовете updateCRC() в своем коде, то все станет неправильно, так как crc обновляется дважды. Затем, когда выполняется gzip -d, в результате двух предыдущих обновлений crc, gzip будет выдавать сообщение «неверные сжатые данные - ошибка crc»

Я также заметил, что вы не закрывали компрессор после его использования. Когда я выполнил код, вставленный выше, он выдал ошибку «gzip: stdin: неожиданный конец файла». Поэтому всегда обязательно сбрасывайте метод и метод close вызывается в конце. С учетом сказанного, у меня есть следующее,

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;


public class Dict
{
    protected static final int BLOCK_SIZE = 128;
    protected static final int DICT_DIZE = 32;

    public static void main(String[] args)
    {
        InputStream stdinBytes = System.in;
        byte[] input = new byte[BLOCK_SIZE];
        byte[] dict = new byte[DICT_DIZE];
        int bytesRead = 0;

        try
        {
            DictGZIPOutputStream compressor = new DictGZIPOutputStream(System.out);
            bytesRead = stdinBytes.read(input, 0, BLOCK_SIZE);

            if (bytesRead >= DICT_DIZE)
            {
                System.arraycopy(input, 0, dict, 0, DICT_DIZE);
            }

            do 
            {               
                compressor.write(input, 0, bytesRead);              

                if (bytesRead == BLOCK_SIZE)
                {
                    System.arraycopy(input, BLOCK_SIZE-1, dict, 0, DICT_DIZE);
                    compressor.setDictionary(dict);
                }

                bytesRead = stdinBytes.read(input, 0, BLOCK_SIZE);
            }
            while (bytesRead > 0);
            compressor.flush();         
            compressor.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

    }

    public static class DictGZIPOutputStream extends GZIPOutputStream
    {

        public DictGZIPOutputStream(OutputStream out) throws IOException
        {
            super(out);
        }

        public void setDictionary(byte[] b)
        {
            def.setDictionary(b);
        }

        public void updateCRC(byte[] input)
        {
            crc.update(input);
        }                       
    }

}

Результат теста на консоли.

$ cat file.txt 
hello world, how are you?1e3djw
hello world, how are you?1e3djw adfa asdfas

$ cat file.txt | java Dict | gzip -d | cmp file.txt ; echo $?
0
...