1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/stream.cpp
3 // Purpose: wxStream base classes
4 // Author: Guilhem Lavaux
5 // Modified by: VZ (23.11.00) to fix realloc()ing new[]ed memory,
9 // Copyright: (c) Guilhem Lavaux
10 // Licence: wxWindows license
11 /////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
15 // ============================================================================
17 // ----------------------------------------------------------------------------
19 // ----------------------------------------------------------------------------
21 #pragma implementation "stream.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
38 #include "wx/stream.h"
39 #include "wx/datstrm.h"
40 #include "wx/objstrm.h"
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 // the temporary buffer size used when copying from stream to stream
47 #define BUF_TEMP_SIZE 10000
49 // ============================================================================
51 // ============================================================================
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 void wxStreamBuffer
::SetError(wxStreamError err
)
59 if ( m_stream
->m_lasterror
== wxStream_NOERROR
)
60 m_stream
->m_lasterror
= err
;
63 void wxStreamBuffer
::InitBuffer()
70 // there is nothing to destroy anyhow
74 void wxStreamBuffer
::Init()
81 wxStreamBuffer
::wxStreamBuffer(wxStreamBase
& stream
, BufMode mode
)
87 m_destroystream
= FALSE
;
90 wxStreamBuffer
::wxStreamBuffer(BufMode mode
)
92 m_stream
= new wxStreamBase
;
96 m_destroystream
= TRUE
;
99 wxStreamBuffer
::wxStreamBuffer(const wxStreamBuffer
& buffer
)
101 // doing this has big chances to lead to a crashwhen the source buffer is
102 // destroyed (otherwise assume the caller knows what he does)
103 wxASSERT_MSG( !buffer
.m_destroybuf
&& !buffer
.m_destroystream
,
104 _T("it's a bad idea to copy this buffer") );
106 m_buffer_start
= buffer
.m_buffer_start
;
107 m_buffer_end
= buffer
.m_buffer_end
;
108 m_buffer_pos
= buffer
.m_buffer_pos
;
109 m_buffer_size
= buffer
.m_buffer_size
;
110 m_fixed
= buffer
.m_fixed
;
111 m_flushable
= buffer
.m_flushable
;
112 m_stream
= buffer
.m_stream
;
113 m_mode
= buffer
.m_mode
;
114 m_destroybuf
= FALSE
;
115 m_destroystream
= FALSE
;
118 void wxStreamBuffer
::FreeBuffer()
121 free(m_buffer_start
);
124 wxStreamBuffer
::~wxStreamBuffer()
128 if ( m_destroystream
)
132 void wxStreamBuffer
::SetBufferIO(void *buffer_start
,
136 // start by freeing the old buffer
139 m_buffer_start
= (char *)buffer_start
;
140 m_buffer_end
= (char *)buffer_end
;
142 m_buffer_size
= m_buffer_end
- m_buffer_start
;
144 // if we own it, we free it
145 m_destroybuf
= !takeOwnership
;
150 void wxStreamBuffer
::SetBufferIO(size_t bufsize
)
152 // start by freeing the old buffer
157 char *buf
= (char *)malloc(bufsize
);
158 SetBufferIO(buf
, buf
+ bufsize
, TRUE
/* take ownership */);
160 else // no buffer size => no buffer
166 void wxStreamBuffer
::ResetBuffer()
168 wxCHECK_RET( m_stream
, _T("should have a stream in wxStreamBuffer") );
170 m_stream
->m_lasterror
= wxStream_NOERROR
;
171 m_stream
->m_lastcount
= 0;
172 if (m_mode
== read
&& m_flushable
)
173 m_buffer_pos
= m_buffer_end
;
175 m_buffer_pos
= m_buffer_start
;
178 // fill the buffer with as much data as possible (only for read buffers)
179 bool wxStreamBuffer
::FillBuffer()
181 wxCHECK_MSG( m_stream
, FALSE
, _T("should have a stream in wxStreamBuffer") );
183 size_t count
= m_stream
->OnSysRead(m_buffer_start
, m_buffer_size
);
187 m_buffer_end
= m_buffer_start
+ count
;
188 m_buffer_pos
= m_buffer_start
;
193 // write the buffer contents to the stream (only for write buffers)
194 bool wxStreamBuffer
::FlushBuffer()
196 wxCHECK_MSG( m_flushable
, FALSE
, _T("can't flush this buffer") );
198 // FIXME: what is this check for? (VZ)
199 if ( m_buffer_pos
== m_buffer_start
)
202 wxCHECK_MSG( m_stream
, FALSE
, _T("should have a stream in wxStreamBuffer") );
204 size_t current
= m_buffer_pos
- m_buffer_start
;
205 size_t count
= m_stream
->OnSysWrite(m_buffer_start
, current
);
206 if ( count
!= current
)
209 m_buffer_pos
= m_buffer_start
;
214 size_t wxStreamBuffer
::GetDataLeft()
216 /* Why is this done? RR. */
217 if ( m_buffer_pos
== m_buffer_end
&& m_flushable
)
220 return GetBytesLeft();
223 // copy up to size bytes from our buffer into the provided one
224 void wxStreamBuffer
::GetFromBuffer(void *buffer
, size_t size
)
226 // don't get more bytes than left in the buffer
227 size_t left
= GetBytesLeft();
232 memcpy(buffer
, m_buffer_pos
, size
);
233 m_buffer_pos
+= size
;
236 // copy the contents of the provided buffer into this one
237 void wxStreamBuffer
::PutToBuffer(const void *buffer
, size_t size
)
239 size_t left
= GetBytesLeft();
244 // we can't realloc the buffer, so just copy what we can
249 // realloc the buffer to have enough space for the data
250 size_t delta
= m_buffer_pos
- m_buffer_start
;
252 char *startOld
= m_buffer_start
;
253 m_buffer_size
+= size
;
254 m_buffer_start
= (char *)realloc(m_buffer_start
, m_buffer_size
);
255 if ( !m_buffer_start
)
257 // don't leak memory if realloc() failed
258 m_buffer_start
= startOld
;
259 m_buffer_size
-= size
;
261 // what else can we do?
265 // adjust the pointers invalidated by realloc()
266 m_buffer_pos
= m_buffer_start
+ delta
;
267 m_buffer_end
= m_buffer_start
+ m_buffer_size
;
271 memcpy(m_buffer_pos
, buffer
, size
);
272 m_buffer_pos
+= size
;
275 void wxStreamBuffer
::PutChar(char c
)
277 wxCHECK_RET( m_stream
, _T("should have a stream in wxStreamBuffer") );
279 // if we don't have buffer at all, just forward this call to the stream,
282 m_stream
->OnSysWrite(&c
, 1);
286 // otherwise check we have enough space left
287 if ( !GetDataLeft() && !FlushBuffer() )
290 SetError(wxStream_WRITE_ERR
);
295 m_stream
->m_lastcount
= 1;
300 char wxStreamBuffer
::Peek()
302 wxCHECK_MSG( m_stream
&& HasBuffer(), 0,
303 _T("should have the stream and the buffer in wxStreamBuffer") );
305 if ( !GetDataLeft() )
307 SetError(wxStream_READ_ERR
);
312 GetFromBuffer(&c
, 1);
318 char wxStreamBuffer
::GetChar()
320 wxCHECK_MSG( m_stream
, 0, _T("should have a stream in wxStreamBuffer") );
325 m_stream
->OnSysRead(&c
, 1);
329 if ( !GetDataLeft() )
331 SetError(wxStream_READ_ERR
);
336 GetFromBuffer(&c
, 1);
337 m_stream
->m_lastcount
= 1;
344 size_t wxStreamBuffer
::Read(void *buffer
, size_t size
)
346 wxCHECK_MSG( m_stream
, 0, _T("should have a stream in wxStreamBuffer") );
348 wxCHECK_MSG( m_mode
!= write
, 0, _T("can't read from this buffer") );
350 // lasterror is reset before all new IO calls
351 m_stream
->m_lasterror
= wxStream_NOERROR
;
355 m_stream
->m_lastcount
= m_stream
->OnSysRead(buffer
, size
);
357 else // we have a buffer, use it
359 size_t orig_size
= size
;
363 size_t left
= GetDataLeft();
365 // if the requested number of bytes if greater than the buffer
366 // size, read data in chunks
369 GetFromBuffer(buffer
, left
);
371 buffer
= (char *)buffer
+ left
;
375 SetError(wxStream_EOF
);
379 else // otherwise just do it in one gulp
381 GetFromBuffer(buffer
, size
);
386 m_stream
->m_lastcount
= orig_size
- size
;
389 return m_stream
->m_lastcount
;
392 // this should really be called "Copy()"
393 size_t wxStreamBuffer
::Read(wxStreamBuffer
*dbuf
)
395 wxCHECK_MSG( m_mode
!= write
, 0, _T("can't read from this buffer") );
397 char buf
[BUF_TEMP_SIZE
];
403 nRead
= Read(dbuf
, WXSIZEOF(buf
));
406 nRead
= dbuf
->Write(buf
, nRead
);
415 size_t wxStreamBuffer
::Write(const void *buffer
, size_t size
)
417 wxCHECK_MSG( m_stream
, 0, _T("should have a stream in wxStreamBuffer") );
418 wxCHECK_MSG( m_mode
!= read
, 0, _T("can't write to this buffer") );
420 // lasterror is reset before all new IO calls
421 m_stream
->m_lasterror
= wxStream_NOERROR
;
423 if ( !HasBuffer() && m_fixed
)
425 // no buffer, just forward the call to the stream
426 m_stream
->m_lastcount
= m_stream
->OnSysWrite(buffer
, size
);
428 else // we [may] have a buffer, use it
430 size_t orig_size
= size
;
434 size_t left
= GetBytesLeft();
436 // if the buffer is too large to fit in the stream buffer, split
437 // it in smaller parts
439 // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
440 // we always go to the second case.
442 // FIXME: fine, but if it fails we should (re)try writing it by
443 // chunks as this will (hopefully) always work (VZ)
444 if ( size
> left
&& m_fixed
)
446 PutToBuffer(buffer
, left
);
448 buffer
= (char *)buffer
+ left
;
450 if ( !FlushBuffer() )
452 SetError(wxStream_WRITE_ERR
);
457 m_buffer_pos
= m_buffer_start
;
459 else // we can do it in one gulp
461 PutToBuffer(buffer
, size
);
466 m_stream
->m_lastcount
= orig_size
- size
;
469 return m_stream
->m_lastcount
;
472 size_t wxStreamBuffer
::Write(wxStreamBuffer
*sbuf
)
474 wxCHECK_MSG( m_mode
!= read
, 0, _T("can't write to this buffer") );
475 wxCHECK_MSG( sbuf
->m_mode
!= write
, 0, _T("can't read from that buffer") );
477 char buf
[BUF_TEMP_SIZE
];
483 size_t nRead
= sbuf
->Read(buf
, WXSIZEOF(buf
));
486 nWrite
= Write(buf
, nRead
);
487 if ( nWrite
< nRead
)
489 // put back data we couldn't copy
490 wxInputStream
*in_stream
= (wxInputStream
*)sbuf
->GetStream();
492 in_stream
->Ungetch(buf
+ nWrite
, nRead
- nWrite
);
502 while ( nWrite
== WXSIZEOF(buf
) );
507 off_t wxStreamBuffer
::Seek(off_t pos
, wxSeekMode mode
)
511 off_t last_access
= GetLastAccess();
522 diff
= pos
+ GetIntPosition();
526 diff
= pos
+ last_access
;
530 wxFAIL_MSG( _T("invalid seek mode") );
532 return wxInvalidOffset
;
534 if (diff
< 0 || diff
> last_access
)
535 return wxInvalidOffset
;
536 SetIntPosition(diff
);
543 // We'll try to compute an internal position later ...
544 ret_off
= m_stream
->OnSysSeek(pos
, wxFromStart
);
549 diff
= pos
+ GetIntPosition();
551 if ( (diff
> last_access
) || (diff
< 0) )
553 // We must take into account the fact that we have read
554 // something previously.
555 ret_off
= m_stream
->OnSysSeek(diff
-last_access
, wxFromCurrent
);
561 SetIntPosition(diff
);
566 // Hard to compute: always seek to the requested position.
567 ret_off
= m_stream
->OnSysSeek(pos
, wxFromEnd
);
572 return wxInvalidOffset
;
575 off_t wxStreamBuffer
::Tell() const
577 off_t pos
= m_stream
->OnSysTell();
578 if ( pos
== wxInvalidOffset
)
579 return wxInvalidOffset
;
581 pos
+= GetIntPosition();
583 if ( m_mode
== read
&& m_flushable
)
584 pos
-= GetLastAccess();
589 // ----------------------------------------------------------------------------
591 // ----------------------------------------------------------------------------
593 wxStreamBase
::wxStreamBase()
595 m_lasterror
= wxStream_NOERROR
;
599 wxStreamBase
::~wxStreamBase()
603 size_t wxStreamBase
::OnSysRead(void *WXUNUSED(buffer
), size_t WXUNUSED(size
))
608 size_t wxStreamBase
::OnSysWrite(const void *WXUNUSED(buffer
), size_t WXUNUSED(bufsize
))
613 off_t wxStreamBase
::OnSysSeek(off_t
WXUNUSED(seek
), wxSeekMode
WXUNUSED(mode
))
615 return wxInvalidOffset
;
618 off_t wxStreamBase
::OnSysTell() const
620 return wxInvalidOffset
;
623 // ----------------------------------------------------------------------------
625 // ----------------------------------------------------------------------------
627 wxInputStream
::wxInputStream()
634 wxInputStream
::~wxInputStream()
639 bool wxInputStream
::Eof() const
641 wxInputStream
*self
= wxConstCast(this, wxInputStream
);
645 if ( GetLastError() == wxSTREAM_EOF
)
655 char *wxInputStream
::AllocSpaceWBack(size_t needed_size
)
657 // get number of bytes left from previous wback buffer
658 size_t toget
= m_wbacksize
- m_wbackcur
;
660 // allocate a buffer large enough to hold prev + new data
661 char *temp_b
= (char *)malloc(needed_size
+ toget
);
666 /* copy previous data (and free old buffer) if needed */
669 memmove(temp_b
+ needed_size
, m_wback
+ m_wbackcur
, toget
);
676 m_wbacksize
= needed_size
+ toget
;
681 size_t wxInputStream
::GetWBack(void *buf
, size_t bsize
)
683 size_t toget
= m_wbacksize
-m_wbackcur
;
691 memcpy(buf
, (m_wback
+m_wbackcur
), toget
);
694 if (m_wbackcur
== m_wbacksize
)
705 size_t wxInputStream
::Ungetch(const void *buf
, size_t bufsize
)
707 char *ptrback
= AllocSpaceWBack(bufsize
);
711 memcpy(ptrback
, buf
, bufsize
);
715 bool wxInputStream
::Ungetch(char c
)
717 void *ptrback
= AllocSpaceWBack(1);
721 *(char *)ptrback
= c
;
725 char wxInputStream
::GetC()
732 wxInputStream
& wxInputStream
::Read(void *buf
, size_t size
)
734 size_t retsize
= GetWBack(buf
, size
);
738 m_lasterror
= wxStream_NOERROR
;
742 buf
= (char *)buf
+ retsize
;
744 m_lastcount
= OnSysRead(buf
, size
) + retsize
;
748 char wxInputStream
::Peek()
752 if (m_lasterror
== wxStream_NOERROR
)
761 wxInputStream
& wxInputStream
::Read(wxOutputStream
& stream_out
)
763 char buf
[BUF_TEMP_SIZE
];
764 size_t bytes_read
= BUF_TEMP_SIZE
;
766 while (bytes_read
== BUF_TEMP_SIZE
)
768 bytes_read
= Read(buf
, bytes_read
).LastRead();
769 bytes_read
= stream_out
.Write(buf
, bytes_read
).LastWrite();
774 off_t wxInputStream
::SeekI(off_t pos
, wxSeekMode mode
)
776 /* Should be check and improve, just to remove a slight bug !
777 I don't know whether it should be put as well in wxFileInputStream::OnSysSeek ? */
778 if (m_lasterror
==wxSTREAM_EOF
)
779 m_lasterror
=wxSTREAM_NOERROR
;
781 /* A call to SeekI() will automatically invalidate any previous call
782 to Ungetch(), otherwise it would be possible to SeekI() to one
783 one position, unread some bytes there, SeekI() to another position
784 and the data would be corrupted.
786 GRG: Could add code here to try to navigate within the wback
787 buffer if possible, but is it really needed? It would only work
788 when seeking in wxFromCurrent mode, else it would invalidate
799 return OnSysSeek(pos
, mode
);
802 off_t wxInputStream
::TellI() const
804 /* GRG: Changed to make it compatible with the wback buffer */
805 off_t pos
= OnSysTell();
807 if (pos
!= wxInvalidOffset
)
808 pos
-= (m_wbacksize
- m_wbackcur
);
813 // --------------------
814 // Overloaded operators
815 // --------------------
818 wxInputStream
& wxInputStream
::operator>>(wxObject
*& obj
)
820 wxObjectInputStream
obj_s(*this);
821 obj
= obj_s
.LoadObject();
824 #endif // wxUSE_SERIAL
827 // ----------------------------------------------------------------------------
829 // ----------------------------------------------------------------------------
831 wxOutputStream
::wxOutputStream()
835 wxOutputStream
::~wxOutputStream()
839 void wxOutputStream
::PutC(char c
)
844 wxOutputStream
& wxOutputStream
::Write(const void *buffer
, size_t size
)
846 m_lastcount
= OnSysWrite(buffer
, size
);
850 wxOutputStream
& wxOutputStream
::Write(wxInputStream
& stream_in
)
852 stream_in
.Read(*this);
856 off_t wxOutputStream
::TellO() const
861 off_t wxOutputStream
::SeekO(off_t pos
, wxSeekMode mode
)
863 return OnSysSeek(pos
, mode
);
866 void wxOutputStream
::Sync()
871 wxOutputStream
& wxOutputStream
::operator<<(wxObject
& obj
)
873 wxObjectOutputStream
obj_s(*this);
874 obj_s
.SaveObject(obj
);
877 #endif // wxUSE_SERIAL
879 // ----------------------------------------------------------------------------
880 // wxCountingOutputStream
881 // ----------------------------------------------------------------------------
883 wxCountingOutputStream
::wxCountingOutputStream ()
888 size_t wxCountingOutputStream
::GetSize() const
893 size_t wxCountingOutputStream
::OnSysWrite(const void *WXUNUSED(buffer
),
896 m_currentPos
+= size
;
897 if (m_currentPos
> m_lastcount
)
898 m_lastcount
= m_currentPos
;
903 off_t wxCountingOutputStream
::OnSysSeek(off_t pos
, wxSeekMode mode
)
912 m_currentPos
= m_lastcount
+ pos
;
920 wxFAIL_MSG( _T("invalid seek mode") );
921 return wxInvalidOffset
;
924 if (m_currentPos
> m_lastcount
)
925 m_lastcount
= m_currentPos
;
930 off_t wxCountingOutputStream
::OnSysTell() const
935 // ----------------------------------------------------------------------------
936 // wxFilterInputStream
937 // ----------------------------------------------------------------------------
939 wxFilterInputStream
::wxFilterInputStream()
943 wxFilterInputStream
::wxFilterInputStream(wxInputStream
& stream
)
945 m_parent_i_stream
= &stream
;
948 wxFilterInputStream
::~wxFilterInputStream()
952 // ----------------------------------------------------------------------------
953 // wxFilterOutputStream
954 // ----------------------------------------------------------------------------
956 wxFilterOutputStream
::wxFilterOutputStream()
960 wxFilterOutputStream
::wxFilterOutputStream(wxOutputStream
& stream
)
962 m_parent_o_stream
= &stream
;
965 wxFilterOutputStream
::~wxFilterOutputStream()
969 // ----------------------------------------------------------------------------
970 // wxBufferedInputStream
971 // ----------------------------------------------------------------------------
973 wxBufferedInputStream
::wxBufferedInputStream(wxInputStream
& s
)
974 : wxFilterInputStream(s
)
976 m_i_streambuf
= new wxStreamBuffer(*this, wxStreamBuffer
::read
);
978 m_i_streambuf
->SetBufferIO(1024);
981 wxBufferedInputStream
::~wxBufferedInputStream()
983 m_parent_i_stream
->SeekI(-m_i_streambuf
->GetBytesLeft(), wxFromCurrent
);
985 delete m_i_streambuf
;
988 char wxBufferedInputStream
::Peek()
990 return m_i_streambuf
->Peek();
993 wxInputStream
& wxBufferedInputStream
::Read(void *buf
, size_t size
)
997 retsize
= GetWBack(buf
, size
);
998 m_lastcount
= retsize
;
1001 m_lasterror
= wxStream_NOERROR
;
1005 buf
= (char *)buf
+ retsize
;
1007 m_i_streambuf
->Read(buf
, size
);
1012 off_t wxBufferedInputStream
::SeekI(off_t pos
, wxSeekMode mode
)
1014 return m_i_streambuf
->Seek(pos
, mode
);
1017 off_t wxBufferedInputStream
::TellI() const
1019 return m_i_streambuf
->Tell();
1022 size_t wxBufferedInputStream
::OnSysRead(void *buffer
, size_t bufsize
)
1024 return m_parent_i_stream
->Read(buffer
, bufsize
).LastRead();
1027 off_t wxBufferedInputStream
::OnSysSeek(off_t seek
, wxSeekMode mode
)
1029 return m_parent_i_stream
->SeekI(seek
, mode
);
1032 off_t wxBufferedInputStream
::OnSysTell() const
1034 return m_parent_i_stream
->TellI();
1037 // ----------------------------------------------------------------------------
1038 // wxBufferedOutputStream
1039 // ----------------------------------------------------------------------------
1041 wxBufferedOutputStream
::wxBufferedOutputStream(wxOutputStream
& s
)
1042 : wxFilterOutputStream(s
)
1044 m_o_streambuf
= new wxStreamBuffer(*this, wxStreamBuffer
::write
);
1045 m_o_streambuf
->SetBufferIO(1024);
1048 wxBufferedOutputStream
::~wxBufferedOutputStream()
1051 delete m_o_streambuf
;
1054 wxOutputStream
& wxBufferedOutputStream
::Write(const void *buffer
, size_t size
)
1057 m_o_streambuf
->Write(buffer
, size
);
1061 off_t wxBufferedOutputStream
::SeekO(off_t pos
, wxSeekMode mode
)
1064 return m_o_streambuf
->Seek(pos
, mode
);
1067 off_t wxBufferedOutputStream
::TellO() const
1069 return m_o_streambuf
->Tell();
1072 void wxBufferedOutputStream
::Sync()
1074 m_o_streambuf
->FlushBuffer();
1075 m_parent_o_stream
->Sync();
1078 size_t wxBufferedOutputStream
::OnSysWrite(const void *buffer
, size_t bufsize
)
1080 return m_parent_o_stream
->Write(buffer
, bufsize
).LastWrite();
1083 off_t wxBufferedOutputStream
::OnSysSeek(off_t seek
, wxSeekMode mode
)
1085 return m_parent_o_stream
->SeekO(seek
, mode
);
1088 off_t wxBufferedOutputStream
::OnSysTell() const
1090 return m_parent_o_stream
->TellO();
1093 size_t wxBufferedOutputStream
::GetSize() const
1095 return m_parent_o_stream
->GetSize() + m_o_streambuf
->GetIntPosition();
1098 // ----------------------------------------------------------------------------
1099 // Some IOManip function
1100 // ----------------------------------------------------------------------------
1102 wxOutputStream
& wxEndL(wxOutputStream
& stream
)
1105 return stream
.Write("\r\n", 2);
1108 return stream
.Write("\r", 1);
1110 return stream
.Write("\n", 1);