Примеры сжатия данных в C/C++ при помощи zlib
6 февраля 2017
Продолжая серию постов о полезных библиотеках в мире C/C++, стоило бы упомянуть хотя бы одну библиотеку для сжатия данных. Библиотек таких великое множество. Среди них, пожалуй, наиболее распространенной, своего рода стандартом де-факто, является zlib. Поэтому о ней далее речь и пойдет.
Пользуясь случаем, напомню, что в предыдущих сериях вы видели:
- Не самый тривиальный пример использования libcurl;
- Перехват сетевого трафика при помощи библиотеки libpcap;
- Работа с PostgreSQL на C при помощи библиотеки libpq;
- Не унылый пост о списках и деревьях поиска в языке C;
- Пример создания GUI-приложений на wxWidgets;
- Работа с 3D-моделями при помощи Assimp;
- Довольно длинная серия постов, посвященная OpenGL;
- Возможно, еще что-то, о чем я уже забыл;
Теперь вернемся к zlib. Допустим, вам нужно сжать сравнительно небольшой кусок данных, целиком помещающийся в память. Делается это элементарно:
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
— их размер.
Разжатие производится по аналогии:
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.
Как обычно, буду рад вашим вопросам и дополнениям.
Метки: C/C++.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.