]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/stream.cpp
fix for always terminating intermediate UniChar String for 4 bytes wchar_t
[wxWidgets.git] / src / common / stream.cpp
index ef92a1807a3b977facd144fa84b1a546d37644e4..1f72c00bd6226808dc8e0bdab23d563fb10bfb6f 100644 (file)
@@ -1,16 +1,25 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        stream.cpp
+// Name:        src/common/stream.cpp
 // Purpose:     wxStream base classes
 // Author:      Guilhem Lavaux
-// Modified by:
+// Modified by: VZ (23.11.00) to fix realloc()ing new[]ed memory,
+//                            general code review
 // Created:     11/07/98
 // RCS-ID:      $Id$
 // Copyright:   (c) Guilhem Lavaux
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#ifdef __GNUG__
-#pragma implementation "stream.h"
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+    #pragma implementation "stream.h"
 #endif
 
 // For compilers that support precompilation, includes "wx.h".
 #include <ctype.h>
 #include "wx/stream.h"
 #include "wx/datstrm.h"
-#include "wx/objstrm.h"
+#include "wx/textfile.h"
+#include "wx/log.h"
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// the temporary buffer size used when copying from stream to stream
+#define BUF_TEMP_SIZE 4096
 
-#define BUF_TEMP_SIZE 10000
+// ============================================================================
+// implementation
+// ============================================================================
 
 // ----------------------------------------------------------------------------
 // wxStreamBuffer
 // ----------------------------------------------------------------------------
 
-#define CHECK_ERROR(err) \
-   if (m_stream->m_lasterror == wxStream_NOERROR) \
-     m_stream->m_lasterror = err
+void wxStreamBuffer::SetError(wxStreamError err)
+{
+   if ( m_stream && m_stream->m_lasterror == wxSTREAM_NO_ERROR )
+       m_stream->m_lasterror = err;
+}
 
-wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
-  : m_buffer_start(NULL), m_buffer_end(NULL), m_buffer_pos(NULL),
-    m_buffer_size(0), m_fixed(TRUE), m_flushable(TRUE), m_stream(&stream),
-    m_mode(mode), m_destroybuf(FALSE), m_destroystream(FALSE)
+void wxStreamBuffer::InitBuffer()
+{
+    m_buffer_start =
+    m_buffer_end =
+    m_buffer_pos = NULL;
+    m_buffer_size = 0;
+
+    // if we are going to allocate the buffer, we should free it later as well
+    m_destroybuf = TRUE;
+}
+
+void wxStreamBuffer::Init()
 {
+    InitBuffer();
+
+    m_fixed = TRUE;
 }
 
 wxStreamBuffer::wxStreamBuffer(BufMode mode)
-  : m_buffer_start(NULL), m_buffer_end(NULL), m_buffer_pos(NULL),
-    m_buffer_size(0), m_fixed(TRUE), m_flushable(FALSE), m_stream(NULL),
-    m_mode(mode), m_destroybuf(FALSE), m_destroystream(TRUE)
 {
-  m_stream = new wxStreamBase();
+    Init();
+
+    m_stream = NULL;
+    m_mode = mode;
+
+    m_flushable = FALSE;
+}
+
+wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
+{
+    Init();
+
+    m_stream = &stream;
+    m_mode = mode;
+
+    m_flushable = TRUE;
 }
 
 wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer)
 {
-  m_buffer_start = buffer.m_buffer_start;
-  m_buffer_end = buffer.m_buffer_end;
-  m_buffer_pos = buffer.m_buffer_pos;
-  m_buffer_size = buffer.m_buffer_size;
-  m_fixed = buffer.m_fixed;
-  m_flushable = buffer.m_flushable;
-  m_stream = buffer.m_stream;
-  m_mode = buffer.m_mode;
-  m_destroybuf = FALSE;
-  m_destroystream = FALSE;
+    // doing this has big chances to lead to a crash when the source buffer is
+    // destroyed (otherwise assume the caller knows what he does)
+    wxASSERT_MSG( !buffer.m_destroybuf,
+                  _T("it's a bad idea to copy this buffer") );
+
+    m_buffer_start = buffer.m_buffer_start;
+    m_buffer_end = buffer.m_buffer_end;
+    m_buffer_pos = buffer.m_buffer_pos;
+    m_buffer_size = buffer.m_buffer_size;
+    m_fixed = buffer.m_fixed;
+    m_flushable = buffer.m_flushable;
+    m_stream = buffer.m_stream;
+    m_mode = buffer.m_mode;
+    m_destroybuf = FALSE;
+}
+
+void wxStreamBuffer::FreeBuffer()
+{
+    if ( m_destroybuf )
+        free(m_buffer_start);
 }
 
 wxStreamBuffer::~wxStreamBuffer()
 {
-  if (m_destroybuf)
-    wxDELETEA(m_buffer_start);
-  if (m_destroystream)
-    delete m_stream;
+    FreeBuffer();
 }
 
