исключения из boost::iostreams::copy()

В приведенном ниже коде у меня есть поврежденный «hello.bz2», в котором есть случайные символы за пределами EOF.

Есть ли способ сделать вызов boost::iostreams::copy() для выброса?

#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>

int main() 
{
    using namespace std;
    using namespace boost::iostreams;

    ifstream file("hello.bz2", ios_base::in | ios_base::binary);
    filtering_streambuf<input> in;
    in.push(bzip2_decompressor());
    in.push(file);
    boost::iostreams::copy(in, cout);
}

РЕДАКТИРОВАТЬ: пожалуйста, игнорируйте строку, которая до сих пор привлекала наибольшее внимание; ЕОФ. Предположим, вы работаете с поврежденным файлом bzip2. Я использовал «EOF», предполагая ошибку, которую я получил, когда запускаю bzcat в файле.

bzcat hello.bz2
hello world

bzcat: hello.bz2: trailing garbage after EOF ignored

person ϹοδεMεδιϲ    schedule 02.07.2010    source источник
comment
Что вы имеете в виду под ЭОФ? Какой-то маркер?   -  person    schedule 02.07.2010
comment
@Neil, пожалуйста, игнорируйте часть EOF ... предположим, что файл поврежден.   -  person ϹοδεMεδιϲ    schedule 02.07.2010


Ответы (2)


Исследовательская работа

std::ios_base::failure — это «базовый класс для типов всех объектов, выбрасываемых в качестве исключений функциями в библиотеке Iostreams, для сообщения об ошибках, обнаруженных во время операций буфера потока».

Глядя на документы boost:

class bzip2_error : public std::ios_base::failure {
public:
    bzip2_error(int error);
    int error() const;
};

bzip2_error — это особое исключение, возникающее при использовании фильтра bzip2, который наследуется от std::ios_base::failure. Как видите, он строится путем передачи целого числа, представляющего код ошибки. У него также есть метод error(), который возвращает код ошибки, с которым он был создан.
В документации коды ошибок bzip2 перечислены следующим образом:

  • data_error — указывает, что сжатый поток данных поврежден. Равно BZ_DATA_ERROR.
  • data_error_magic — указывает, что сжатый поток данных не начинается с «волшебной» последовательности «B», «Z», «h». Равен BZ_DATA_ERROR_MAGIC.
  • config_error — указывает, что libbzip2 неправильно настроен для текущей платформы. Равен BZ_CONFIG_ERROR.

Код

РЕДАКТИРОВАТЬ Я также хочу уточнить, что boost::iostreams::copy() будет вызывать здесь исключение не boost::iostreams::copy(), а фильтр bzip2. Только iostream или фильтры будут генерировать исключения, копировать только использует iostream/filter, что может привести к тому, что iostream/filter выдаст исключение.

**EDIT 2 ** Похоже, проблема связана с bzip2_decompressor_impl, как вы и ожидали. Я воспроизвел бесконечный цикл вращения, когда файл bz2 пуст. Мне потребовалось некоторое время, чтобы понять, как создать ускорение и связь с библиотекой bzip2, zlib и iostreams, чтобы увидеть, смогу ли я воспроизвести ваши результаты.

g++ test.cpp -lz -lbz2 boostinstall/boost/bin.v2/libs/iostreams/build/darwin-4.2.1/release/link-static/threading-multi/libboost_iostreams.a -Lboostinstall/boost/bin.v2/libs/ -Iboost/include/boost-1_42 -g

test.cpp:

#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>

int main()
{
    using namespace std;
    using namespace boost::iostreams;

    try {
        ifstream file("hello.bz2", ios_base::in | ios_base::binary);
        filtering_streambuf<input> in;
        in.push(bzip2_decompressor());
        in.push(file);
        boost::iostreams::copy(in, cout);
    }
    catch(const bzip2_error& exception) {
        int error = exception.error();

        if(error == boost::iostreams::bzip2::data_error) {
            // compressed data stream is corrupted
            cout << "compressed data stream is corrupted";
        }
        else if(error == boost::iostreams::bzip2::data_error_magic)
        {
            // compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'
            cout << "compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'";
        }
        else if(boost::iostreams::bzip2::config_error) {
            // libbzip2 has been improperly configured for the current platform
            cout << "libbzip2 has been improperly configured for the current platform";
        }
    }
}

отладка:

gdb a.out
(gdb) b bzip2.hpp:344

