Продолжая серию постов о полезных библиотеках в мире 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 .
Как обычно, буду рад вашим вопросам и дополнениям.