]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/stream.cpp
Fix for GetValue
[wxWidgets.git] / src / common / stream.cpp
index 38499f5793a1634784944d78a99ecc813d137c18..957dd933fec1d796cb64a5959c8d08e8d33b81ef 100644 (file)
@@ -17,8 +17,9 @@
 // ----------------------------------------------------------------------------
 // headers
 // ----------------------------------------------------------------------------
+
 #ifdef __GNUG__
-#pragma implementation "stream.h"
+    #pragma implementation "stream.h"
 #endif
 
 // For compilers that support precompilation, includes "wx.h".
@@ -37,7 +38,8 @@
 #include <ctype.h>
 #include "wx/stream.h"
 #include "wx/datstrm.h"
-#include "wx/objstrm.h"
+#include "wx/textfile.h"
+#include "wx/log.h"
 
 // ----------------------------------------------------------------------------
 // constants
@@ -67,8 +69,8 @@ void wxStreamBuffer::InitBuffer()
     m_buffer_pos = NULL;
     m_buffer_size = 0;
 
-    // there is nothing to destroy anyhow
-    m_destroybuf = FALSE;
+    // if we are going to allocate the buffer, we should free it later as well
+    m_destroybuf = TRUE;
 }
 
 void wxStreamBuffer::Init()
@@ -80,6 +82,8 @@ void wxStreamBuffer::Init()
 
 wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
 {
+    Init();
+
     m_stream = &stream;
     m_mode = mode;
 
@@ -89,7 +93,16 @@ wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
 
 wxStreamBuffer::wxStreamBuffer(BufMode mode)
 {
-    m_stream = new wxStreamBase;
+    Init();
+
+    wxASSERT_MSG(mode != read_write, wxT("you have to use the other ctor for read_write mode") );
+    if ( mode == read )
+        m_stream = new wxInputStream;
+    else if ( mode == write)
+        m_stream = new wxOutputStream;
+    else
+        m_stream = NULL;
+
     m_mode = mode;
 
     m_flushable = FALSE;
@@ -129,6 +142,16 @@ wxStreamBuffer::~wxStreamBuffer()
         delete m_stream;
 }
 
+wxInputStream *wxStreamBuffer::GetInputStream() const
+{
+    return m_mode == write ? NULL : (wxInputStream *)m_stream;
+}
+
+wxOutputStream *wxStreamBuffer::GetOutputStream() const
+{
+    return m_mode == read ? NULL : (wxOutputStream *)m_stream;
+}
+
 void wxStreamBuffer::SetBufferIO(void *buffer_start,
                                  void *buffer_end,
                                  bool takeOwnership)
@@ -150,7 +173,7 @@ void wxStreamBuffer::SetBufferIO(void *start,
     m_buffer_size = len;
 
     // if we own it, we free it
-    m_destroybuf = !takeOwnership;
+    m_destroybuf = takeOwnership;
 
     ResetBuffer();
 }
@@ -185,9 +208,11 @@ void wxStreamBuffer::ResetBuffer()
 // fill the buffer with as much data as possible (only for read buffers)
 bool wxStreamBuffer::FillBuffer()
 {
-    wxCHECK_MSG( m_stream, FALSE, _T("should have a stream in wxStreamBuffer") );
+    wxInputStream *inStream = GetInputStream();
 
-    size_t count = m_stream->OnSysRead(m_buffer_start, m_buffer_size);
+    wxCHECK_MSG( inStream, FALSE, _T("should have a stream in wxStreamBuffer") );
+
+    size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size);
     if ( !count )
         return FALSE;
 
@@ -206,10 +231,12 @@ bool wxStreamBuffer::FlushBuffer()
     if ( m_buffer_pos == m_buffer_start )
         return FALSE;
 
-    wxCHECK_MSG( m_stream, FALSE, _T("should have a stream in wxStreamBuffer") );
+    wxOutputStream *outStream = GetOutputStream();
+
+    wxCHECK_MSG( outStream, FALSE, _T("should have a stream in wxStreamBuffer") );
 
     size_t current = m_buffer_pos - m_buffer_start;
-    size_t count = m_stream->OnSysWrite(m_buffer_start, current);
+    size_t count = outStream->OnSysWrite(m_buffer_start, current);
     if ( count != current )
         return FALSE;
 
@@ -281,12 +308,14 @@ void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size)
 
 void wxStreamBuffer::PutChar(char c)
 {
-    wxCHECK_RET( m_stream, _T("should have a stream in wxStreamBuffer") );
+    wxOutputStream *outStream = GetOutputStream();
+
+    wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") );
 
     // if we don't have buffer at all, just forward this call to the stream,
     if ( !HasBuffer() )
     {
-        m_stream->OnSysWrite(&c, 1);
+        outStream->OnSysWrite(&c, 1);
     }
     else
     {
@@ -324,12 +353,14 @@ char wxStreamBuffer::Peek()
 
 char wxStreamBuffer::GetChar()
 {
-    wxCHECK_MSG( m_stream, 0, _T("should have a stream in wxStreamBuffer") );
+    wxInputStream *inStream = GetInputStream();
+
+    wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
 
     char c;
     if ( !HasBuffer() )
     {
-        m_stream->OnSysRead(&c, 1);
+        inStream->OnSysRead(&c, 1);
     }
     else
     {
@@ -350,16 +381,16 @@ char wxStreamBuffer::GetChar()
 
 size_t wxStreamBuffer::Read(void *buffer, size_t size)
 {
-    wxCHECK_MSG( m_stream, 0, _T("should have a stream in wxStreamBuffer") );
+    wxInputStream *inStream = GetInputStream();
 
-    wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") );
+    wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
 
     // lasterror is reset before all new IO calls
     m_stream->m_lasterror = wxStream_NOERROR;
 
     if ( !HasBuffer() )
     {
-        m_stream->m_lastcount = m_stream->OnSysRead(buffer, size);
+        m_stream->m_lastcount = inStream->OnSysRead(buffer, size);
     }
     else // we have a buffer, use it
     {
@@ -421,8 +452,9 @@ size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf)
 
 size_t wxStreamBuffer::Write(const void *buffer, size_t size)
 {
-    wxCHECK_MSG( m_stream, 0, _T("should have a stream in wxStreamBuffer") );
-    wxCHECK_MSG( m_mode != read, 0, _T("can't write to this buffer") );
+    wxOutputStream *outStream = GetOutputStream();
+
+    wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") );
 
     // lasterror is reset before all new IO calls
     m_stream->m_lasterror = wxStream_NOERROR;
@@ -430,7 +462,7 @@ size_t wxStreamBuffer::Write(const void *buffer, size_t size)
     if ( !HasBuffer() && m_fixed )
     {
         // no buffer, just forward the call to the stream
-        m_stream->m_lastcount = m_stream->OnSysWrite(buffer, size);
+        m_stream->m_lastcount = outStream->OnSysWrite(buffer, size);
     }
     else // we [may] have a buffer, use it
     {
@@ -581,9 +613,21 @@ off_t wxStreamBuffer::Seek(off_t pos, wxSeekMode mode)
 
 off_t wxStreamBuffer::Tell() const
 {
-    off_t pos = m_stream->OnSysTell();
-    if ( pos == wxInvalidOffset )
-        return wxInvalidOffset;
+    off_t pos;
+
+    // only ask the stream for position if we have a real stream and not a
+    // dummy one which we created ourselves, otherwise we'd call
+    // wxStream::OnSysTell() which would always return wxInvalidOffset
+    if ( !m_destroystream )
+    {
+        pos = m_stream->OnSysTell();
+        if ( pos == wxInvalidOffset )
+            return wxInvalidOffset;
+    }
+    else // no associated stream
+    {
+        pos = 0;
+    }
 
     pos += GetIntPosition();
 
@@ -607,16 +651,6 @@ wxStreamBase::~wxStreamBase()
 {
 }
 
-size_t wxStreamBase::OnSysRead(void *WXUNUSED(buffer), size_t WXUNUSED(size))
-{
-    return 0;
-}
-
-size_t wxStreamBase::OnSysWrite(const void *WXUNUSED(buffer), size_t WXUNUSED(bufsize))
-{
-    return 0;
-}
-
 off_t wxStreamBase::OnSysSeek(off_t WXUNUSED(seek), wxSeekMode WXUNUSED(mode))
 {
     return wxInvalidOffset;
@@ -643,13 +677,24 @@ wxInputStream::~wxInputStream()
     free(m_wback);
 }
 
+size_t wxInputStream::OnSysRead(void * WXUNUSED(buffer),
+                                size_t WXUNUSED(bufsize))
+{
+    return 0;
+}
+
 bool wxInputStream::Eof() const
 {
     wxInputStream *self = wxConstCast(this, wxInputStream);
 
     char c;
     self->Read(&c, 1);
-    if ( GetLastError() == wxSTREAM_EOF )
+
+    // some streams can know that they're at EOF before actually trying to
+    // read beyond the end of stream (e.g. files) while others have no way of
+    // knowing it, so to provide the same behaviour in all cases we only
+    // return TRUE from here if the character really couldn't be read
+    if ( !self->LastRead() && GetLastError() == wxSTREAM_EOF )
     {
         return TRUE;
     }
@@ -670,14 +715,14 @@ char *wxInputStream::AllocSpaceWBack(size_t needed_size)
     if (!temp_b)
         return NULL;
 
-    /* copy previous data (and free old buffer) if needed */
+    // copy previous data (and free old buffer) if needed
     if (m_wback)
     {
         memmove(temp_b + needed_size, m_wback + m_wbackcur, toget);
         free(m_wback);
     }
 
-    /* done */
+    // done
     m_wback = temp_b;
     m_wbackcur = 0;
     m_wbacksize = needed_size + toget;
@@ -687,46 +732,58 @@ char *wxInputStream::AllocSpaceWBack(size_t needed_size)
 
 size_t wxInputStream::GetWBack(void *buf, size_t bsize)
 {
-    size_t toget = m_wbacksize-m_wbackcur;
-
     if (!m_wback)
         return 0;
 
-    if (bsize < toget)
-       toget = bsize;
+    // how many bytes do we have in the buffer?
+    size_t toget = m_wbacksize - m_wbackcur;
+
+    if ( bsize < toget )
+    {
+        // we won't read everything
+        toget = bsize;
+    }
 
-    memcpy(buf, (m_wback+m_wbackcur), toget);
+    // copy the data from the cache 
+    memcpy(buf, m_wback + m_wbackcur, toget);
 
     m_wbackcur += toget;
-    if (m_wbackcur == m_wbacksize)
+    if ( m_wbackcur == m_wbacksize )
     {
+        // TODO: should we really free it here all the time? maybe keep it?
         free(m_wback);
         m_wback = NULL;
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
 
+    // return the number of bytes copied
     return toget;
 }
 
 size_t wxInputStream::Ungetch(const void *buf, size_t bufsize)
 {
+    if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF )
+    {
+        // can't operate on this stream until the error is cleared
+        return 0;
+    }
+
     char *ptrback = AllocSpaceWBack(bufsize);
     if (!ptrback)
         return 0;
 
+    // Eof() shouldn't return TRUE any longer
+    if ( m_lasterror == wxSTREAM_EOF )
+        m_lasterror = wxSTREAM_NO_ERROR;
+
     memcpy(ptrback, buf, bufsize);
     return bufsize;
 }
 
 bool wxInputStream::Ungetch(char c)
 {
-    void *ptrback = AllocSpaceWBack(1);
-    if (!ptrback)
-        return FALSE;
-
-    *(char *)ptrback = c;
-    return TRUE;
+    return Ungetch(&c, sizeof(char)) != 0;
 }
 
 char wxInputStream::GetC()
@@ -780,23 +837,29 @@ wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
 
 off_t wxInputStream::SeekI(off_t pos, wxSeekMode mode)
 {
-    /* Should be check and improve, just to remove a slight bug !
-       I don't know whether it should be put as well in wxFileInputStream::OnSysSeek ? */
+    // RR: This code is duplicated in wxBufferedInputStream. This is
+    // not really a good design, but buffered stream are different
+    // from all other in that they handle two stream-related objects,
+    // the stream buffer and parent stream.
+
+    // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek
     if (m_lasterror==wxSTREAM_EOF)
         m_lasterror=wxSTREAM_NOERROR;
 
-    /* A call to SeekI() will automatically invalidate any previous call
-       to Ungetch(), otherwise it would be possible to SeekI() to one
+    /* RR: A call to SeekI() will automatically invalidate any previous
+       call to Ungetch(), otherwise it would be possible to SeekI() to
        one position, unread some bytes there, SeekI() to another position
        and the data would be corrupted.
 
        GRG: Could add code here to try to navigate within the wback
        buffer if possible, but is it really needed? It would only work
        when seeking in wxFromCurrent mode, else it would invalidate
-       anyway...
-     */
+       anyway... */
+       
     if (m_wback)
     {
+        wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
+        
         free(m_wback);
         m_wback = NULL;
         m_wbacksize = 0;
@@ -808,7 +871,6 @@ off_t wxInputStream::SeekI(off_t pos, wxSeekMode mode)
 
 off_t wxInputStream::TellI() const
 {
-    /* GRG: Changed to make it compatible with the wback buffer */
     off_t pos = OnSysTell();
 
     if (pos != wxInvalidOffset)
@@ -817,19 +879,6 @@ off_t wxInputStream::TellI() const
     return pos;
 }
 
-// --------------------
-// Overloaded operators
-// --------------------
-
-#if wxUSE_SERIAL
-wxInputStream& wxInputStream::operator>>(wxObject *& obj)
-{
-    wxObjectInputStream obj_s(*this);
-    obj = obj_s.LoadObject();
-    return *this;
-}
-#endif // wxUSE_SERIAL
-
 
 // ----------------------------------------------------------------------------
 // wxOutputStream
@@ -843,6 +892,12 @@ wxOutputStream::~wxOutputStream()
 {
 }
 
+size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
+                                  size_t WXUNUSED(bufsize))
+{
+    return 0;
+}
+
 void wxOutputStream::PutC(char c)
 {
     Write(&c, 1);
@@ -874,14 +929,6 @@ void wxOutputStream::Sync()
 {
 }
 
-#if wxUSE_SERIAL
-wxOutputStream& wxOutputStream::operator<<(wxObject& obj)
-{
-    wxObjectOutputStream obj_s(*this);
-    obj_s.SaveObject(obj);
-    return *this;
-}
-#endif // wxUSE_SERIAL
 
 // ----------------------------------------------------------------------------
 // wxCountingOutputStream
@@ -945,6 +992,7 @@ off_t wxCountingOutputStream::OnSysTell() const
 
 wxFilterInputStream::wxFilterInputStream()
 {
+    m_parent_i_stream = NULL;
 }
 
 wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
@@ -962,6 +1010,7 @@ wxFilterInputStream::~wxFilterInputStream()
 
 wxFilterOutputStream::wxFilterOutputStream()
 {
+    m_parent_o_stream = NULL;
 }
 
 wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
@@ -977,17 +1026,27 @@ wxFilterOutputStream::~wxFilterOutputStream()
 // wxBufferedInputStream
 // ----------------------------------------------------------------------------
 
-wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s)
+wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
+                                             wxStreamBuffer *buffer)
                      : wxFilterInputStream(s)
 {
-    m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
+    if ( buffer )
+    {
+        // use the buffer provided by the user
+        m_i_streambuf = buffer;
+    }
+    else // create a default buffer
+    {
+        m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
 
-    m_i_streambuf->SetBufferIO(1024);
+        m_i_streambuf->SetBufferIO(1024);
+    }
 }
 
 wxBufferedInputStream::~wxBufferedInputStream()
 {
-    m_parent_i_stream->SeekI(-m_i_streambuf->GetBytesLeft(), wxFromCurrent);
+    m_parent_i_stream->SeekI(-(off_t)m_i_streambuf->GetBytesLeft(),
+                             wxFromCurrent);
 
     delete m_i_streambuf;
 }
@@ -999,31 +1058,58 @@ char wxBufferedInputStream::Peek()
 
 wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
 {
-    size_t retsize;
+    // reset the error flag
+    m_lasterror = wxStream_NOERROR;
 
-    retsize = GetWBack(buf, size);
-    m_lastcount = retsize;
-    if (retsize == size)
+    // first read from the already cached data
+    m_lastcount = GetWBack(buf, size);
+
+    // do we have to read anything more?
+    if ( m_lastcount < size )
     {
-        m_lasterror = wxStream_NOERROR;
-        return *this;
-    }
-    size -= retsize;
-    buf = (char *)buf + retsize;
+        size -= m_lastcount;
+        buf = (char *)buf + m_lastcount;
+
+        // the call to wxStreamBuffer::Read() below will reset our m_lastcount,
+        // so save it
+        size_t countOld = m_lastcount;
 
-    m_i_streambuf->Read(buf, size);
+        m_i_streambuf->Read(buf, size);
+
+        m_lastcount += countOld;
+    }
 
     return *this;
 }
 
 off_t wxBufferedInputStream::SeekI(off_t pos, wxSeekMode mode)
 {
+    // RR: Look at wxInputStream for comments.
+
+    if (m_lasterror==wxSTREAM_EOF)
+        m_lasterror=wxSTREAM_NOERROR;
+
+    if (m_wback)
+    {
+        wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
+        
+        free(m_wback);
+        m_wback = NULL;
+        m_wbacksize = 0;
+        m_wbackcur = 0;
+    }
+    
     return m_i_streambuf->Seek(pos, mode);
 }
 
 off_t wxBufferedInputStream::TellI() const
 {
-    return m_i_streambuf->Tell();
+    off_t pos = m_i_streambuf->Tell();
+
+    if (pos != wxInvalidOffset)
+        pos -= (m_wbacksize - m_wbackcur);
+        
+    return pos;
 }
 
 size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize)
@@ -1041,15 +1127,32 @@ off_t wxBufferedInputStream::OnSysTell() const
     return m_parent_i_stream->TellI();
 }
 
+void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer)
+{
+    wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") );
+
+    delete m_i_streambuf;
+    m_i_streambuf = buffer;
+}
+
 // ----------------------------------------------------------------------------
 // wxBufferedOutputStream
 // ----------------------------------------------------------------------------
 
-wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s)
+wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s,
+                                               wxStreamBuffer *buffer)
                       : wxFilterOutputStream(s)
 {
-    m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
-    m_o_streambuf->SetBufferIO(1024);
+    if ( buffer )
+    {
+        m_o_streambuf = buffer;
+    }
+    else // create a default one
+    {
+        m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
+
+        m_o_streambuf->SetBufferIO(1024);
+    }
 }
 
 wxBufferedOutputStream::~wxBufferedOutputStream()
@@ -1102,21 +1205,23 @@ size_t wxBufferedOutputStream::GetSize() const
    return m_parent_o_stream->GetSize() + m_o_streambuf->GetIntPosition();
 }
 
+void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer)
+{
+    wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") );
+
+    delete m_o_streambuf;
+    m_o_streambuf = buffer;
+}
+
 // ----------------------------------------------------------------------------
 // Some IOManip function
 // ----------------------------------------------------------------------------
 
 wxOutputStream& wxEndL(wxOutputStream& stream)
 {
-#ifdef __MSW__
-  return stream.Write("\r\n", 2);
-#else
-#ifdef __WXMAC__
-  return stream.Write("\r", 1);
-#else
-  return stream.Write("\n", 1);
-#endif
-#endif
+    static const wxChar *eol = wxTextFile::GetEOL();
+
+    return stream.Write(eol, wxStrlen(eol));
 }
 
 #endif