-void wxStreamBuffer::SetBufferIO(char *buffer_start, char *buffer_end)
+wxInputStream *wxStreamBuffer::GetInputStream() const
 {
-  if (m_destroybuf)
-    wxDELETEA(m_buffer_start);
-  m_buffer_start = buffer_start;
-  m_buffer_end   = buffer_end;
+    return m_mode == write ? NULL : (wxInputStream *)m_stream;
+}
 
-  m_buffer_size = m_buffer_end-m_buffer_start;
-  m_destroybuf = FALSE;
-  ResetBuffer();
+wxOutputStream *wxStreamBuffer::GetOutputStream() const
+{
+    return m_mode == read ? NULL : (wxOutputStream *)m_stream;
 }
 
-void wxStreamBuffer::SetBufferIO(size_t bufsize)
+void wxStreamBuffer::SetBufferIO(void *buffer_start,
+                                 void *buffer_end,
+                                 bool takeOwnership)
 {
-  char *b_start;
+    SetBufferIO(buffer_start, (char *)buffer_end - (char *)buffer_start,
+                takeOwnership);
+}
+
+void wxStreamBuffer::SetBufferIO(void *start,
+                                 size_t len,
+                                 bool takeOwnership)
+{
+    // start by freeing the old buffer
+    FreeBuffer();
 
-  if (m_destroybuf)
-    wxDELETEA(m_buffer_start);
+    m_buffer_start = (char *)start;
+    m_buffer_end   = m_buffer_start + len;
 
-  if (!bufsize) {
-    m_buffer_start = (char*)NULL;
-    m_buffer_end = (char*)NULL;
-    m_buffer_pos = (char*)NULL;
-    m_buffer_size = 0;
-    return;
-  }
+    m_buffer_size = len;
+
+    // if we own it, we free it
+    m_destroybuf = takeOwnership;
+
+    ResetBuffer();
+}
 
-  b_start = new char[bufsize];
-  SetBufferIO(b_start, b_start + bufsize);
-  m_destroybuf = TRUE;
+void wxStreamBuffer::SetBufferIO(size_t bufsize)
+{
+    // start by freeing the old buffer
+    FreeBuffer();
+
+    if ( bufsize )
+    {
+        SetBufferIO(malloc(bufsize), bufsize, TRUE /* take ownership */);
+    }
+    else // no buffer size => no buffer
+    {
+        InitBuffer();
+    }
 }
 
 void wxStreamBuffer::ResetBuffer()
 {
-  m_stream->m_lasterror = wxStream_NOERROR;
-  m_stream->m_lastcount = 0;
-  if (m_mode == read && m_flushable)
-    m_buffer_pos = m_buffer_end;
-  else
-    m_buffer_pos = m_buffer_start;
+    if ( m_stream )
+    {
+        m_stream->Reset();
+        m_stream->m_lastcount = 0;
+    }
+
+    m_buffer_pos = m_mode == read && m_flushable
+                        ? m_buffer_end
+                        : m_buffer_start;
 }
 
+// fill the buffer with as much data as possible (only for read buffers)
 bool wxStreamBuffer::FillBuffer()
 {
-  size_t count;
+    wxInputStream *inStream = GetInputStream();
+
+    // It's legal to have no stream, so we don't complain about it just return FALSE
+    if ( !inStream )
+        return FALSE;
+
+    size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size);
+    if ( !count )
+        return FALSE;
 
-  count = m_stream->OnSysRead(m_buffer_start, m_buffer_size);
-  m_buffer_end = m_buffer_start+count;
-  m_buffer_pos = m_buffer_start;
+    m_buffer_end = m_buffer_start + count;
+    m_buffer_pos = m_buffer_start;
 
-  if (count == 0)
-    return FALSE;
-  return TRUE;
+    return TRUE;
 }
 
+// write the buffer contents to the stream (only for write buffers)
 bool wxStreamBuffer::FlushBuffer()
 {
-  size_t count, current;
+    wxCHECK_MSG( m_flushable, FALSE, _T("can't flush this buffer") );
+
+    // FIXME: what is this check for? (VZ)
+    if ( m_buffer_pos == m_buffer_start )
+        return FALSE;
+
+    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 = outStream->OnSysWrite(m_buffer_start, current);
+    if ( count != current )
+        return FALSE;
+
+    m_buffer_pos = m_buffer_start;
 
-  if (m_buffer_pos == m_buffer_start || !m_flushable)
-    return FALSE;
+    return TRUE;
+}
 
-  current = m_buffer_pos-m_buffer_start;
-  count = m_stream->OnSysWrite(m_buffer_start, current);
-  if (count != current)
-    return FALSE;
-  m_buffer_pos = m_buffer_start;
+size_t wxStreamBuffer::GetDataLeft()
+{
+    /* Why is this done? RR. */
+    if ( m_buffer_pos == m_buffer_end && m_flushable)
+        FillBuffer();
 
-  return TRUE;
+    return GetBytesLeft();
 }
 
