boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 21.06.11 12:15
Оценка:
Вот такую интересную фигню только что обнаружил.
Писал конвертор данных в 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 файлов отличие оказывается только в последнем байте.


22.06.11 23:11: Перенесено из 'C/C++'
Re: boost base64->bin, bin->base64 (интересная фигня)
От: Centaur Россия  
Дата: 21.06.11 16:34
Оценка:
Здравствуйте, nen777w, Вы писали:

N>typedef boost::archive::iterators::istream_iterator<char> boost_istream_iterator;
N>typedef boost::archive::iterators::ostream_iterator<char> boost_ostream_iterator;


Продолжаю настаивать на istreambuf_iterator.
Re[2]: boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 22.06.11 13:17
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, nen777w, Вы писали:


C>
N>>typedef boost::archive::iterators::istream_iterator<char> boost_istream_iterator;
N>>typedef boost::archive::iterators::ostream_iterator<char> boost_ostream_iterator;
C>


C>Продолжаю настаивать на istreambuf_iterator.


Попробовал и так.
Я вам скажу мистики стало ещё больше.
И как это можно объяснить???

  "Вот код"

#include "boost/archive/iterators/base64_from_binary.hpp"
#include "boost/archive/iterators/binary_from_base64.hpp"
#include "boost/archive/iterators/transform_width.hpp"

//typedefs
typedef  std::istreambuf_iterator<char>    my_istream_iterator;
typedef  std::ostreambuf_iterator<char>    my_ostream_iterator;

typedef boost::archive::iterators::base64_from_binary<
          boost::archive::iterators::transform_width< my_istream_iterator, 6, 8>
> bin_to_base64;

typedef boost::archive::iterators::transform_width<
    boost::archive::iterators::binary_from_base64< my_istream_iterator >, 8, 6
> base64_to_bin;

void test()
{
   {
        //ВХОДНОЙ ФАЙЛ!!!
    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( my_istream_iterator(ifs >> std::noskipws) ),
        bin_to_base64( my_istream_iterator() ),
        my_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);

    std::copy(
        base64_to_bin( my_istream_iterator(ifs >> std::noskipws) ),
        base64_to_bin( my_istream_iterator() ),
        my_ostream_iterator(ofs)
    );
  }
}


Вот теперь как это можно объяснить:

1) Если вхожным файлом будет файл ZIP формата, то мало того что мы получим assert из недр STL-я:

_DEBUG_ERROR("istreambuf_iterator is not dereferencable");

в первом и во втором случае использования, так мы ещё и получим искаженный выходной файл "test.rez" который будет на 1-байт длиннее.
А вот если входным файлом будет любой другой файл, то всё будет OK. Бред какой то млять...

Кто нибудь может проверить это у себя?
Re[3]: boost base64->bin, bin->base64 (интересная фигня)
От: tasko  
Дата: 22.06.11 14:41
Оценка:
Сразу же извиняюсь за оффтоп
http://habrahabr.ru/blogs/crazydev/119676/

ps одна и таже тематика — zip не так прост!
Re: boost base64->bin, bin->base64 (интересная фигня)
От: 0xDEADBEEF Ниоткуда  
Дата: 23.06.11 08:46
Оценка: +2
Здравствуйте, nen777w, Вы писали:

N>А вот для zip файлов отличие оказывается только в последнем байте.

Вполне возможен баг в бустовом кодировщике. Точнее, баг в обработке последнего неполного триплета.
Попробуй покодировать файлы размером 1, 2 и 3 байта. Посмотри что из этого получится.
__________
16.There is no cause so right that one cannot find a fool following it.
Re[2]: boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 23.06.11 10:20
Оценка:
Здравствуйте, 0xDEADBEEF, Вы писали:

DEA>Здравствуйте, nen777w, Вы писали:


N>>А вот для zip файлов отличие оказывается только в последнем байте.

DEA>Вполне возможен баг в бустовом кодировщике. Точнее, баг в обработке последнего неполного триплета.
DEA>Попробуй покодировать файлы размером 1, 2 и 3 байта. Посмотри что из этого получится.

