Subversion Repositories spk

Rev

Blame | Last modification | View Log | RSS feed

/*
  these classes define behaviour of bob_dom_o*filestream and
  bob_dom_i*filestream on windows filesystem

  I also wanted to write streams for x2 filesystem (x2fd library) but they
  don't exist now
*/

#ifndef BOB_REALFILE_STREAM_INCLUDED
#define BOB_REALFILE_STREAM_INCLUDED

#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>

/*
        bob_dom_obinaryrealfile - output binary stream (real filesystem files)
        bob_dom_otextrealfile - output text stream (real filesystem files)
*/

template <class Ty>
class owinfilestream : public Ty
{
        private:
                static const size_t buffer_size = 4096;

                int m_fd;
                unsigned char *m_buffer;
                unsigned char *m_bufferPos;
                unsigned char *m_bufferEnd;

                size_t availBuffer() const { return m_bufferEnd - m_bufferPos; }

        public:
                typedef Ty base;
                typedef typename base::state state;
                typedef typename base::size_type size_type;
                typedef typename base::const_value_type_ptr const_value_type_ptr;
                typedef typename const base::value_type const_value_type;

                owinfilestream() 
                { 
                  m_buffer = new unsigned char[buffer_size];
                  m_fd = -1; 
                  m_bufferPos = m_buffer; 
                  m_bufferEnd = m_buffer + buffer_size; 
                  clear();
                }
                
                ~owinfilestream() 
                { 
                  close();
                  delete[] m_buffer;
                }

                static size_type bufferSize() { return buffer_size; }

                void clear(state s = goodbit) { base::clear(s); setstate(m_fd != -1 ? goodbit : badbit); }

                virtual bool open(const char *fileName, filestream::mode m)
                {
                        int f = 0, cf = 0;

                        name(fileName);

                        if((m & filestream::rdonly && m & filestream::create) || (m & filestream::rdonly && m & filestream::rdwr)){
                                clear(failbit);
                                return false;
                        }
                        if(m & filestream::rdonly)
                                f |= _O_RDONLY;
                        else if(m & filestream::rdwr)
                                f |= _O_RDWR;

                        if(m & filestream::create)
                                f |= _O_CREAT | _O_RDWR;

                        m_fd = ::_open(fileName, _O_BINARY | f, _S_IREAD | _S_IWRITE);
                        if(m & filestream::seekeof)
                                _lseek(m_fd, 0, SEEK_END);

                        clear();
                        return m_fd != -1;
                }

                virtual void close()
                {
                  if(m_fd == -1) return;
                  
                        flush();
                        _chsize(m_fd, ::_tell(m_fd));
                        ::_close(m_fd);
                        m_fd = -1;
                        setstate(badbit);
                }

                void flush()
                {
                        if(!good()) return;
                        size_t s = m_bufferPos - m_buffer;
                        if(s == 0) return;
                        if(::_write(m_fd, m_buffer, (unsigned int)s) != (unsigned int)s)
                                setstate(failbit);
                        else
                                m_bufferPos = m_buffer;
                }

                virtual size_type write(const_value_type_ptr data, size_t size)
                {
                        if(!good()) return -1;

                        size_type res = 0;
                        if(size > availBuffer())
                                flush();

                        if(size > availBuffer()){
                                res = ::_write(m_fd, data, (unsigned int)size);
                                if(res != size)
                                        setstate(failbit);
                        }
                        else {
                                memcpy(m_bufferPos, data, size);
                                m_bufferPos += size;
                                res = size;
                        }
                        return res;
                }

                virtual bool put(const_value_type& ch)
                {
                        return write(&ch, 1) > 0;
                }

                virtual size_t tell()
                {
                        size_t pos = ::_tell(m_fd);
                        if(pos >= 0) pos += m_bufferPos - m_buffer;
                        return pos;
                }

                virtual size_t seek(int offset, mystream::stream_base::seek_type origin)
                {
                        int o;
                        size_t res;
                        flush();

                        switch(origin){
                                case mystream::stream_base::seek_begin:
                                        o = SEEK_SET;
                                        break;
                                case mystream::stream_base::seek_end:
                                        o = SEEK_END;
                                        break;
                                case mystream::stream_base::seek_current:
                                        o = SEEK_CUR;
                                        break;
                                default:
                                        o = -1;
                        }
                        res = _lseek(m_fd, offset, o);

                        return res;
                }
};

typedef owinfilestream<obinaryfilestream> obinaryrealfile;
typedef owinfilestream<otextfilestream> otextrealfile;

class iwinfilestream : public ibinaryfilestream
{
        public:
                typedef ibinaryfilestream base;
                typedef base::value_type_ptr value_type_ptr;
                typedef base::value_type value_type;
                typedef base::size_type size_type;
                typedef base::state state;
                typedef base::offset_type offset_type;

        private:
                static const size_t m_bufferSize=5000;
                //static const size_t m_bufferSize=8;

                int m_fd;

                value_type_ptr m_buffer;
                const_value_type_ptr m_bufferEnd;
                const_value_type_ptr m_bufferPos;

                offset_type m_lineEnd;

                bool m_bBufferEOF;

        public:
                static size_type bufferSize() { return m_bufferSize; }

        private:
                size_type bufferAvail() const { return m_bufferEnd - m_bufferPos; }

                size_t fillBuffer()
                {
                        size_t r=(size_t)::_read(m_fd, m_buffer, m_bufferSize);
                        m_bufferEnd=m_buffer + r; // set buffer end
                        m_bufferPos=m_buffer; // buffer pos to beginning
                        m_bBufferEOF=::_eof(m_fd)!=0; // is this the last buffer?
                        return r;
                }

        public:
                iwinfilestream()
                {
                        m_fd=-1; clear(badbit);
                        m_buffer=new value_type[m_bufferSize];
                }

