Вот такую интересную фигню только что обнаружил.
Писал конвертор данных в base64 и обратно вот код:
typedef boost::archive::iterators::istream_iterator<char> boost_istream_iterator;
typedef boost::archive::iterators::ostream_iterator<char> boost_ostream_iterator;
typedef boost::archive::iterators::insert_linebreaks<
boost::archive::iterators::base64_from_binary<
boost::archive::iterators::transform_width<boost_istream_iterator, 6, 8>
>, 76
> bin_to_base64;
typedef boost::archive::iterators::transform_width<
boost::archive::iterators::binary_from_base64<
boost::archive::iterators::remove_whitespace<boost_istream_iterator>
>, 8, 6
> base64_to_bin;
void test()
{
{
//фигня заключается вот в чём:
//если файлом для конвертации будет выступать любой бинарный или тектосвый файл, всё будет работать как надо
//а вот если будет файл zip формата
std::ifstream ifs("test.zip", std::ios_base::in|std::ios_base::binary);
std::ofstream ofs("test.arc", std::ios_base::out|std::ios_base::binary);
std::copy(
bin_to_base64(boost_istream_iterator(ifs >> std::noskipws)),
bin_to_base64(boost_istream_iterator()),
boost_ostream_iterator(ofs)
);
}
{
std::ifstream ifs("test.arc", std::ios_base::in|std::ios_base::binary);
std::ofstream ofs("test.rez", std::ios_base::out|std::ios_base::binary);
//то вот тут получим в конце неправильную конвертацию с выбросом исключения
//dataflow_exception::invalid_base64_character
std::copy(
base64_to_bin(boost_istream_iterator(ifs >> std::noskipws)),
base64_to_bin(boost_istream_iterator()),
boost_ostream_iterator(ofs)
);
}
}
Как объяснить такую фигню, Я пока незнаю. Буду копать.
Брал сторонний обычный конвертор в base64 и смотрел для всех файлов результат что через boost что через сторонний конвертор совпадает.
А вот для zip файлов отличие оказывается только в последнем байте.
1) Если вхожным файлом будет файл ZIP формата, то мало того что мы получим assert из недр STL-я:
_DEBUG_ERROR("istreambuf_iterator is not dereferencable");
в первом и во втором случае использования, так мы ещё и получим искаженный выходной файл "test.rez" который будет на 1-байт длиннее.
А вот если входным файлом будет любой другой файл, то всё будет OK. Бред какой то млять...
Здравствуйте, nen777w, Вы писали:
N>А вот для zip файлов отличие оказывается только в последнем байте.
Вполне возможен баг в бустовом кодировщике. Точнее, баг в обработке последнего неполного триплета.
Попробуй покодировать файлы размером 1, 2 и 3 байта. Посмотри что из этого получится.
__________
16.There is no cause so right that one cannot find a fool following it.
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Здравствуйте, nen777w, Вы писали:
N>>А вот для zip файлов отличие оказывается только в последнем байте. DEA>Вполне возможен баг в бустовом кодировщике. Точнее, баг в обработке последнего неполного триплета. DEA>Попробуй покодировать файлы размером 1, 2 и 3 байта. Посмотри что из этого получится.
Да. Так и есть, 1,2 — глючит 3 уже нет, 4-ре опять глючит.
Ушёл читать об алгоритме base64. В boost багу уже накатал.
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Здравствуйте, nen777w, Вы писали:
N>>А вот для zip файлов отличие оказывается только в последнем байте. DEA>Вполне возможен баг в бустовом кодировщике. Точнее, баг в обработке последнего неполного триплета. DEA>Попробуй покодировать файлы размером 1, 2 и 3 байта. Посмотри что из этого получится.
Короче подебажил немного. Выяснил в чём фигня.
Фигня в том что ни один из
boost::archive::iterators::istream_iterator<char>
или
std::istreambuf_iterator<char>
не подходят для случая использования совместно с
boost::archive::iterators::transform_width
Если допустим имеем файл состоящий из 2-х байтов 12
То в случае использования
boost::archive::iterators::istream_iterator<char>
и достижения конца файла при попытке инкремента итератора ifstream вернёт -1 и это значение будет использовано в
boost::archive::iterators::transform_width метод fill()
или в сл. использования:
std::istreambuf_iterator<char>
и достижения конца файла при попытке инкремента итератора в
boost::archive::iterators::transform_width метод fill()
будет использовано предидущее значение т.е. 2
Что тоже не верно.
Что бы всё работало правильно нужно что бы возвращался 0. Короче прийдётся написать свой итератор для такого случая. Или порыться в boost может там есть такой уже а Я просто не заметил.
Это Я пока нарыл в одну сторону bin->base64 в другую сторону немного по другому там конвертирующий итератор base64->bin работает первым, а потом только происходит преобразование и вот там фигня получается.
Там по идее при достижении конца файла надо вернуть 65 что бы по той таблице обратной конвертации в binary_from_base64.hpp в transform_width вернулся 0...
но фигня в том что триплет не полный и transform_width инкрементирует этот итератор два раза что бы дополнить а так как снаружи std::copy
то запретить писать в поток никак не получится в результате получим ещё один дополнительный байт со значением 0... (это для того что бы исключение boost не выбросил об невалидном base64 символе).
Получается нужно писать ещё один специальны итератор, либо менят таблицу в binary_from_base64.hpp
так что бы в позиции 0 была не -1 а 0
Корроче лажа... ещё втом надо получается писать какой то свой copy и перед тем как писать с одного стрима в другой, надо проверить а не находится ли уже тот в стрим !good() состоянии...
Блин....
Вот так вот.
Выдержка из boost/archive/iterators/transform_width.hpp:
// ...Be very careful
// when using and end iterator. end is only reliable detected when the input
// stream length is some common multiple of x and y. E.G. Base64 6 bit
// character and 8 bit bytes. Lowest common multiple is 24 => 4 6 bit characters
// or 3 8 bit characters
Т.е.: входной поток должен быть выравнен к общему делителю (на 3 байта, в случае base64).