+// copy up to size bytes from our buffer into the provided one
 void wxStreamBuffer::GetFromBuffer(void *buffer, size_t size)
 {
-  size_t s_toget = m_buffer_end-m_buffer_pos;
+    // don't get more bytes than left in the buffer
+    size_t left = GetBytesLeft();
 
-  if (size < s_toget)
-    s_toget = size;
+    if ( size > left )
+        size = left;
 
-  memcpy(buffer, m_buffer_pos, s_toget);
-  m_buffer_pos += s_toget;
+    memcpy(buffer, m_buffer_pos, size);
+    m_buffer_pos += size;
 }
 
+// copy the contents of the provided buffer into this one
 void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size)
 {
-  size_t s_toput = m_buffer_end-m_buffer_pos;
-
-  if (s_toput < size && !m_fixed) {
-    if (!m_buffer_start)
-      SetBufferIO(size);
-    else {
-      size_t delta = m_buffer_pos-m_buffer_start;
+    size_t left = GetBytesLeft();
 
-      m_buffer_start = (char *)realloc(m_buffer_start, m_buffer_size+size);
-      m_buffer_pos   = m_buffer_start + delta;
-      // I round a bit
-      m_buffer_size += size;
-      m_buffer_end   = m_buffer_start+m_buffer_size;
+    if ( size > left )
+    {
+        if ( m_fixed )
+        {
+            // we can't realloc the buffer, so just copy what we can
+            size = left;
+        }
+        else // !m_fixed
+        {
+            // realloc the buffer to have enough space for the data
+            size_t delta = m_buffer_pos - m_buffer_start;
+
+            char *startOld = m_buffer_start;
+            m_buffer_size += size;
+            m_buffer_start = (char *)realloc(m_buffer_start, m_buffer_size);
+            if ( !m_buffer_start )
+            {
+                // don't leak memory if realloc() failed
+                m_buffer_start = startOld;
+                m_buffer_size -= size;
+
+                // what else can we do?
+                return;
+            }
+
+            // adjust the pointers invalidated by realloc()
+            m_buffer_pos = m_buffer_start + delta;
+            m_buffer_end = m_buffer_start + m_buffer_size;
+        }
     }
-    s_toput = size;
-  }
-  if (s_toput > size)
-    s_toput = size;
-  memcpy(m_buffer_pos, buffer, s_toput);
-  m_buffer_pos += s_toput;
+
+    memcpy(m_buffer_pos, buffer, size);
+    m_buffer_pos += size;
 }
 
 void wxStreamBuffer::PutChar(char c)
 {
-  wxASSERT(m_stream != NULL);
+    wxOutputStream *outStream = GetOutputStream();
 
-  if (!m_buffer_size) {
-    m_stream->OnSysWrite(&c, 1);
-    return;
-  }
+    wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") );
 
-  if (GetDataLeft() == 0 && !FlushBuffer()) {
-    CHECK_ERROR(wxStream_WRITE_ERR);
-    return;
-  }
-
-  PutToBuffer(&c, 1);
-  m_stream->m_lastcount = 1;
+    // if we don't have buffer at all, just forward this call to the stream,
+    if ( !HasBuffer() )
+    {
+        outStream->OnSysWrite(&c, sizeof(c));
+    }
+    else
+    {
+        // otherwise check we have enough space left
+        if ( !GetDataLeft() && !FlushBuffer() )
+        {
+            // we don't
+            SetError(wxSTREAM_WRITE_ERROR);
+        }
+        else
+        {
+            PutToBuffer(&c, sizeof(c));
+            m_stream->m_lastcount = 1;
+        }
+    }
 }
 
 char wxStreamBuffer::Peek()
 {
-  char c;
+    wxCHECK_MSG( m_stream && HasBuffer(), 0,
+                 _T("should have the stream and the buffer in wxStreamBuffer") );
 
-  wxASSERT(m_stream != NULL && m_buffer_size != 0);
-
-  if (!GetDataLeft()) {
-    CHECK_ERROR(wxStream_READ_ERR);
-    return 0;
-  }
+    if ( !GetDataLeft() )
+    {
+        SetError(wxSTREAM_READ_ERROR);
+        return 0;
+    }
 
-  GetFromBuffer(&c, 1);
-  m_buffer_pos--;
+    char c;
+    GetFromBuffer(&c, sizeof(c));
+    m_buffer_pos--;
 
-  return c;
+    return c;
 }
 
 char wxStreamBuffer::GetChar()
 {
-  char c;
-
-  wxASSERT(m_stream != NULL);
+    wxInputStream *inStream = GetInputStream();
 
-  if (!m_buffer_size) {
-    m_stream->OnSysRead(&c, 1);
-    return c;
-  }
+    wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
 
-  if (!GetDataLeft()) {
-    CHECK_ERROR(wxStream_READ_ERR);
-    return 0;
-  }
+    char c;
+    if ( !HasBuffer() )
+    {
+        inStream->OnSysRead(&c, sizeof(c));
+    }
+    else
+    {
+        if ( !GetDataLeft() )
+        {
+            SetError(wxSTREAM_READ_ERROR);
+            c = 0;
+        }
+        else
+        {
+            GetFromBuffer(&c, sizeof(c));
+            m_stream->m_lastcount = 1;
+        }
+    }
 
-  GetFromBuffer(&c, 1);
-  
-  m_stream->m_lastcount = 1;
-  return c;
+    return c;
 }
 
 size_t wxStreamBuffer::Read(void *buffer, size_t size)
 {
-  wxASSERT(m_stream != NULL);
-
-  if (m_mode == write)
-    return 0;
-
-  // ------------------
-  // Buffering disabled
-  // ------------------
+    // lasterror is reset before all new IO calls
+    if ( m_stream )
+        m_stream->Reset();
 
-  m_stream->m_lasterror = wxStream_NOERROR;
-  if (!m_buffer_size)
-    return (m_stream->m_lastcount += m_stream->OnSysRead(buffer, size));
-
-  // -----------------
-  // Buffering enabled
-  // -----------------
-  size_t buf_left, orig_size = size;
+    size_t read;
+    if ( !HasBuffer() )
+    {
+        wxInputStream *inStream = GetInputStream();
 
-  while (size > 0) {
-    buf_left = GetDataLeft(); 
+        wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
 
-    // First case: the requested buffer is larger than the stream buffer,
-    //             we split it.
-    if (size > buf_left) {
-      GetFromBuffer(buffer, buf_left);
-      size  -= buf_left;
-      buffer = (char *)buffer + buf_left; // ANSI C++ violation.
+        read = inStream->OnSysRead(buffer, size);
+    }
+    else // we have a buffer, use it
+    {
+        size_t orig_size = size;
+
+        while ( size > 0 )
+        {
+            size_t left = GetDataLeft();
+
+            // if the requested number of bytes if greater than the buffer
+            // size, read data in chunks
+            if ( size > left )
+            {
+                GetFromBuffer(buffer, left);
+                size -= left;
+                buffer = (char *)buffer + left;
+
+                if ( !FillBuffer() )
+                {
+                    SetError(wxSTREAM_EOF);
+                    break;
+                }
+            }
+            else // otherwise just do it in one gulp
+            {
+                GetFromBuffer(buffer, size);
+                size = 0;
+            }
+        }
+
+        read = orig_size - size;
+    }
 
-      if (!FillBuffer()) {
-        CHECK_ERROR(wxStream_EOF);
-        return (m_stream->m_lastcount = orig_size-size);
-      }
-    } else {
+    if ( m_stream )
+        m_stream->m_lastcount = read;
 
-      // Second case: we just copy from the stream buffer.
-      GetFromBuffer(buffer, size);
-      break;
-    }
-  }
-  return (m_stream->m_lastcount += orig_size);
+    return read;
 }
 
-size_t wxStreamBuffer::Read(wxStreamBuffer *s_buf)
+// this should really be called "Copy()"
+size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf)
 {
-  char buf[BUF_TEMP_SIZE];
-  size_t s = 0, bytes_read = BUF_TEMP_SIZE;
+    wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") );
 
-  if (m_mode == write)
-    return 0;
+    char buf[BUF_TEMP_SIZE];
+    size_t nRead,
+           total = 0;
+
+    do
+    {
+        nRead = Read(dbuf, WXSIZEOF(buf));
+        if ( nRead )
+        {
+            nRead = dbuf->Write(buf, nRead);
+            total += nRead;
+        }
+    }
+    while ( nRead );
 
-  while (bytes_read != 0) {
-    bytes_read = Read(buf, bytes_read);
-    bytes_read = s_buf->Write(buf, bytes_read);
-    s += bytes_read;
-  }
-  return s;
+    return total;
 }
 
 size_t wxStreamBuffer::Write(const void *buffer, size_t size)
 {
-  wxASSERT(m_stream != NULL);
-
-  if (m_mode == read)
-    return 0;
-
-  // ------------------
-  // Buffering disabled
-  // ------------------
-
-  m_stream->m_lasterror = wxStream_NOERROR;
-  if (!m_buffer_size && m_fixed)
-    return (m_stream->m_lastcount = m_stream->OnSysWrite(buffer, size));
-
-  // ------------------
-  // Buffering enabled
-  // ------------------
-
-  size_t buf_left, orig_size = size;
-
-  while (size > 0) {
-    buf_left = m_buffer_end - m_buffer_pos;
+    if (m_stream)
+    {
+        // lasterror is reset before all new IO calls
+        m_stream->Reset();
+    }
 
-    // First case: the buffer to write is larger than the stream buffer,
-    //             we split it
-    // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
-    // we always go to the second case.
-    if (size > buf_left && m_fixed) {
-      PutToBuffer(buffer, buf_left);
-      size -= buf_left;
-      buffer = (char *)buffer + buf_left; // ANSI C++ violation.
+    size_t ret;
 
-      if (!FlushBuffer()) {
-       CHECK_ERROR(wxStream_WRITE_ERR);
-        return (m_stream->m_lastcount = orig_size-size);
-      }
+    if ( !HasBuffer() && m_fixed )
+    {
+        wxOutputStream *outStream = GetOutputStream();
 
-      m_buffer_pos = m_buffer_start;
+        wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") );
 
-    } else {
+        // no buffer, just forward the call to the stream
+        ret = outStream->OnSysWrite(buffer, size);
+    }
+    else // we [may] have a buffer, use it
+    {
+        size_t orig_size = size;
+
+        while ( size > 0 )
+        {
+            size_t left = GetBytesLeft();
+
+            // if the buffer is too large to fit in the stream buffer, split
+            // it in smaller parts
+            //
+            // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
+            //     we always go to the second case.
+            //
+            // FIXME: fine, but if it fails we should (re)try writing it by
+            //        chunks as this will (hopefully) always work (VZ)
+
+            if ( size > left && m_fixed )
+            {
+                PutToBuffer(buffer, left);
+                size -= left;
+                buffer = (char *)buffer + left;
+
+                if ( !FlushBuffer() )
+                {
+                    SetError(wxSTREAM_WRITE_ERROR);
+
+                    break;
+                }
+
+                m_buffer_pos = m_buffer_start;
+            }
+            else // we can do it in one gulp
+            {
+                PutToBuffer(buffer, size);
+                size = 0;
+            }
+        }
+
+        ret = orig_size - size;
+    }
 
-      // Second case: just copy it in the stream buffer.
-      PutToBuffer(buffer, size);
-      break;
+    if (m_stream)
+    {
+        // i am not entirely sure what we do this for
+        m_stream->m_lastcount = ret;
     }
-  }
-  return (m_stream->m_lastcount = orig_size);
+
+    return ret;
 }
 
 size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf)
 {
-  char buf[BUF_TEMP_SIZE];
-  size_t s = 0, bytes_count = BUF_TEMP_SIZE, b_count2;
-  wxInputStream *in_stream;
+    wxCHECK_MSG( m_mode != read, 0, _T("can't write to this buffer") );
+    wxCHECK_MSG( sbuf->m_mode != write, 0, _T("can't read from that buffer") );
 
-  if (m_mode == read)
-    return 0;
+    char buf[BUF_TEMP_SIZE];
+    size_t nWrite,
+           total = 0;
 
-  in_stream = (wxInputStream *)sbuf->Stream();
+    do
+    {
+        size_t nRead = sbuf->Read(buf, WXSIZEOF(buf));
+        if ( nRead )
+        {
+            nWrite = Write(buf, nRead);
+            if ( nWrite < nRead )
+            {
+                // put back data we couldn't copy
+                wxInputStream *in_stream = (wxInputStream *)sbuf->GetStream();
+
+                in_stream->Ungetch(buf + nWrite, nRead - nWrite);
+            }
+
+            total += nWrite;
+        }
+        else
+        {
+            nWrite = 0;
+        }
+    }
+    while ( nWrite == WXSIZEOF(buf) );
 
-  while (bytes_count == BUF_TEMP_SIZE) {
-    b_count2 = sbuf->Read(buf, bytes_count);
-    bytes_count = Write(buf, b_count2);
-    if (b_count2 > bytes_count)
-      in_stream->Ungetch(buf+bytes_count, b_count2-bytes_count);
-    s += bytes_count;
-  }
-  return s;
+    return total;
 }
 
 off_t wxStreamBuffer::Seek(off_t pos, wxSeekMode mode)
 {
-  off_t ret_off, diff, last_access;
+    off_t ret_off, diff;
 
-  last_access = GetLastAccess();
+    off_t last_access = GetLastAccess();
 
-  if (!m_flushable) {
-    switch (mode) {
-      case wxFromStart:   diff = pos;                     break;
-      case wxFromCurrent: diff = pos + GetIntPosition();  break;
-      case wxFromEnd:     diff = pos + last_access;       break;
-      default: return wxInvalidOffset;
+    if ( !m_flushable )
+    {
+        switch (mode)
+        {
+            case wxFromStart:
+                diff = pos;
+                break;
+
+            case wxFromCurrent:
+                diff = pos + GetIntPosition();
+                break;
+
+            case wxFromEnd:
+                diff = pos + last_access;
+                break;
+
+            default:
+                wxFAIL_MSG( _T("invalid seek mode") );
+
+                return wxInvalidOffset;
+        }
+        if (diff < 0 || diff > last_access)
+            return wxInvalidOffset;
+        SetIntPosition(diff);
+        return diff;
     }
-    if (diff < 0 || diff > last_access)
-      return wxInvalidOffset;
-    SetIntPosition(diff);
-    return diff;
-  }
 
-  switch (mode) {
-  case wxFromStart: {
-    // We'll try to compute an internal position later ...
-    ret_off = m_stream->OnSysSeek(pos, wxFromStart);
-    ResetBuffer();
-    return ret_off;
-  }
-  case wxFromCurrent: {
-    diff = pos + GetIntPosition();
-
-    if ( (diff > last_access) || (diff < 0) ) {
-      // We must take into account the fact that we have read something
-      // previously.
-      ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent);
-      ResetBuffer();
-      return ret_off;
-    } else {
-      SetIntPosition(diff);
-      return pos;
-    }
-  }
-  case wxFromEnd:
-    // Hard to compute: always seek to the requested position.
-    ret_off = m_stream->OnSysSeek(pos, wxFromEnd);
-    ResetBuffer();
-    return ret_off;
-  }
-  return wxInvalidOffset;
+    switch ( mode )
+    {
+        case wxFromStart:
+            // We'll try to compute an internal position later ...
+            ret_off = m_stream->OnSysSeek(pos, wxFromStart);
+            ResetBuffer();
+            return ret_off;
+
+        case wxFromCurrent:
+            diff = pos + GetIntPosition();
+
+            if ( (diff > last_access) || (diff < 0) )
+            {
+                // We must take into account the fact that we have read
+                // something previously.
+                ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent);
+                ResetBuffer();
+                return ret_off;
+            }
+            else
+            {
+                SetIntPosition(diff);
+                return pos;
+            }
+
+        case wxFromEnd:
+            // Hard to compute: always seek to the requested position.
+            ret_off = m_stream->OnSysSeek(pos, wxFromEnd);
+            ResetBuffer();
+            return ret_off;
+    }
+
+    return wxInvalidOffset;
 }
 
 off_t wxStreamBuffer::Tell() const
 {
-    off_t pos = m_stream->OnSysTell();
-    if (pos == wxInvalidOffset)
-        return wxInvalidOffset;
+    off_t pos;
+
+    // ask the stream for position if we have a real one
+    if ( m_stream )
+    {
+        pos = m_stream->OnSysTell();
+        if ( pos == wxInvalidOffset )
+            return wxInvalidOffset;
+    }
+    else // no associated stream
+    {
+        pos = 0;
+    }
 
     pos += GetIntPosition();
-       
-    if (m_mode == read && m_flushable)
+
+    if ( m_mode == read && m_flushable )
         pos -= GetLastAccess();
-    
-    return pos;
-}
 