Да. Так и есть, 1,2 — глючит 3 уже нет, 4-ре опять глючит.
Ушёл читать об алгоритме base64. В boost багу уже накатал.
Re[2]: boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 23.06.11 11:55
Оценка:
Здравствуйте, 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() состоянии...
Блин....
Вот так вот.
Re: boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 23.06.11 13:51
Оценка: 2 (1)
Короче кому будет нужно или интересно.
Публикую "правильные" итераторы для работы с:

boost::archive::iterators::base64_from_binary
boost::archive::iterators::binary_from_base64
boost::archive::iterators::transform_width

  "Файл base64.h"
#ifndef __base64_h__
#define __base64_h__

#include <cstddef> // NULL
#include <istream>
#include <boost/iterator/iterator_facade.hpp>

namespace boost { 
    namespace archive {
        namespace iterators {

            // given a type, make an input iterator based on a pointer to that type
            template<class Elem = char>
            class istream_iterator_bin_to_base64 :  
                public boost::iterator_facade<
                            istream_iterator_bin_to_base64<Elem>,
                            Elem,
                            std::input_iterator_tag,
                            Elem
                >
            {
                friend class boost::iterator_core_access;
                typedef istream_iterator_bin_to_base64 this_t ;
                typedef BOOST_DEDUCED_TYPENAME boost::iterator_facade<
                    istream_iterator_bin_to_base64<Elem>,
                    Elem,
                    std::input_iterator_tag,
                    Elem
                > super_t;
                typedef BOOST_DEDUCED_TYPENAME std::basic_istream<Elem> istream_type;

                //Access the value referred to 
                Elem dereference() const {
                    return m_current_value;
                }

                bool equal(const this_t & rhs) const {
                    // note: only  works for comparison against end of stream
                    return m_istream == rhs.m_istream;
                }

                void increment(){
                    if(NULL != m_istream){
                        m_current_value = static_cast<Elem>(m_istream->get());
                        if(! m_istream->good()){
                            const_cast<this_t *>(this)->m_istream = NULL;
                            m_current_value = 0;
                        }
                    }
                }

                istream_type *m_istream;
                Elem m_current_value;
            public:
                istream_iterator_bin_to_base64(istream_type & is) :
                  m_istream(& is)
                  {
                      increment();
                  }

                  istream_iterator_bin_to_base64() :
                  m_istream(NULL)
                  {}

                  istream_iterator_bin_to_base64(const istream_iterator_bin_to_base64<Elem> & rhs) :
                  m_istream(rhs.m_istream),
                      m_current_value(rhs.m_current_value)
                  {}

            };

            // given a type, make an input iterator based on a pointer to that type
            template<class Elem = char>
            class istream_iterator_base64_to_bin :  
                            public boost::iterator_facade<
                            istream_iterator_base64_to_bin<Elem>,
                            Elem,
                            std::input_iterator_tag,
                            Elem
                >
            {
                friend class boost::iterator_core_access;
                typedef istream_iterator_base64_to_bin this_t ;
                typedef BOOST_DEDUCED_TYPENAME boost::iterator_facade<
                    istream_iterator_base64_to_bin<Elem>,
                    Elem,
                    std::input_iterator_tag,
                    Elem
                > super_t;
                typedef BOOST_DEDUCED_TYPENAME std::basic_istream<Elem> istream_type;

                //Access the value referred to 
                Elem dereference() const {
                    return m_current_value;
                }

                bool equal(const this_t & rhs) const {
                    // note: only  works for comparison against end of stream
                    return m_istream == rhs.m_istream;
                }

                void increment(){
                    if(NULL != m_istream){
                        m_current_value = static_cast<Elem>(m_istream->get());
                        if(! m_istream->good()){
                            const_cast<this_t *>(this)->m_istream = NULL;
                            m_current_value = 65;
                        }
                    }
                }

                istream_type *m_istream;
                Elem m_current_value;
            public:
                  istream_iterator_base64_to_bin(istream_type & is) :
                  m_istream(& is)
                  {
                      increment();
                  }

                  istream_iterator_base64_to_bin() :
                  m_istream(NULL)
                  {}

                  istream_iterator_base64_to_bin(const istream_iterator_base64_to_bin<Elem> & rhs) :
                  m_istream(rhs.m_istream),
                      m_current_value(rhs.m_current_value)
                  {}

            };

