X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/df18cc7aa1afdbe74d4b8e398d12ef34d1e78b5c..77c8efc8c37da6d6a5e2e8022d21d1cd7d76371d:/src/common/stream.cpp diff --git a/src/common/stream.cpp b/src/common/stream.cpp index e3d076ad77..f7cd2ea2c5 100644 --- a/src/common/stream.cpp +++ b/src/common/stream.cpp @@ -36,6 +36,7 @@ #include #include "wx/datstrm.h" #include "wx/textfile.h" +#include "wx/scopeguard.h" // ---------------------------------------------------------------------------- // constants @@ -63,7 +64,6 @@ 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; @@ -76,24 +76,24 @@ void wxStreamBuffer::Init() m_fixed = true; } -wxStreamBuffer::wxStreamBuffer(BufMode mode) +void wxStreamBuffer::InitWithStream(wxStreamBase& stream, BufMode mode) { Init(); - m_stream = NULL; + m_stream = &stream; m_mode = mode; - m_flushable = false; + m_flushable = true; } -wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode) +wxStreamBuffer::wxStreamBuffer(BufMode mode) { Init(); - m_stream = &stream; + m_stream = NULL; m_mode = mode; - m_flushable = true; + m_flushable = false; } wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer) @@ -101,12 +101,11 @@ wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer) // 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") ); + wxT("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; @@ -156,8 +155,6 @@ void wxStreamBuffer::SetBufferIO(void *start, m_buffer_start = (char *)start; m_buffer_end = m_buffer_start + len; - m_buffer_size = len; - // if we own it, we free it m_destroybuf = takeOwnership; @@ -195,7 +192,7 @@ void wxStreamBuffer::ResetBuffer() void wxStreamBuffer::Truncate() { size_t new_size = m_buffer_pos - m_buffer_start; - if ( new_size == m_buffer_size ) + if ( m_buffer_pos == m_buffer_end ) return; if ( !new_size ) @@ -206,11 +203,10 @@ void wxStreamBuffer::Truncate() } char *new_start = (char *)realloc(m_buffer_start, new_size); - wxCHECK_RET( new_size, _T("shrinking buffer shouldn't fail") ); + wxCHECK_RET( new_size, wxT("shrinking buffer shouldn't fail") ); m_buffer_start = new_start; - m_buffer_size = new_size; - m_buffer_end = m_buffer_start + m_buffer_size; + m_buffer_end = m_buffer_start + new_size; m_buffer_pos = m_buffer_end; } @@ -223,7 +219,7 @@ bool wxStreamBuffer::FillBuffer() if ( !inStream ) return false; - size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size); + size_t count = inStream->OnSysRead(GetBufferStart(), GetBufferSize()); if ( !count ) return false; @@ -236,7 +232,7 @@ bool wxStreamBuffer::FillBuffer() // write the buffer contents to the stream (only for write buffers) bool wxStreamBuffer::FlushBuffer() { - wxCHECK_MSG( m_flushable, false, _T("can't flush this buffer") ); + wxCHECK_MSG( m_flushable, false, wxT("can't flush this buffer") ); // FIXME: what is this check for? (VZ) if ( m_buffer_pos == m_buffer_start ) @@ -244,7 +240,7 @@ bool wxStreamBuffer::FlushBuffer() wxOutputStream *outStream = GetOutputStream(); - wxCHECK_MSG( outStream, false, _T("should have a stream in wxStreamBuffer") ); + wxCHECK_MSG( outStream, false, wxT("should have a stream in wxStreamBuffer") ); size_t current = m_buffer_pos - m_buffer_start; size_t count = outStream->OnSysWrite(m_buffer_start, current); @@ -293,24 +289,26 @@ void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size) 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 ) + if ( m_buffer_pos + size > m_buffer_end ) { - // don't leak memory if realloc() failed - m_buffer_start = startOld; - m_buffer_size -= size; + size_t delta = m_buffer_pos - m_buffer_start; + size_t new_size = delta + size; - // what else can we do? - return; - } + char *startOld = m_buffer_start; + m_buffer_start = (char *)realloc(m_buffer_start, new_size); + if ( !m_buffer_start ) + { + // don't leak memory if realloc() failed + m_buffer_start = startOld; + + // 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; + // adjust the pointers invalidated by realloc() + m_buffer_pos = m_buffer_start + delta; + m_buffer_end = m_buffer_start + new_size; + } // else: the buffer is big enough } } @@ -322,7 +320,7 @@ void wxStreamBuffer::PutChar(char c) { wxOutputStream *outStream = GetOutputStream(); - wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") ); + wxCHECK_RET( outStream, wxT("should have a stream in wxStreamBuffer") ); // if we don't have buffer at all, just forward this call to the stream, if ( !HasBuffer() ) @@ -348,7 +346,7 @@ void wxStreamBuffer::PutChar(char c) char wxStreamBuffer::Peek() { wxCHECK_MSG( m_stream && HasBuffer(), 0, - _T("should have the stream and the buffer in wxStreamBuffer") ); + wxT("should have the stream and the buffer in wxStreamBuffer") ); if ( !GetDataLeft() ) { @@ -367,7 +365,7 @@ char wxStreamBuffer::GetChar() { wxInputStream *inStream = GetInputStream(); - wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") ); + wxCHECK_MSG( inStream, 0, wxT("should have a stream in wxStreamBuffer") ); char c; if ( !HasBuffer() ) @@ -393,7 +391,7 @@ char wxStreamBuffer::GetChar() size_t wxStreamBuffer::Read(void *buffer, size_t size) { - wxASSERT_MSG( buffer, _T("Warning: Null pointer is about to be used") ); + wxASSERT_MSG( buffer, wxT("Warning: Null pointer is about to be used") ); /* Clear buffer first */ memset(buffer, 0x00, size); @@ -407,7 +405,7 @@ size_t wxStreamBuffer::Read(void *buffer, size_t size) { wxInputStream *inStream = GetInputStream(); - wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") ); + wxCHECK_MSG( inStream, 0, wxT("should have a stream in wxStreamBuffer") ); readBytes = inStream->OnSysRead(buffer, size); } @@ -452,7 +450,7 @@ size_t wxStreamBuffer::Read(void *buffer, size_t size) // this should really be called "Copy()" size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf) { - wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") ); + wxCHECK_MSG( m_mode != write, 0, wxT("can't read from this buffer") ); char buf[BUF_TEMP_SIZE]; size_t nRead, @@ -474,7 +472,7 @@ size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf) size_t wxStreamBuffer::Write(const void *buffer, size_t size) { - wxASSERT_MSG( buffer, _T("Warning: Null pointer is about to be send") ); + wxASSERT_MSG( buffer, wxT("Warning: Null pointer is about to be send") ); if (m_stream) { @@ -488,7 +486,7 @@ size_t wxStreamBuffer::Write(const void *buffer, size_t size) { wxOutputStream *outStream = GetOutputStream(); - wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") ); + wxCHECK_MSG( outStream, 0, wxT("should have a stream in wxStreamBuffer") ); // no buffer, just forward the call to the stream ret = outStream->OnSysWrite(buffer, size); @@ -546,8 +544,8 @@ size_t wxStreamBuffer::Write(const void *buffer, size_t size) size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf) { - 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") ); + wxCHECK_MSG( m_mode != read, 0, wxT("can't write to this buffer") ); + wxCHECK_MSG( sbuf->m_mode != write, 0, wxT("can't read from that buffer") ); char buf[BUF_TEMP_SIZE]; size_t nWrite, @@ -602,7 +600,7 @@ wxFileOffset wxStreamBuffer::Seek(wxFileOffset pos, wxSeekMode mode) break; default: - wxFAIL_MSG( _T("invalid seek mode") ); + wxFAIL_MSG( wxT("invalid seek mode") ); return wxInvalidOffset; } @@ -638,7 +636,7 @@ wxFileOffset wxStreamBuffer::Seek(wxFileOffset pos, wxSeekMode mode) size_t int_diff = wx_truncate_cast(size_t, diff); wxCHECK_MSG( (wxFileOffset)int_diff == diff, wxInvalidOffset, wxT("huge file not supported") ); SetIntPosition(int_diff); - return pos; + return diff; } case wxFromEnd: @@ -698,7 +696,7 @@ size_t wxStreamBase::GetSize() const return 0; const size_t len = wx_truncate_cast(size_t, length); - wxASSERT_MSG( len == length + size_t(0), _T("large files not supported") ); + wxASSERT_MSG( len == length + size_t(0), wxT("large files not supported") ); return len; } @@ -774,7 +772,7 @@ char *wxInputStream::AllocSpaceWBack(size_t needed_size) size_t wxInputStream::GetWBack(void *buf, size_t size) { - wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be used") ); + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be used") ); /* Clear buffer first */ memset(buf, 0x00, size); @@ -810,7 +808,7 @@ size_t wxInputStream::GetWBack(void *buf, size_t size) size_t wxInputStream::Ungetch(const void *buf, size_t bufsize) { - wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be used in Ungetch()") ); + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be used in Ungetch()") ); if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF ) { @@ -844,7 +842,7 @@ int wxInputStream::GetC() wxInputStream& wxInputStream::Read(void *buf, size_t size) { - wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be read") ); + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be read") ); char *p = (char *)buf; m_lastcount = 0; @@ -919,13 +917,48 @@ wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode) { // 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, + // from all others 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; + // avoid unnecessary seek operations (optimization) + wxFileOffset currentPos = TellI(), size = GetLength(); + if ((mode == wxFromStart && currentPos == pos) || + (mode == wxFromCurrent && pos == 0) || + (mode == wxFromEnd && size != wxInvalidOffset && currentPos == size-pos)) + return currentPos; + + if (!IsSeekable() && mode == wxFromCurrent && pos > 0) + { + // rather than seeking, we can just read data and discard it; + // this allows to forward-seek also non-seekable streams! + char buf[BUF_TEMP_SIZE]; + size_t bytes_read; + + // read chunks of BUF_TEMP_SIZE bytes until we reach the new position + for ( ; pos >= BUF_TEMP_SIZE; pos -= bytes_read) + { + bytes_read = Read(buf, WXSIZEOF(buf)).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == WXSIZEOF(buf)); + } + + // read the last 'pos' bytes + bytes_read = Read(buf, (size_t)pos).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == (size_t)pos); + + // we should now have sought to the right position... + return TellI(); + } + /* 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 @@ -1059,7 +1092,7 @@ wxFileOffset wxCountingOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode break; default: - wxFAIL_MSG( _T("invalid seek mode") ); + wxFAIL_MSG( wxT("invalid seek mode") ); return wxInvalidOffset; } @@ -1156,29 +1189,25 @@ wxString wxFilterClassFactoryBase::PopExtension(const wxString& location) const } wxString::size_type wxFilterClassFactoryBase::FindExtension( - const wxChar *location) const + const wxString& location) const { - size_t len = wxStrlen(location); - for (const wxChar *const *p = GetProtocols(wxSTREAM_FILEEXT); *p; p++) { - size_t l = wxStrlen(*p); - - if (l <= len && wxStrcmp(*p, location + len - l) == 0) - return len - l; + if ( location.EndsWith(*p) ) + return location.length() - wxStrlen(*p); } return wxString::npos; } -bool wxFilterClassFactoryBase::CanHandle(const wxChar *protocol, +bool wxFilterClassFactoryBase::CanHandle(const wxString& protocol, wxStreamProtocolType type) const { if (type == wxSTREAM_FILEEXT) return FindExtension(protocol) != wxString::npos; else for (const wxChar *const *p = GetProtocols(type); *p; p++) - if (wxStrcmp(*p, protocol) == 0) + if (protocol == *p) return true; return false; @@ -1211,21 +1240,33 @@ void wxFilterClassFactory::Remove() // wxBufferedInputStream // ---------------------------------------------------------------------------- -wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s, +namespace +{ + +// helper function used for initializing the buffer used by +// wxBufferedInput/OutputStream: it simply returns the provided buffer if it's +// not NULL or creates a buffer of the given size otherwise +template +wxStreamBuffer * +CreateBufferIfNeeded(T& stream, wxStreamBuffer *buffer, size_t bufsize = 1024) +{ + return buffer ? buffer : new wxStreamBuffer(bufsize, stream); +} + +} // anonymous namespace + +wxBufferedInputStream::wxBufferedInputStream(wxInputStream& stream, wxStreamBuffer *buffer) - : wxFilterInputStream(s) + : wxFilterInputStream(stream) { - 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 = CreateBufferIfNeeded(*this, buffer); +} - m_i_streambuf->SetBufferIO(1024); - } +wxBufferedInputStream::wxBufferedInputStream(wxInputStream& stream, + size_t bufsize) + : wxFilterInputStream(stream) +{ + m_i_streambuf = CreateBufferIfNeeded(*this, NULL, bufsize); } wxBufferedInputStream::~wxBufferedInputStream() @@ -1255,12 +1296,15 @@ wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size) size -= m_lastcount; buf = (char *)buf + m_lastcount; - // the call to wxStreamBuffer::Read() below will reset our m_lastcount, - // so save it + // the call to wxStreamBuffer::Read() below may reset our m_lastcount + // (but it also may not do it if the buffer is associated to another + // existing stream and wasn't created by us), so save it size_t countOld = m_lastcount; - m_i_streambuf->Read(buf, size); + // the new count of the bytes read is the count of bytes read this time + m_lastcount = m_i_streambuf->Read(buf, size); + // plus those we had read before m_lastcount += countOld; } @@ -1314,7 +1358,7 @@ wxFileOffset wxBufferedInputStream::OnSysTell() const void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer) { - wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") ); + wxCHECK_RET( buffer, wxT("wxBufferedInputStream needs buffer") ); delete m_i_streambuf; m_i_streambuf = buffer; @@ -1324,20 +1368,18 @@ void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer) // wxBufferedOutputStream // ---------------------------------------------------------------------------- -wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s, +wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& stream, wxStreamBuffer *buffer) - : wxFilterOutputStream(s) + : wxFilterOutputStream(stream) { - if ( buffer ) - { - m_o_streambuf = buffer; - } - else // create a default one - { - m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write); + m_o_streambuf = CreateBufferIfNeeded(*this, buffer); +} - m_o_streambuf->SetBufferIO(1024); - } +wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& stream, + size_t bufsize) + : wxFilterOutputStream(stream) +{ + m_o_streambuf = CreateBufferIfNeeded(*this, NULL, bufsize); } wxBufferedOutputStream::~wxBufferedOutputStream() @@ -1399,12 +1441,99 @@ wxFileOffset wxBufferedOutputStream::GetLength() const void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer) { - wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") ); + wxCHECK_RET( buffer, wxT("wxBufferedOutputStream needs buffer") ); delete m_o_streambuf; m_o_streambuf = buffer; } +// --------------------------------------------------------------------------- +// wxWrapperInputStream implementation +// --------------------------------------------------------------------------- + +wxWrapperInputStream::wxWrapperInputStream() +{ + m_lasterror = wxSTREAM_READ_ERROR; +} + +wxWrapperInputStream::wxWrapperInputStream(wxInputStream& stream) + : wxFilterInputStream(stream) +{ + SynchronizeLastError(); +} + +wxWrapperInputStream::wxWrapperInputStream(wxInputStream *stream) + : wxFilterInputStream(stream) +{ + if ( m_parent_i_stream ) + SynchronizeLastError(); + else + m_lasterror = wxSTREAM_READ_ERROR; +} + +void wxWrapperInputStream::InitParentStream(wxInputStream& stream) +{ + wxCHECK_RET( !m_parent_i_stream, "Can't init parent stream twice" ); + + m_parent_i_stream = &stream; + + SynchronizeLastError(); +} + +void wxWrapperInputStream::InitParentStream(wxInputStream* stream) +{ + wxCHECK_RET( !m_parent_i_stream, "Can't init parent stream twice" ); + + m_parent_i_stream = stream; + + if ( m_parent_i_stream ) + { + m_owns = true; + + SynchronizeLastError(); + } +} + +wxFileOffset wxWrapperInputStream::GetLength() const +{ + wxCHECK_MSG(m_parent_i_stream, wxInvalidOffset, "Stream not valid"); + + wxON_BLOCK_EXIT_THIS0(wxWrapperInputStream::SynchronizeLastError); + return m_parent_i_stream->GetLength(); +} + +bool wxWrapperInputStream::IsSeekable() const +{ + wxCHECK_MSG(m_parent_i_stream, false, "Stream not valid"); + return m_parent_i_stream->IsSeekable(); +} + +size_t wxWrapperInputStream::OnSysRead(void *buffer, size_t size) +{ + wxCHECK_MSG(m_parent_i_stream, false, "Stream not valid"); + + wxON_BLOCK_EXIT_THIS0(wxWrapperInputStream::SynchronizeLastError); + + m_parent_i_stream->Read(buffer, size); + return m_parent_i_stream->LastRead(); +} + +wxFileOffset wxWrapperInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode) +{ + wxCHECK_MSG(IsSeekable(), false, "Stream not seekable"); + + wxON_BLOCK_EXIT_THIS0(wxWrapperInputStream::SynchronizeLastError); + return m_parent_i_stream->SeekI (pos, mode); +} + +wxFileOffset wxWrapperInputStream::OnSysTell() const +{ + wxCHECK_MSG(m_parent_i_stream, false, "Stream not valid"); + + wxON_BLOCK_EXIT_THIS0(wxWrapperInputStream::SynchronizeLastError); + return m_parent_i_stream->TellI(); +} + // ---------------------------------------------------------------------------- // Some IOManip function // ----------------------------------------------------------------------------