-size_t wxStreamBuffer::GetDataLeft()
-{
-    /* Why is this done? RR. */
-    if (m_buffer_end == m_buffer_pos && m_flushable)
-        FillBuffer();
-       
-    return m_buffer_end-m_buffer_pos;
+    return pos;
 }
 
 // ----------------------------------------------------------------------------
@@ -450,7 +649,7 @@ size_t wxStreamBuffer::GetDataLeft()
 
 wxStreamBase::wxStreamBase()
 {
-    m_lasterror = wxStream_NOERROR;
+    m_lasterror = wxSTREAM_NO_ERROR;
     m_lastcount = 0;
 }
 
@@ -458,16 +657,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;
@@ -483,159 +672,214 @@ off_t wxStreamBase::OnSysTell() const
 // ----------------------------------------------------------------------------
 
 wxInputStream::wxInputStream()
-  : wxStreamBase(),
-    m_wback(NULL), m_wbacksize(0), m_wbackcur(0)
 {
+    m_wback = NULL;
+    m_wbacksize =
+    m_wbackcur = 0;
 }
 
 wxInputStream::~wxInputStream()
 {
-  if (m_wback)
     free(m_wback);
 }
 
+bool wxInputStream::CanRead() const
+{
+    // we don't know if there is anything to read or not and by default we
+    // prefer to be optimistic and try to read data unless we know for sure
+    // there is no more of it
+    return m_lasterror != wxSTREAM_EOF;
+}
+
+bool wxInputStream::Eof() const
+{
+    // the only way the base class can know we're at EOF is when we'd already
+    // tried to read beyond it in which case last error is set accordingly
+    return GetLastError() == wxSTREAM_EOF;
+}
+
 char *wxInputStream::AllocSpaceWBack(size_t needed_size)
 {
-    /* get number of bytes left from previous wback buffer */
+    // get number of bytes left from previous wback buffer
     size_t toget = m_wbacksize - m_wbackcur;
 
-    /* allocate a buffer large enough to hold prev + new data */
-    char *temp_b = (char *) malloc(needed_size + toget);
+    // allocate a buffer large enough to hold prev + new data
+    char *temp_b = (char *)malloc(needed_size + toget);
 
     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;
 
-    return (char *) m_wback;
+    return m_wback;
 }
 
