Как заставить LZO работать с файловым потоком? - PullRequest
2 голосов
/ 21 ноября 2010

Я пытаюсь сжать файловый поток с помощью LZO и не очень далеко.В частности, я получаю ошибку сегментации при извлечении файла архива, созданного моей compressFileWithLzo1x функцией.

My main объявления функций и прототипов:

#include <stdio.h>
#include <stdlib.h>
#include "lzo/include/lzo/lzo1x.h"

#define LZO_IN_CHUNK (128*1024L)
#define LZO_OUT_CHUNK (LZO_IN_CHUNK + LZO_IN_CHUNK/16 + 64 + 3)

int compressFileWithLzo1x(const char *inFn, const char *outFn);
int extractFileWithLzo1x(const char *inFn);

int main(int argc, char **argv) {

    const char *inFilename = "test.txt";
    const char *outFilename = "test.txt.lzo1x";

    if ( compressFileWithLzo1x(inFilename, outFilename) != 0 )
        exit(EXIT_FAILURE);

    if ( extractFileWithLzo1x(outFilename) != 0 )
        exit(EXIT_FAILURE);

    return 0;
}

Вот реализациямоя функция сжатия:

int compressFileWithLzo1x(const char *inFn, const char *outFn) {

    FILE *inFnPtr = fopen(outFn, "r");
    FILE *outFnPtr = fopen(outFn, "wb");
    int compressionResult;
    lzo_bytep in;
    lzo_bytep out;
    lzo_voidp wrkmem;
    lzo_uint out_len;
    size_t inResult;

    if (lzo_init() != LZO_E_OK)
        return -1;

    in = (lzo_bytep)malloc(LZO_IN_CHUNK);
    out = (lzo_bytep)malloc(LZO_OUT_CHUNK);
    wrkmem = (lzo_voidp)malloc(LZO1X_1_MEM_COMPRESS);

    do { 
        inResult = fread(in, sizeof(lzo_byte), LZO_IN_CHUNK, inFnPtr);
        if (inResult == 0)
            break;
        compressionResult = lzo1x_1_compress(in, LZO_IN_CHUNK, out, &out_len, wrkmem);
        if ((out_len >= LZO_IN_CHUNK) || (compressionResult != LZO_E_OK))
            return -1;
        if (fwrite(out, sizeof(lzo_byte), (size_t)out_len, outFnPtr) != (size_t)out_len || ferror(outFnPtr))
            return -1;
        fflush(outFnPtr);
    } while (!feof(inFnPtr) && !ferror(inFnPtr));

    free(wrkmem);
    free(out);
    free(in);
    fclose(inFnPtr);
    fclose(outFnPtr);

    return 0;
}

Вот реализация моей функции декомпрессии:

