Примеры сжатия данных в C/C++ при помощи zlib

6 февраля 2017

Продолжая серию постов о полезных библиотеках в мире C/C++, стоило бы упомянуть хотя бы одну библиотеку для сжатия данных. Библиотек таких великое множество. Среди них, пожалуй, наиболее распространенной, своего рода стандартом де-факто, является zlib. Поэтому о ней далее речь и пойдет.

Пользуясь случаем, напомню, что в предыдущих сериях вы видели:

Теперь вернемся к zlib. Допустим, вам нужно сжать сравнительно небольшой кусок данных, целиком помещающийся в память. Делается это элементарно:

uLongf compress_buff_size = compressBound(file_size);
void* compress_buff = malloc(compress_buff_size);
if(compress_buff == NULL)
{
    fprintf(stderr,
        "malloc(compress_buff_size) failed, "
        "compress_buff_size = %lu\n",
        compress_buff_size);
    exit(1);
}

uLongf compressed_size = compress_buff_size;
res = compress(compress_buff, &compressed_size, file_buff, file_size);
if(res != Z_OK)
{
    fprintf(stderr, "compress(...) failed, res = %d\n", res);
    exit(1);
}

Процедура compressBound возвращает максимальный размер, какого могут оказаться данные в сжатом виде, а compress непосредственно производит сжатие. В результате в compress_buff будут записаны данные в сжатом виде, а в compressed_size — их размер.

Разжатие производится по аналогии:

uLongf decompressed_size = (uLongf)file_size;
res = uncompress(file_buff, &decompressed_size,
    compress_buff, compressed_size);
if(res != Z_OK)
{
    fprintf(stderr, "uncompress(...) failed, res = %d\n", res);
    exit(1);
}

Компрессия и декомпрессия больших объемов данных производится чуть сложнее, но ненамного:

/* Компрессия */
bool compress_file(FILE *src, FILE *dst)
{
    uint8_t inbuff[CHUNK_SIZE];
    uint8_t outbuff[CHUNK_SIZE];
    z_stream stream = {0};

    if(deflateInit(&stream, COMPRESSION_LEVEL) != Z_OK)
    {  
        fprintf(stderr, "deflateInit(...) failed!\n");
        return false;
    }  

    int flush;
    do {
        stream.avail_in = fread(inbuff, 1, CHUNK_SIZE, src);
        if(ferror(src))
        {
            fprintf(stderr, "fread(...) failed!\n");
            deflateEnd(&stream);
            return false;
        }

        flush = feof(src) ? Z_FINISH : Z_NO_FLUSH;
        stream.next_in = inbuff;

        do {
            stream.avail_out = CHUNK_SIZE;
            stream.next_out = outbuff;
            deflate(&stream, flush);
            uint32_t nbytes = CHUNK_SIZE - stream.avail_out;

            if(fwrite(outbuff, 1, nbytes, dst) != nbytes ||
               ferror(dst))
            {
                fprintf(stderr, "fwrite(...) failed!\n");
                deflateEnd(&stream);
                return false;
            }
        } while (stream.avail_out == 0);
    } while (flush != Z_FINISH);

    deflateEnd(&stream);
    return true;
}

/* Декомпрессия */
bool decompress_file(FILE *src, FILE *dst)
{
    uint8_t inbuff[CHUNK_SIZE];
    uint8_t outbuff[CHUNK_SIZE];
    z_stream stream = { 0 };

    int result = inflateInit(&stream);
    if(result != Z_OK)
    {  
        fprintf(stderr, "inflateInit(...) failed!\n");
        return false;
    }  

    do {
        stream.avail_in = fread(inbuff, 1, CHUNK_SIZE, src);
        if(ferror(src))
        {
            fprintf(stderr, "fread(...) failed!\n");
            inflateEnd(&stream);
            return false;
        }

        if(stream.avail_in == 0)
            break;

        stream.next_in = inbuff;

        do {
            stream.avail_out = CHUNK_SIZE;
            stream.next_out = outbuff;
            result = inflate(&stream, Z_NO_FLUSH);
            if(result == Z_NEED_DICT || result == Z_DATA_ERROR ||
                result == Z_MEM_ERROR)
            {
                fprintf(stderr, "inflate(...) failed: %d\n", result);
                inflateEnd(&stream);
                return false;
            }

            uint32_t nbytes = CHUNK_SIZE - stream.avail_out;

            if(fwrite(outbuff, 1, nbytes, dst) != nbytes ||
               ferror(dst))
            {
                fprintf(stderr, "fwrite(...) failed!\n");
                inflateEnd(&stream);
                return false;
            }
        } while (stream.avail_out == 0);
    } while (result != Z_STREAM_END);

    inflateEnd(&stream);
    return result == Z_STREAM_END;
}

Долго и нудно разжевывать этот код мне что-то лень. Думаю, вы в состоянии самостоятельно в нем разобраться. В крайнем случае всегда можно почитать /usr/include/zlib.h — в нем есть подробные комментарии ко всем процедурам.

Полная версия исходников к этому посту лежит на GitHub. Для сборки проекта я использовал Autotools. Не помню, зачем мне нужен был именно Autotools, так как код писался давно. В README.md есть инструкция по сборке, плюс обратите внимание на пост Основы сборки проектов при помощи Autotools.

Как обычно, буду рад вашим вопросам и дополнениям.

Метки: .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.