-size_t wxInputStream::GetWBack(char *buf, size_t bsize)
+size_t wxInputStream::GetWBack(void *buf, size_t size)
 {
-    size_t s_toget = m_wbacksize-m_wbackcur;
-
     if (!m_wback)
         return 0;
 
-    if (bsize < s_toget)
-       s_toget = bsize;
+    // how many bytes do we have in the buffer?
+    size_t toget = m_wbacksize - m_wbackcur;
+
+    if ( size < toget )
+    {
+        // we won't read everything
+        toget = size;
+    }
 
-    memcpy(buf, (m_wback+m_wbackcur), s_toget);
+    // copy the data from the cache
+    memcpy(buf, m_wback + m_wbackcur, toget);
 
-    m_wbackcur += s_toget;
-    if (m_wbackcur == m_wbacksize) 
+    m_wbackcur += toget;
+    if ( m_wbackcur == m_wbacksize )
     {
+        // TODO: should we really free it here all the time? maybe keep it?
         free(m_wback);
-        m_wback = (char *)NULL;
+        m_wback = NULL;
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
-    
-    return s_toget;
+
+    // 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)
 {
-    char * ptrback = AllocSpaceWBack(1);
-    if (!ptrback)
-        return FALSE;
-
-    *ptrback = c;
-    return TRUE;
+    return Ungetch(&c, sizeof(c)) != 0;
 }
 
 char wxInputStream::GetC()
 {
     char c;
-    Read(&c, 1);
+    Read(&c, sizeof(c));
     return c;
 }
 
-wxInputStream& wxInputStream::Read(void *buffer, size_t size)
+wxInputStream& wxInputStream::Read(void *buf, size_t size)
 {
-    char *buf = (char *)buffer;
+    char *p = (char *)buf;
+    m_lastcount = 0;
 
-    size_t retsize = GetWBack(buf, size);
-    if (retsize == size) 
+    size_t read = GetWBack(buf, size);
+    for ( ;; )
     {
-        m_lastcount = size;
-        m_lasterror = wxStream_NOERROR;
-        return *this;
+        size -= read;
+        m_lastcount += read;
+        p += read;
+
+        if ( !size )
+        {
+            // we read the requested amount of data
+            break;
+        }
+
+        if ( p != buf && !CanRead() )
+        {
+            // we have already read something and we would block in OnSysRead()
+            // now: don't do it but return immediately
+            break;
+        }
+
+        read = OnSysRead(p, size);
+        if ( !read )
+        {
+            // no more data available
+            break;
+        }
     }
-    size -= retsize;
-    buf  += retsize;
 
-    m_lastcount = OnSysRead(buf, size) + retsize;
     return *this;
 }
 
 char wxInputStream::Peek()
 {
     char c;
-    Read(&c, 1);
-    if (m_lasterror == wxStream_NOERROR) 
+    Read(&c, sizeof(c));
+    if (m_lasterror == wxSTREAM_NO_ERROR)
     {
         Ungetch(c);
         return c;
     }
-    
+
     return 0;
 }
 
 wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
 {
-    char buf[BUF_TEMP_SIZE]; 
-    size_t bytes_read = BUF_TEMP_SIZE;
+    char buf[BUF_TEMP_SIZE];
 
-    while (bytes_read == BUF_TEMP_SIZE) 
+    for ( ;; )
     {
-        bytes_read = Read(buf, bytes_read).LastRead();
-        bytes_read = stream_out.Write(buf, bytes_read).LastWrite();
+        size_t bytes_read = Read(buf, WXSIZEOF(buf)).LastRead();
+        if ( !bytes_read )
+            break;
+
+        if ( stream_out.Write(buf, bytes_read).LastWrite() != bytes_read )
+            break;
     }
+
     return *this;
 }
 
 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 ? */
-    if (m_lasterror==wxSTREAM_EOF) 
-        m_lasterror=wxSTREAM_NOERROR;
+    // 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_NO_ERROR;
 
-    /* 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...
-     */
-    if (m_wback) 
+       anyway... */
+
+    if (m_wback)
     {
+        wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
+
         free(m_wback);
-        m_wback = (char*) NULL;
+        m_wback = NULL;
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
@@ -645,7 +889,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)
@@ -654,25 +897,12 @@ 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
-
 
 // ----------------------------------------------------------------------------
 // wxOutputStream
 // ----------------------------------------------------------------------------
+
 wxOutputStream::wxOutputStream()
-  : wxStreamBase()
 {
 }
 
@@ -680,9 +910,15 @@ wxOutputStream::~wxOutputStream()
 {
 }
 
+size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
+                                  size_t WXUNUSED(bufsize))
+{
+    return 0;
+}
+
 void wxOutputStream::PutC(char c)
 {
-    Write((void *) &c, 1);
+    Write(&c, sizeof(c));
 }
 
 wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size)