int extractFileWithLzo1x(const char *inFn) {

    FILE *inFnPtr = fopen(inFn, "rb");
    lzo_bytep in = (lzo_bytep)malloc(LZO_IN_CHUNK);
    lzo_bytep out = (lzo_bytep)malloc(LZO_OUT_CHUNK);
    int extractionResult; 
    size_t inResult;
    lzo_uint new_length;

    if (lzo_init() != LZO_E_OK)
        return -1;

    do {
        new_length = LZO_IN_CHUNK;
        inResult = fread(in, sizeof(lzo_byte), LZO_IN_CHUNK, inFnPtr);
        extractionResult = lzo1x_decompress(out, LZO_OUT_CHUNK, in, &new_length, NULL);
        if ((extractionResult != LZO_E_OK) || (new_length != LZO_IN_CHUNK))
            return -1;
        fprintf(stderr, "out: [%s]\n", (unsigned char *)out);
    } while (!feof(inFnPtr) && (!ferror(inFnPtr));

    free(in);
    free(out);
    fclose(inFnPtr);

    return 0;
}

Здесь происходит ошибка сегментации:

extractionResult = lzo1x_decompress(out, LZO_OUT_CHUNK, in, &new_length, NULL);

Что не так сэтот подход, который вызывает ошибку сегментации?

Надеюсь, на этот раз я не пропустил ни одного кода.Не стесняйтесь, дайте мне знать, если мне нужно добавить больше информации.Заранее спасибо за совет.

Ответы [ 3 ]

2 голосов
/ 22 апреля 2012

Вы сжимаете независимые блоки.Декомпрессору LZO требуется длина в байтах сжатых данных, потому что, когда он декодирует EOF, он проверяет, все ли входные байты были использованы (и возвращает ошибку, если нет), поэтому вам также необходимо сохранить длину каждого сжатого фрагмента.,Таким образом, вам нужен более сложный формат файла.Например:

# compressing, in python-like pseudocode
ifile = open("data", "rb")
ofile = open("data.mylzo", "wb")
input, input_len = ifile.read(65536)
while input_len > 0:
  compressed, compressed_len = lzo1x(input, input_len)
  compressed_len -= 1 # store len-1 of next block
  if compressed_len < 65536 - 1:
    ofile.write(compressed_len & 255) # be sure of endianess in file formats!
    ofile.write(compressed_len >> 8)
    ofile.write(compressed)
  else:
    ofile.write(255) # incompressible block stored it as-is (saves space & time).
    ofile.write(255)
    ofile.write(input)
  input, input_len = ifile.read(65536)
ofile.close()
ifile.close()

# decompressing, in python-like pseudocode
ifile = open("data.mylzo", "rb")
ofile = open("data", "wb")
compressed_len_s = ifile.read(2)
while len(compressed_len_s) == 2:
  compressed_len = (compressed_len_s[0] | (compressed_len_s[1] << 8)) + 1
  if compressed_len == 65536:
    ofile.write(ifile.read(65536)) # this can be done without copying
  else:
    compressed = ifile.read(compressed_len)
    decompressed = lzo1x_decompress(compressed, compressed_len)
    ofile.write(decompressed)
  compressed_len_s = ifile.read(2)
ofile.close()
ifile.close()

Если вы хотите иметь возможность распаковывать чанки без пропуска (для распаковки в параллельном или произвольном доступе), вы должны поместить длины сжатых чанков в начале, перед первым чанком,Предшествуйте им с количеством чанков.

Последний чанк может быть короче 64 Кб, и он может быть несжимаемым, но мы все равно будем хранить сжатую форму, даже если она длиннее несжатой формы, потому чтотолько полные блоки по 64 Кб хранятся как есть.Если весь файл короче, чем 64 КБ, он будет расти.

1 голос
/ 16 ноября 2012

Я думаю, что вы открываете неправильный файл в int compressFileWithLzo1x:

FILE *inFnPtr = fopen(outFn, "r");

это должно быть

FILE *inFnPtr = fopen(inFn, "r");
1 голос
/ 21 ноября 2010

Код, который вы дали, не скомпилируется (поддельные = в #defines; inFilePtr вместо inFnPtr в разных местах и ​​т. Д.). Но:

  1. При сжатии вы не учитываете фактический объем данных, возвращаемых fread(), который может быть меньше LZO_IN_CHUNK.

    compressionResult = lzo1x_1_compress(in, LZO_IN_CHUNK, out, &out_len, wrkmem);
    

    вероятно должно быть

    compressionResult = lzo1x_1_compress(in, inResult, out, &out_len, wrkmem);
    

    (Вряд ли это проблема, но в конец файла будет добавлено поддельное мусор.)

  2. При распаковке у вас возникает похожая проблема: и аргументы in / out неверны, что может быть причиной вашего segfault.

    extractionResult = lzo1x_decompress(out, LZO_OUT_CHUNK, in, &new_length, NULL);
    

    вероятно должно быть

    extractionResult = lzo1x_decompress(in, inResult, out, &new_length, NULL);
    
...