Существует цикл, который запускает распаковку bzip2 в symmetric.hpp:109 :

        while (true)
        {
            // Invoke filter if there are unconsumed characters in buffer or if
            // filter must be flushed.
            bool flush = status == f_eof;
            if (buf.ptr() != buf.eptr() || flush) {
                const char_type* next = buf.ptr();
                bool done =
                    !filter().filter(next, buf.eptr(), next_s, end_s, flush);
                buf.ptr() = buf.data() + (next - buf.data());
                if (done)
                    return detail::check_eof(
                               static_cast<std::streamsize>(next_s - s)
                           );
            }

            // If no more characters are available without blocking, or
            // if read request has been satisfied, return.
            if ( (status == f_would_block && buf.ptr() == buf.eptr()) ||
                 next_s == end_s )
            {
                return static_cast<std::streamsize>(next_s - s);
            }

            // Fill buffer.
            if (status == f_good)
                status = fill(src);
        }

Метод фильтра bzip2_decompressor_impl bzip2.hpp:344 вызывается для симметричного.hpp:117 :

template<typename Alloc>
bool bzip2_decompressor_impl<Alloc>::filter
    ( const char*& src_begin, const char* src_end,
      char*& dest_begin, char* dest_end, bool /* flush */ )
{
    if (!ready())
        init();
    if (eof_)
        return false;
    before(src_begin, src_end, dest_begin, dest_end);
    int result = decompress();
    after(src_begin, dest_begin);
    bzip2_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result);
    return !(eof_ = result == bzip2::stream_end);
}

Я думаю, что проблема проста, флаг eof_ в bzip2_decompressor_impl никогда не устанавливается. Если это не произойдет каким-то волшебным образом, которого я не понимаю, он принадлежит классу bzip2_decompressor_impl, и для него всегда устанавливается значение false. Итак, когда мы делаем это:

cat /dev/null > hello.bz2

Мы получаем вращающийся цикл, который никогда не заканчивается, мы не прерываемся при попадании в EOF. Это, безусловно, ошибка, потому что другие программы (например, vim) без проблем откроют текстовый файл, созданный аналогичным образом. Однако я могу заставить фильтр выбрасывать, когда файл bz2 "поврежден":

echo "other corrupt" > hello.bz2
./a.out
compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'

Иногда вы должны воспринимать открытый исходный код с долей скептицизма. Будет более вероятно, что ваши bz2 будут испорчены и правильно брошены. Однако случай /dev/null является серьезной ошибкой. Мы должны отправить его разработчику Boost, чтобы они могли это исправить.

person manifest    schedule 02.07.2010
comment
@manifest Большое спасибо за подробный ответ. Но, к сожалению, в моем конкретном случае это не помогло. Не уверен, что происходит под капотом. Кажется, что-то подозрительное в boost iostreams и его механизме фильтрации. Может быть, это просто новый участник, функциональность bzip2. Одна вещь, которую я заметил, заключалась в том, что механизм фильтрации не может понять, что происходит что-то не так, если я загружаю фильтр с искаженным файлом bz2. Проще всего, если в приведенном выше фрагменте кода создать пустой hello.bz2 и запустить его. вы увидите, как загрузка процессора подскочит до 99%?!? - person ϹοδεMεδιϲ; 05.07.2010
comment
под пустым hello.bz2 я подразумеваю кота /dev/null › hello.bz2 - person ϹοδεMεδιϲ; 05.07.2010
comment
См. редактирование, я считаю, что есть ошибка с реализацией фильтра bzip2. - person manifest; 06.07.2010
comment
Думая о том, чтобы отправить сообщение об ошибке, я проверил повышение из ствола. Код фильтра bzip2 полностью отличается, поэтому они, возможно, исправили его. Обновлю здесь позже - person manifest; 06.07.2010
comment
Когда я проверил магистраль, заглушил ее и перелинковал cat /dev/null › hello.bz2, то ./a.out приводит к тому, что libbzip2 был неправильно настроен для текущей платформы. Хотя это может быть и не исключение, которого я ожидал, по крайней мере, это исключение, а не бесконечное вращение. Правильно созданный файл .bz2 по-прежнему будет работать, а поврежденный файл вызывает исключение магической последовательности. Я бы порекомендовал убедиться, что вы ссылаетесь на последние библиотеки. - person manifest; 06.07.2010
comment
Если кто-то захочет обойти эту проблему в boost 1.43; получить исходный код iostreams из ветки релиза и скомпилировать с помощью define=BOOST_IOSTREAMS_USE_DEPRECATED. Регрессионные тесты могут не пройти; но вы можете заменить libs/iostreams/test/detail/temp_file.hpp на один из 1.43. - person ϹοδεMεδιϲ; 11.07.2010

Как у вас есть случайные символы после конца файла?

Если вы имеете в виду, что в файле есть мусорные данные, как алгоритм декомпрессии сможет определить, являются ли данные мусором, чтобы принять решение throw?

person Mark B    schedule 02.07.2010
comment
Вот что я вижу, когда запускаю bzcat для файла. bzcat: hello.bz2: мусор после игнорирования EOF Честно говоря, меня больше интересует способ захвата ошибки из декомпрессора. - person ϹοδεMεδιϲ; 02.07.2010