@@ -711,21 +947,12 @@ void wxOutputStream::Sync()
 {
 }
 
-#if wxUSE_SERIAL
-wxOutputStream& wxOutputStream::operator<<(wxObject& obj)
-{
-    wxObjectOutputStream obj_s(*this);
-    obj_s.SaveObject(obj);
-    return *this;
-}
-#endif
 
 // ----------------------------------------------------------------------------
 // wxCountingOutputStream
 // ----------------------------------------------------------------------------
 
 wxCountingOutputStream::wxCountingOutputStream ()
-  : wxOutputStream()
 {
      m_currentPos = 0;
 }
@@ -735,24 +962,40 @@ size_t wxCountingOutputStream::GetSize() const
     return m_lastcount;
 }
 
-size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer), size_t size)
+size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer),
+                                          size_t size)
 {
     m_currentPos += size;
-    if (m_currentPos > m_lastcount) m_lastcount = m_currentPos;
+    if (m_currentPos > m_lastcount)
+        m_lastcount = m_currentPos;
+
     return m_currentPos;
 }
 
 off_t wxCountingOutputStream::OnSysSeek(off_t pos, wxSeekMode mode)
 {
-    if (mode == wxFromStart)
-        m_currentPos = pos;
-    if (mode == wxFromEnd)
-        m_currentPos = m_lastcount + pos;
-    else
-        m_currentPos += pos;
-    
-    if (m_currentPos > m_lastcount) m_lastcount = m_currentPos;
-  
+    switch ( mode )
+    {
+        case wxFromStart:
+            m_currentPos = pos;
+            break;
+
+        case wxFromEnd:
+            m_currentPos = m_lastcount + pos;
+            break;
+
+        case wxFromCurrent:
+            m_currentPos += pos;
+            break;
+
+        default:
+            wxFAIL_MSG( _T("invalid seek mode") );
+            return wxInvalidOffset;
+    }
+
+    if (m_currentPos > m_lastcount)
+        m_lastcount = m_currentPos;
+
     return m_currentPos;
 }
 