                ~iwinfilestream() { close(); delete m_buffer; }

                void clear(state s=goodbit) { base::clear(s); setstate(m_fd!=-1 ? goodbit : badbit); }

                bool open(const char *fileName, filestream::mode m)
                {
                        int f=0, cf=0;

                        name(fileName);

                        if((m & filestream::rdonly && m & filestream::create) || (m & filestream::rdonly && m & filestream::rdwr)){
                                clear(failbit);
                                return false;
                        }
                        if(m & filestream::rdonly)
                                f|=_O_RDONLY;
                        else if(m & filestream::rdwr)
                                f|=_O_RDWR;

                        if(m & filestream::create)
                                f|=_O_CREAT | _O_RDWR;

                        m_fd=::_open(fileName, _O_BINARY | f, _S_IREAD | _S_IWRITE);
                        clear();

                        if(m & filestream::seekeof)
                                _lseek(m_fd, 0, SEEK_END);

                        m_bufferPos = m_bufferEnd = m_buffer + m_bufferSize;
                        m_bBufferEOF=false;

                        m_lineEnd=0;

                        return m_fd!=-1;
                }

                void close()
                {
                  if(m_fd == -1) return;
                  
                        ::_close(m_fd);
                        m_fd=-1;
                        setstate(badbit);
                }

                bool advance(offset_type off)
                {
                        // offset is in our buffer, just shift the buffer pos
                        if((m_bufferPos + off >= m_buffer) && (m_bufferPos + off <= m_bufferEnd)){
                                m_bufferPos+=off;
                                // our buffer is the last one, if we are at its end we are at eof
                                if(m_bBufferEOF && m_bufferPos==m_bufferEnd)
                                        setstate(eofbit);
                                else
                                        clear((state)(rdstate() & ~eofbit));
                                return true;
                        }
                        // offset is outside our buffer, do a file seek
                        else{
                                // file offset is always at end of our buffer but buffer offset is different
                                // we must adjust the offset to lseek by difference between end of buffer and buffer pos
                                int res=::_lseek(m_fd, off - (long)(m_bufferEnd - m_bufferPos), SEEK_CUR);
                                if(res==-1)
                                        setstate(failbit);
                                else
                                        m_bufferPos=m_bufferEnd; // "erase" the buffer

                                if(::_eof(m_fd))
                                        setstate(eofbit);
                                else
                                        clear((state)(rdstate() & ~eofbit));

                                return res!=-1;
                        }
                }

                size_t tell() const
                {
                        return ::_tell(m_fd) - (m_bufferEnd - m_bufferPos);
                }

                size_type read(value_type_ptr buffer, size_type size)
                {
                        if(eof() && size > 0) setstate(failbit);
                        if(!good()) return -1;

                        // copy everything from our buffer to the out buffer
                        size_t readed=0;
                        size_t s=bufferAvail();
                        if(size < s) s=size;

                        memcpy(buffer, m_bufferPos, s);
                        advance((offset_type)s);
                        size-=s;
                        readed=s;

                        // our buffer doesn't have all the required data - we must read more
                        if(size){
                                // our buffer is not large enough - read the data directly
                                if(size > m_bufferSize){
                                        readed+=(size_t)::_read(m_fd, buffer + s, (unsigned int)size);
                                        m_bufferPos=m_bufferEnd; // this means that our buffer is empty
                                        if(::_eof(m_fd))
                                                setstate(eofbit);
                                        else
                                                clear((state)(rdstate() & ~eofbit));
                                }
                                // read the data to our buffer and copy the required size to out buffer
                                else{
                                        size_t r=fillBuffer();
                                        if(size < r) r=size; // copy the missing data to out buffer
                                        memcpy(buffer + readed, m_bufferPos, r);
                                        readed+=r;
                                        advance((offset_type) r);
                                }
                        }
                        return readed;
                }

                value_type_ptr readln()
                {
                        value_type_ptr buff=0;
                        value_type ch, old;
                        int size;

                        if(!good()) return 0;

                        while(!mystream::stream_base::eof()){
                                if(bufferAvail()==0)
                                        fillBuffer();

                                // 0xD might be followed by 0xA
                                ch=old=*m_bufferPos++;
                                if(ch==0xD || ch==0xA){
                                        size=(offset_type)tell() - m_lineEnd;
                                        advance(-size);
                                        buff=new value_type[size];
                                        read(buff, size);
                                        buff[size-1]=0; // to overwrite line end (0xD or 0xA)

                                        advance(1);
                                        m_lineEnd=(offset_type)tell();

                                        if(ch==0xD){
                                                // we are not at eof
                                                if(!eof()){
                                                        if(bufferAvail()==0) fillBuffer();
                                                        if(*m_bufferPos==0xA){
                                                                advance(1);
                                                                m_lineEnd++;
                                                        }
                                                }
                                        }
                                        break;
                                }
                        }
                        if(buff==0){
                                size=(offset_type)tell() - m_lineEnd;
                                advance(-size);
                                buff=new value_type[size + 1];
                                read(buff, size);
                                buff[size]=0;
                        }

                        return buff;
                }

                value_type_ptr readln2()
                {
                        if(!good()) return 0;

                        if(bufferAvail()==0) fillBuffer();
                        /*
                        value_type *pos;
                        for(pos=m_bufferPos; pos < m_bufferEnd; ++pos){
                                if(*pos==0xD || 0xA){
                                        newLineEnd=tell() + pos;

                                        lseek(m_fd, m_lineEnd, SEEK_SET);
                                        ::read
                                }
                        }*/
                }
};

typedef iwinfilestream ibinaryrealfile;

#endif // !defined(BOB_REALFILE_STREAM_INCLUDED)