            template<class InIt, class OutIt>
            inline void __CLRCALL_OR_CDECL copy_base64_to_bin(InIt First, InIt Last, OutIt Dest)
            {
                for (; First != Last; ++Dest, ++First)
                {
                    typename InIt::value_type val = *First;
                    if( First != Last )
                        *Dest = val;
                    else
                        break;
                }
            }

        } // namespace iterators
    } // namespace archive
} // namespace boost


#endif


И
  "пример использования"
#include "boost/archive/iterators/base64_from_binary.hpp"
#include "boost/archive/iterators/binary_from_base64.hpp"
#include "boost/archive/iterators/transform_width.hpp"

#include "base64.h"


//typedefs
typedef boost::archive::iterators::istream_iterator_bin_to_base64<char>        my_istream_iterator_bin_to_base64;
typedef boost::archive::iterators::istream_iterator_base64_to_bin<char>        my_istream_iterator_base64_to_bin;
typedef boost::archive::iterators::ostream_iterator<char> my_ostream_iterator;

typedef boost::archive::iterators::base64_from_binary<
        boost::archive::iterators::transform_width<my_istream_iterator_bin_to_base64, 6, 8>
    > bin_to_base64;

typedef boost::archive::iterators::transform_width<
    boost::archive::iterators::binary_from_base64< my_istream_iterator_base64_to_bin >, 8, 6
> base64_to_bin;


void test()
{
{
    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( my_istream_iterator_bin_to_base64(ifs >> std::noskipws) ),
        bin_to_base64( my_istream_iterator_bin_to_base64() ),
        my_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);

    boost::archive::iterators::copy_base64_to_bin(
        base64_to_bin( my_istream_iterator_base64_to_bin(ifs >> std::noskipws) ),
        base64_to_bin( my_istream_iterator_base64_to_bin() ),
        my_ostream_iterator(ofs)
    );
}
}


Почему это было сделано и для чего есть в этом топике.
boost base64 encode/decode fix
Re[2]: boost base64->bin, bin->base64 (интересная фигня)
От: nen777w  
Дата: 13.07.11 11:37
Оценка:
Обновляю исходник.
Привёл к человеческому виду. Добавил буферезацию потока. Изменил именна классов на:
istream_iterator_encode_to_base64
istream_iterator_decode_from_base64

  "Исходник"
#ifndef __base64_h__
#define __base64_h__

#include <cstddef> // NULL
#include <istream>
#include <xutility>
#include <memory.h>
#include "boost/iterator/iterator_facade.hpp"
#include "boost/shared_array.hpp"

namespace boost { namespace archive { namespace iterators {

        enum {
            prebuf_default_buffer_size = 5242880 /*5 Mb*/
        };