@@ -760,18 +1003,17 @@ off_t wxCountingOutputStream::OnSysTell() const
 {
     return m_currentPos;
 }
-  
+
 // ----------------------------------------------------------------------------
 // wxFilterInputStream
 // ----------------------------------------------------------------------------
 
 wxFilterInputStream::wxFilterInputStream()
-  : wxInputStream()
 {
+    m_parent_i_stream = NULL;
 }
 
 wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
-  : wxInputStream()
 {
     m_parent_i_stream = &stream;
 }
@@ -783,13 +1025,13 @@ wxFilterInputStream::~wxFilterInputStream()
 // ----------------------------------------------------------------------------
 // wxFilterOutputStream
 // ----------------------------------------------------------------------------
+
 wxFilterOutputStream::wxFilterOutputStream()
-  : wxOutputStream()
 {
+    m_parent_o_stream = NULL;
 }
 
 wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
-  : wxOutputStream()
 {
     m_parent_o_stream = &stream;
 }
@@ -801,17 +1043,28 @@ wxFilterOutputStream::~wxFilterOutputStream()
 // ----------------------------------------------------------------------------
 // wxBufferedInputStream
 // ----------------------------------------------------------------------------
-wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s)
-  : wxFilterInputStream(s)
+
+wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
+                                             wxStreamBuffer *buffer)
+                     : wxFilterInputStream(s)
 {
-    m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
-    m_i_streambuf->SetBufferIO(1024);
+    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);
+    }
 }
 
 wxBufferedInputStream::~wxBufferedInputStream()
 {
-    off_t unused_bytes=m_i_streambuf->GetBufferPos()-m_i_streambuf->GetBufferEnd();
-    m_parent_i_stream->SeekI(unused_bytes,wxFromCurrent);
+    m_parent_i_stream->SeekI(-(off_t)m_i_streambuf->GetBytesLeft(),
+                             wxFromCurrent);
 
     delete m_i_streambuf;
 }
@@ -821,34 +1074,60 @@ char wxBufferedInputStream::Peek()
     return m_i_streambuf->Peek();
 }
 
-wxInputStream& wxBufferedInputStream::Read(void *buffer, size_t size)
+wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
 {
-    size_t retsize;
-    char *buf = (char *)buffer;
+    // reset the error flag
+    Reset();
+
+    // first read from the already cached data
+    m_lastcount = GetWBack(buf, size);
 
-    retsize = GetWBack(buf, size);
-    m_lastcount = retsize;
-    if (retsize == size) 
+    // do we have to read anything more?
+    if ( m_lastcount < size )
     {
-        m_lasterror = wxStream_NOERROR;
-        return *this;
-    }
-    size -= retsize;
-    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)
+        Reset();
+
+    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)
@@ -866,16 +1145,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)
-  : wxFilterOutputStream(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()
@@ -928,21 +1223,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