Личный опыт разработки ПО

Сборник рецептов

Архивирование с библиотеками zlib и bzip2/libbzip2 используя Boost Iostreams

Прокомментировать

Недавно у меня возникла задача добавить сжатие данных в программу. Сразу нашлось две свободных библиотеки – zlib и libbzip2 написанные на C. Изучив вопрос глубже, оказалось что писать удобные обертки над функциями на C не нужно, так как в Boost IOStreams все уже написано.

О том как добавить данный функционал и правильно собрать проект я расскажу в данной заметке. Также будет приведен пример кода для сжатия и распаковки файлов и сравнение zlib (алгоритм DEFLATE, методы gzip и zlib) и libbzip2 (алгоритм bzip2) по скорости работы и уровню сжатия тестовых, бинарных и исполняемых файлов.

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

Сразу приведу код, который рассмотрю ниже:

boost::iostreams::filtering_ostreambuf out;
out.push(boost::iostreams::bzip2_compressor());
out.push(boost::iostreams::file_sink("data.bin", std::ios::binary));
boost::iostreams::copy(boost::iostreams::file_source("data.bz2", std::ios::binary), out);
...
boost::iostreams::filtering_istreambuf in;
in.push(boost::iostreams::bzip2_decompressor());
in.push(boost::iostreams::file_source("data.bz2", std::ios::binary));
boost::iostreams::copy(in, boost::iostreams::file_sink("data.bin", std::ios::binary));

По сути в этих восьми строках содержится все необходимое для сжатия и распаковки файла. В обоих случаях мы создаем однонаправленный (только на чтение или только на запись) фильтрующий буфер потока. Создаем фильтр осуществляющий сжатие или распаковку, устройство представляющее файл, и соединяем их в буфере. После этого можно используя утилиту copy из Boost Iostreams копировать содержимое другого потока в буфер или наоборот.

Если вместо буфера (filtering_streambuf) использовать поток (filtering_stream), то писать или читать из него можно используя интерфейс потоков C++.

Устройства в Boost Iostream могут быть источниками (Source) или приемниками (Sink). Соответственно одни могут поставлять данные по требованию откуда либо, например из файла, а другие наоборот данные только куда то отправляют. Причем вы можете написать свой приемник или источник (это несложно) и создать свое устройство реализующее необходимую абстракцию. Например socket_source и socket_sink позволяющие работать с сокетами, pipe_source и pipe_sink тоже самое для именованных каналов или вообще null_sink (который кстати в Boost уже есть), которое просто получив данные тут же о них забывает. Причем соединив, скажем socket_source с фильтром boost::iostreams::bzip2_compressor вы получите передачу через сокеты сжатых данных, а дописав еще шифрующий фильтр можно получить передачу зашифрованных сжатых данных.

Сборка

Чтобы собрать ваш проект с Boost и одной из библиотек сжатия нужно сделать три вещи:

  1. Собрать библиотеку zlib или bzip2/libzip2 и убедиться, что ваша система сборки увидит путь к заголовочным файлам и собранным библиотекам. Кстати на большинстве Unix систем об этом можно не задумываться, так как скорее всего библиотека уже установлена.
  2. Установить при сборке проекта переменную компиляции BOOST_IOSTREAMS_NO_LIB
  3. Включить в сборку один из файлов из Boost (в версии 1.41.0 они находятся в libs/iostreams/src) в зависимости от используемого метода:
    • zlib – zlib.cpp
    • gzip – gzip.cpp
    • bzip2 – bzip2.cpp

Сравнение методов сжатия zlib/gzip/bzip2

Увлекшись я написал небольшой тест для сравнения описываемых методов. Вот результаты запуска программы:

Бинарный файл со случайными числами

Размер: 5 242 880 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 0.20 0.15 5 053 306
gzip 0.18 0.15 5 053 318
bzip2 0.59 0.77 5 049 795

 

Текстовой файл (несколько мануалов Linux слитые в один файл)

Размер: 3 534 333 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 0.28 0.07 1 050 711
gzip 0.36 0.08 1 050 723
bzip2 0.43 0.24 856 572

 

Исполняемый файл

Размер: 4 568 332 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 1.51 0.10 1 636 939
gzip 1.61 0.10 1 636 951
bzip2 0.87 0.36 1 439 625

 

Тест конечно не претендует на серьезное исследование, но определенные выводы сделать позволяет:

  • bzip2 сжимает лучше, но при этом значительно медленнее.
  • zlib и gzip практически одинаковы. Это правда так, так как они используют один алгоритм, единственное отличие в том, что zlib оптимизирован для сжатия потоковых данных, например из-за этой особенности он используется в протоколе HTTP/1.1, в то время как gz применяется для сжатия файлов.

Файлы к заметке

По традиции вы можете скачать исходный код тестового проекта. Архив содержит код библиотек и файлы для сборки с CMake, таким образом вам нужно только иметь установленный Boost, Cmake и положить в директорию data проекта свои тестовые данные.

7th Март 2010
22:59

Рубрика: C++,Разработка

Метки:

Оставить комментарий