        // given a type, make an input iterator based on a pointer to that type
        template<class Elem = char, Elem eof_value = -1, long buffer_size = prebuf_default_buffer_size>
        class base64_istream_iterator_ :  
            public boost::iterator_facade<base64_istream_iterator_<Elem, eof_value, buffer_size>, Elem, std::input_iterator_tag, Elem>
        {
            friend class boost::iterator_core_access;

            typedef base64_istream_iterator_ this_t ;

            typedef BOOST_DEDUCED_TYPENAME boost::iterator_facade<
                base64_istream_iterator_<Elem, eof_value, buffer_size>,
                Elem,
                std::input_iterator_tag,
                Elem
            > super_t;

        protected:

            typedef BOOST_DEDUCED_TYPENAME std::basic_istream<Elem> istream_type;

            typedef BOOST_DEDUCED_TYPENAME boost::shared_array<Elem> shared_array;

            //Access the value referred to 
            Elem dereference() const {
                return m_current_value;
            }

            bool equal(const this_t & rhs) const {
                // note: only  works for comparison against end of stream
                return m_istream == rhs.m_istream;
            }

            bool pre_read() {

                assert( m_prebuf_pos >= m_prebuf_len );
                if( NULL != m_istream ) {
                    //calculate remain length of stream
                    istream_type::pos_type cur_pos = m_istream->tellg();
                    m_istream->seekg(-1, std::ios_base::end);
                    istream_type::pos_type end_pos = m_istream->tellg();
                    m_istream->seekg(cur_pos, std::ios_base::beg);
                    long remain = (long)(end_pos - cur_pos)+1;
                    assert(remain >= 0);
                    if( 0 == remain ) {
                        m_istream->get(); //eof
                        assert(! m_istream->good());
                        m_istream = NULL;
                        return false;
                    }
            
                    m_prebuf_len = min( remain, buffer_size );
                    m_istream->read( m_prebuf.get(), m_prebuf_len );
                    m_prebuf_pos = 0;
                    return true;
                }

                return false;
            }

            void increment() {

                ++m_prebuf_pos;
                if( m_prebuf_pos >= m_prebuf_len ) {
                    if( !pre_read() ) { 
                        m_current_value = eof_value; 
                        return; 
                    }
                }

                m_current_value = m_prebuf[m_prebuf_pos];
            }

        private:
            istream_type *m_istream;
            Elem          m_current_value;
            
            //pre-buffer
            shared_array    m_prebuf;
            //current pre-buffer length
            long        m_prebuf_len;
            //current position in pre-buffer
            long        m_prebuf_pos;
        public:
              base64_istream_iterator_(istream_type & is) 
                  : m_istream(& is)
                  , m_prebuf_len(0)
                  , m_prebuf_pos(0)
              {
                  m_prebuf.reset( new Elem[buffer_size] );
                  increment();
              }

              base64_istream_iterator_() 
                  : m_istream(NULL)
                  , m_prebuf_len(0)
                  , m_prebuf_pos(0)

              {}

              base64_istream_iterator_(const base64_istream_iterator_<Elem, eof_value, buffer_size> & rhs) 
                  : m_istream(rhs.m_istream)
                  , m_current_value(rhs.m_current_value)
                  , m_prebuf_len(rhs.m_prebuf_len)
                  , m_prebuf_pos(rhs.m_prebuf_pos)
                  , m_prebuf(rhs.m_prebuf)
              {}
        };

        //...........................................................................................................

        template<class Elem = char, long buffer_size = prebuf_default_buffer_size>
        class istream_iterator_encode_to_base64 : public base64_istream_iterator_<Elem, 0, buffer_size> 
        {            
            friend class boost::iterator_core_access;

            typedef base64_istream_iterator_<Elem, 0, buffer_size> base_;
            
        public:
            istream_iterator_encode_to_base64(BOOST_DEDUCED_TYPENAME base_::istream_type & is) 
                : base_(is)
            {}

            istream_iterator_encode_to_base64() 
                : base_()
            {}

            istream_iterator_encode_to_base64(const istream_iterator_encode_to_base64<Elem, buffer_size> & rhs) 
                : base_(rhs)
            {}
        };
        

        template<class Elem = char, long buffer_size = prebuf_default_buffer_size>
        class istream_iterator_decode_from_base64 : public base64_istream_iterator_<Elem, 65, buffer_size> 
        {
            friend class boost::iterator_core_access;

            typedef base64_istream_iterator_<Elem, 65, buffer_size> base_;
        
        public:
            istream_iterator_decode_from_base64(BOOST_DEDUCED_TYPENAME base_::istream_type & is) 
                : base_(is)
            {}

            istream_iterator_decode_from_base64() 
                : base_()
            {}

            istream_iterator_decode_from_base64(const istream_iterator_decode_from_base64<Elem, buffer_size> & rhs) 
                : base_(rhs)
            {}
        };

        //...........................................................................................................
        //...........................................................................................................


        template<class InIt, class OutIt>
        inline void __CLRCALL_OR_CDECL copy_base64_to_bin(InIt First, InIt Last, OutIt Dest)
        {
            for (; First != Last; ++Dest, ++First )
            {
                typename InIt::value_type val = *First;
                if( First != Last )
                    *Dest = val;
                else
                    break;
            }
        }
} } }

#endif


Жаль, но мои вопли по поводу этой баги boost-овциами небыли услышавны.
Возможно потому что плохо описал ошибку.
base64 boost::iterators
Re: boost base64->bin, bin->base64 (интересная фигня)
От: Аноним  
Дата: 26.08.11 10:54
Оценка:
Выдержка из 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).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.