fixed a huge memory leak in wxStreamBuffer
[wxWidgets.git] / src / common / stream.cpp
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,
6 // general code review
7 // Created: 11/07/98
8 // RCS-ID: $Id$
9 // Copyright: (c) Guilhem Lavaux
10 // Licence: wxWindows license
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #ifdef __GNUG__
22 #pragma implementation "stream.h"
23 #endif
24
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 #ifndef WX_PRECOMP
33 #include "wx/defs.h"
34 #endif
35
36 #if wxUSE_STREAMS
37
38 #include <ctype.h>
39 #include "wx/stream.h"
40 #include "wx/datstrm.h"
41 #include "wx/textfile.h"
42
43 // ----------------------------------------------------------------------------
44 // constants
45 // ----------------------------------------------------------------------------
46
47 // the temporary buffer size used when copying from stream to stream
48 #define BUF_TEMP_SIZE 10000
49
50 // ============================================================================
51 // implementation
52 // ============================================================================
53
54 // ----------------------------------------------------------------------------
55 // wxStreamBuffer
56 // ----------------------------------------------------------------------------
57
58 void wxStreamBuffer::SetError(wxStreamError err)
59 {
60 if ( m_stream->m_lasterror == wxStream_NOERROR )
61 m_stream->m_lasterror = err;
62 }
63
64 void wxStreamBuffer::InitBuffer()
65 {
66 m_buffer_start =
67 m_buffer_end =
68 m_buffer_pos = NULL;
69 m_buffer_size = 0;
70
71 // if we are going to allocate the buffer, we should free it later as well
72 m_destroybuf = TRUE;
73 }
74
75 void wxStreamBuffer::Init()
76 {
77 InitBuffer();
78
79 m_fixed = TRUE;
80 }
81
82 wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
83 {
84 Init();
85
86 m_stream = &stream;
87 m_mode = mode;
88
89 m_flushable = TRUE;
90 m_destroystream = FALSE;
91 }
92
93 wxStreamBuffer::wxStreamBuffer(BufMode mode)
94 {
95 Init();
96
97 wxASSERT_MSG(mode != read_write, wxT("you have to use the other ctor for read_write mode") );
98 if ( mode == read )
99 m_stream = new wxInputStream;
100 else if ( mode == write)
101 m_stream = new wxOutputStream;
102 else
103 m_stream = NULL;
104
105 m_mode = mode;
106
107 m_flushable = FALSE;
108 m_destroystream = TRUE;
109 }
110
111 wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer)
112 {
113 // doing this has big chances to lead to a crashwhen the source buffer is
114 // destroyed (otherwise assume the caller knows what he does)
115 wxASSERT_MSG( !buffer.m_destroybuf && !buffer.m_destroystream,
116 _T("it's a bad idea to copy this buffer") );
117
118 m_buffer_start = buffer.m_buffer_start;
119 m_buffer_end = buffer.m_buffer_end;
120 m_buffer_pos = buffer.m_buffer_pos;
121 m_buffer_size = buffer.m_buffer_size;
122 m_fixed = buffer.m_fixed;
123 m_flushable = buffer.m_flushable;
124 m_stream = buffer.m_stream;
125 m_mode = buffer.m_mode;
126 m_destroybuf = FALSE;
127 m_destroystream = FALSE;
128 }
129
130 void wxStreamBuffer::FreeBuffer()
131 {
132 if ( m_destroybuf )
133 free(m_buffer_start);
134 }
135
136 wxStreamBuffer::~wxStreamBuffer()
137 {
138 FreeBuffer();
139
140 if ( m_destroystream )
141 delete m_stream;
142 }
143
144 wxInputStream *wxStreamBuffer::GetInputStream() const
145 {
146 return m_mode == write ? NULL : (wxInputStream *)m_stream;
147 }
148
149 wxOutputStream *wxStreamBuffer::GetOutputStream() const
150 {
151 return m_mode == read ? NULL : (wxOutputStream *)m_stream;
152 }
153
154 void wxStreamBuffer::SetBufferIO(void *buffer_start,
155 void *buffer_end,
156 bool takeOwnership)
157 {
158 SetBufferIO(buffer_start, (char *)buffer_end - (char *)buffer_start,
159 takeOwnership);
160 }
161
162 void wxStreamBuffer::SetBufferIO(void *start,
163 size_t len,
164 bool takeOwnership)
165 {
166 // start by freeing the old buffer
167 FreeBuffer();
168
169 m_buffer_start = (char *)start;
170 m_buffer_end = m_buffer_start + len;
171
172 m_buffer_size = len;
173
174 // if we own it, we free it
175 m_destroybuf = takeOwnership;
176
177 ResetBuffer();
178 }
179
180 void wxStreamBuffer::SetBufferIO(size_t bufsize)
181 {
182 // start by freeing the old buffer
183 FreeBuffer();
184
185 if ( bufsize )
186 {
187 SetBufferIO(malloc(bufsize), bufsize, TRUE /* take ownership */);
188 }
189 else // no buffer size => no buffer
190 {
191 InitBuffer();
192 }
193 }
194
195 void wxStreamBuffer::ResetBuffer()
196 {
197 wxCHECK_RET( m_stream, _T("should have a stream in wxStreamBuffer") );
198
199 m_stream->m_lasterror = wxStream_NOERROR;
200 m_stream->m_lastcount = 0;
201 if (m_mode == read && m_flushable)
202 m_buffer_pos = m_buffer_end;
203 else
204 m_buffer_pos = m_buffer_start;
205 }
206
207 // fill the buffer with as much data as possible (only for read buffers)
208 bool wxStreamBuffer::FillBuffer()
209 {
210 wxInputStream *inStream = GetInputStream();
211
212 wxCHECK_MSG( inStream, FALSE, _T("should have a stream in wxStreamBuffer") );
213
214 size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size);
215 if ( !count )
216 return FALSE;
217
218 m_buffer_end = m_buffer_start + count;
219 m_buffer_pos = m_buffer_start;
220
221 return TRUE;
222 }
223
224 // write the buffer contents to the stream (only for write buffers)
225 bool wxStreamBuffer::FlushBuffer()
226 {
227 wxCHECK_MSG( m_flushable, FALSE, _T("can't flush this buffer") );
228
229 // FIXME: what is this check for? (VZ)
230 if ( m_buffer_pos == m_buffer_start )
231 return FALSE;
232
233 wxOutputStream *outStream = GetOutputStream();
234
235 wxCHECK_MSG( outStream, FALSE, _T("should have a stream in wxStreamBuffer") );
236
237 size_t current = m_buffer_pos - m_buffer_start;
238 size_t count = outStream->OnSysWrite(m_buffer_start, current);
239 if ( count != current )
240 return FALSE;
241
242 m_buffer_pos = m_buffer_start;
243
244 return TRUE;
245 }
246
247 size_t wxStreamBuffer::GetDataLeft()
248 {
249 /* Why is this done? RR. */
250 if ( m_buffer_pos == m_buffer_end && m_flushable)
251 FillBuffer();
252
253 return GetBytesLeft();
254 }
255
256 // copy up to size bytes from our buffer into the provided one
257 void wxStreamBuffer::GetFromBuffer(void *buffer, size_t size)
258 {
259 // don't get more bytes than left in the buffer
260 size_t left = GetBytesLeft();
261
262 if ( size > left )
263 size = left;
264
265 memcpy(buffer, m_buffer_pos, size);
266 m_buffer_pos += size;
267 }
268
269 // copy the contents of the provided buffer into this one
270 void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size)
271 {
272 size_t left = GetBytesLeft();
273 if ( size > left )
274 {
275 if ( m_fixed )
276 {
277 // we can't realloc the buffer, so just copy what we can
278 size = left;
279 }
280 else // !m_fixed
281 {
282 // realloc the buffer to have enough space for the data
283 size_t delta = m_buffer_pos - m_buffer_start;
284
285 char *startOld = m_buffer_start;
286 m_buffer_size += size;
287 m_buffer_start = (char *)realloc(m_buffer_start, m_buffer_size);
288 if ( !m_buffer_start )
289 {
290 // don't leak memory if realloc() failed
291 m_buffer_start = startOld;
292 m_buffer_size -= size;
293
294 // what else can we do?
295 return;
296 }
297
298 // adjust the pointers invalidated by realloc()
299 m_buffer_pos = m_buffer_start + delta;
300 m_buffer_end = m_buffer_start + m_buffer_size;
301 }
302 }
303
304 memcpy(m_buffer_pos, buffer, size);
305 m_buffer_pos += size;
306 }
307
308 void wxStreamBuffer::PutChar(char c)
309 {
310 wxOutputStream *outStream = GetOutputStream();
311
312 wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") );
313
314 // if we don't have buffer at all, just forward this call to the stream,
315 if ( !HasBuffer() )
316 {
317 outStream->OnSysWrite(&c, 1);
318 }
319 else
320 {
321 // otherwise check we have enough space left
322 if ( !GetDataLeft() && !FlushBuffer() )
323 {
324 // we don't
325 SetError(wxStream_WRITE_ERR);
326 }
327 else
328 {
329 PutToBuffer(&c, 1);
330 m_stream->m_lastcount = 1;
331 }
332 }
333 }
334
335 char wxStreamBuffer::Peek()
336 {
337 wxCHECK_MSG( m_stream && HasBuffer(), 0,
338 _T("should have the stream and the buffer in wxStreamBuffer") );
339
340 if ( !GetDataLeft() )
341 {
342 SetError(wxStream_READ_ERR);
343 return 0;
344 }
345
346 char c;
347 GetFromBuffer(&c, 1);
348 m_buffer_pos--;
349
350 return c;
351 }
352
353 char wxStreamBuffer::GetChar()
354 {
355 wxInputStream *inStream = GetInputStream();
356
357 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
358
359 char c;
360 if ( !HasBuffer() )
361 {
362 inStream->OnSysRead(&c, 1);
363 }
364 else
365 {
366 if ( !GetDataLeft() )
367 {
368 SetError(wxStream_READ_ERR);
369 c = 0;
370 }
371 else
372 {
373 GetFromBuffer(&c, 1);
374 m_stream->m_lastcount = 1;
375 }
376 }
377
378 return c;
379 }
380
381 size_t wxStreamBuffer::Read(void *buffer, size_t size)
382 {
383 wxInputStream *inStream = GetInputStream();
384
385 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
386
387 // lasterror is reset before all new IO calls
388 m_stream->m_lasterror = wxStream_NOERROR;
389
390 if ( !HasBuffer() )
391 {
392 m_stream->m_lastcount = inStream->OnSysRead(buffer, size);
393 }
394 else // we have a buffer, use it
395 {
396 size_t orig_size = size;
397
398 while ( size > 0 )
399 {
400 size_t left = GetDataLeft();
401
402 // if the requested number of bytes if greater than the buffer
403 // size, read data in chunks
404 if ( size > left )
405 {
406 GetFromBuffer(buffer, left);
407 size -= left;
408 buffer = (char *)buffer + left;
409
410 if ( !FillBuffer() )
411 {
412 SetError(wxStream_EOF);
413 break;
414 }
415 }
416 else // otherwise just do it in one gulp
417 {
418 GetFromBuffer(buffer, size);
419 size = 0;
420 }
421 }
422
423 m_stream->m_lastcount = orig_size - size;
424 }
425
426 return m_stream->m_lastcount;
427 }
428
429 // this should really be called "Copy()"
430 size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf)
431 {
432 wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") );
433
434 char buf[BUF_TEMP_SIZE];
435 size_t nRead,
436 total = 0;
437
438 do
439 {
440 nRead = Read(dbuf, WXSIZEOF(buf));
441 if ( nRead )
442 {
443 nRead = dbuf->Write(buf, nRead);
444 total += nRead;
445 }
446 }
447 while ( nRead );
448
449 return total;
450 }
451
452 size_t wxStreamBuffer::Write(const void *buffer, size_t size)
453 {
454 wxOutputStream *outStream = GetOutputStream();
455
456 wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") );
457
458 // lasterror is reset before all new IO calls
459 m_stream->m_lasterror = wxStream_NOERROR;
460
461 if ( !HasBuffer() && m_fixed )
462 {
463 // no buffer, just forward the call to the stream
464 m_stream->m_lastcount = outStream->OnSysWrite(buffer, size);
465 }
466 else // we [may] have a buffer, use it
467 {
468 size_t orig_size = size;
469
470 while ( size > 0 )
471 {
472 size_t left = GetBytesLeft();
473
474 // if the buffer is too large to fit in the stream buffer, split
475 // it in smaller parts
476 //
477 // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
478 // we always go to the second case.
479 //
480 // FIXME: fine, but if it fails we should (re)try writing it by
481 // chunks as this will (hopefully) always work (VZ)
482 if ( size > left && m_fixed )
483 {
484 PutToBuffer(buffer, left);
485 size -= left;
486 buffer = (char *)buffer + left;
487
488 if ( !FlushBuffer() )
489 {
490 SetError(wxStream_WRITE_ERR);
491
492 break;
493 }
494
495 m_buffer_pos = m_buffer_start;
496 }
497 else // we can do it in one gulp
498 {
499 PutToBuffer(buffer, size);
500 size = 0;
501 }
502 }
503
504 m_stream->m_lastcount = orig_size - size;
505 }
506
507 return m_stream->m_lastcount;
508 }
509
510 size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf)
511 {
512 wxCHECK_MSG( m_mode != read, 0, _T("can't write to this buffer") );
513 wxCHECK_MSG( sbuf->m_mode != write, 0, _T("can't read from that buffer") );
514
515 char buf[BUF_TEMP_SIZE];
516 size_t nWrite,
517 total = 0;
518
519 do
520 {
521 size_t nRead = sbuf->Read(buf, WXSIZEOF(buf));
522 if ( nRead )
523 {
524 nWrite = Write(buf, nRead);
525 if ( nWrite < nRead )
526 {
527 // put back data we couldn't copy
528 wxInputStream *in_stream = (wxInputStream *)sbuf->GetStream();
529
530 in_stream->Ungetch(buf + nWrite, nRead - nWrite);
531 }
532
533 total += nWrite;
534 }
535 else
536 {
537 nWrite = 0;
538 }
539 }
540 while ( nWrite == WXSIZEOF(buf) );
541
542 return total;
543 }
544
545 off_t wxStreamBuffer::Seek(off_t pos, wxSeekMode mode)
546 {
547 off_t ret_off, diff;
548
549 off_t last_access = GetLastAccess();
550
551 if ( !m_flushable )
552 {
553 switch (mode)
554 {
555 case wxFromStart:
556 diff = pos;
557 break;
558
559 case wxFromCurrent:
560 diff = pos + GetIntPosition();
561 break;
562
563 case wxFromEnd:
564 diff = pos + last_access;
565 break;
566
567 default:
568 wxFAIL_MSG( _T("invalid seek mode") );
569
570 return wxInvalidOffset;
571 }
572 if (diff < 0 || diff > last_access)
573 return wxInvalidOffset;
574 SetIntPosition(diff);
575 return diff;
576 }
577
578 switch ( mode )
579 {
580 case wxFromStart:
581 // We'll try to compute an internal position later ...
582 ret_off = m_stream->OnSysSeek(pos, wxFromStart);
583 ResetBuffer();
584 return ret_off;
585
586 case wxFromCurrent:
587 diff = pos + GetIntPosition();
588
589 if ( (diff > last_access) || (diff < 0) )
590 {
591 // We must take into account the fact that we have read
592 // something previously.
593 ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent);
594 ResetBuffer();
595 return ret_off;
596 }
597 else
598 {
599 SetIntPosition(diff);
600 return pos;
601 }
602
603 case wxFromEnd:
604 // Hard to compute: always seek to the requested position.
605 ret_off = m_stream->OnSysSeek(pos, wxFromEnd);
606 ResetBuffer();
607 return ret_off;
608 }
609
610 return wxInvalidOffset;
611 }
612
613 off_t wxStreamBuffer::Tell() const
614 {
615 off_t pos = m_stream->OnSysTell();
616 if ( pos == wxInvalidOffset )
617 return wxInvalidOffset;
618
619 pos += GetIntPosition();
620
621 if ( m_mode == read && m_flushable )
622 pos -= GetLastAccess();
623
624 return pos;
625 }
626
627 // ----------------------------------------------------------------------------
628 // wxStreamBase
629 // ----------------------------------------------------------------------------
630
631 wxStreamBase::wxStreamBase()
632 {
633 m_lasterror = wxStream_NOERROR;
634 m_lastcount = 0;
635 }
636
637 wxStreamBase::~wxStreamBase()
638 {
639 }
640
641 off_t wxStreamBase::OnSysSeek(off_t WXUNUSED(seek), wxSeekMode WXUNUSED(mode))
642 {
643 return wxInvalidOffset;
644 }
645
646 off_t wxStreamBase::OnSysTell() const
647 {
648 return wxInvalidOffset;
649 }
650
651 // ----------------------------------------------------------------------------
652 // wxInputStream
653 // ----------------------------------------------------------------------------
654
655 wxInputStream::wxInputStream()
656 {
657 m_wback = NULL;
658 m_wbacksize =
659 m_wbackcur = 0;
660 }
661
662 wxInputStream::~wxInputStream()
663 {
664 free(m_wback);
665 }
666
667 size_t wxInputStream::OnSysRead(void * WXUNUSED(buffer),
668 size_t WXUNUSED(bufsize))
669 {
670 return 0;
671 }
672
673 bool wxInputStream::Eof() const
674 {
675 wxInputStream *self = wxConstCast(this, wxInputStream);
676
677 char c;
678 self->Read(&c, 1);
679
680 // some streams can know that they're at EOF before actually trying to
681 // read beyond the end of stream (e.g. files) while others have no way of
682 // knowing it, so to provide the same behaviour in all cases we only
683 // return TRUE from here if the character really couldn't be read
684 if ( !self->LastRead() && GetLastError() == wxSTREAM_EOF )
685 {
686 return TRUE;
687 }
688
689 self->Ungetch(c);
690
691 return FALSE;
692 }
693
694 char *wxInputStream::AllocSpaceWBack(size_t needed_size)
695 {
696 // get number of bytes left from previous wback buffer
697 size_t toget = m_wbacksize - m_wbackcur;
698
699 // allocate a buffer large enough to hold prev + new data
700 char *temp_b = (char *)malloc(needed_size + toget);
701
702 if (!temp_b)
703 return NULL;
704
705 /* copy previous data (and free old buffer) if needed */
706 if (m_wback)
707 {
708 memmove(temp_b + needed_size, m_wback + m_wbackcur, toget);
709 free(m_wback);
710 }
711
712 /* done */
713 m_wback = temp_b;
714 m_wbackcur = 0;
715 m_wbacksize = needed_size + toget;
716
717 return m_wback;
718 }
719
720 size_t wxInputStream::GetWBack(void *buf, size_t bsize)
721 {
722 if (!m_wback)
723 return 0;
724
725 // how many bytes do we have in the buffer?
726 size_t toget = m_wbacksize - m_wbackcur;
727
728 if ( bsize < toget )
729 {
730 // we won't read everything
731 toget = bsize;
732 }
733
734 // copy the data from the cache
735 memcpy(buf, m_wback + m_wbackcur, toget);
736
737 m_wbackcur += toget;
738 if ( m_wbackcur == m_wbacksize )
739 {
740 // TODO: should we really free it here all the time? maybe keep it?
741 free(m_wback);
742 m_wback = NULL;
743 m_wbacksize = 0;
744 m_wbackcur = 0;
745 }
746
747 // return the number of bytes copied
748 return toget;
749 }
750
751 size_t wxInputStream::Ungetch(const void *buf, size_t bufsize)
752 {
753 char *ptrback = AllocSpaceWBack(bufsize);
754 if (!ptrback)
755 return 0;
756
757 memcpy(ptrback, buf, bufsize);
758 return bufsize;
759 }
760
761 bool wxInputStream::Ungetch(char c)
762 {
763 void *ptrback = AllocSpaceWBack(1);
764 if (!ptrback)
765 return FALSE;
766
767 *(char *)ptrback = c;
768 return TRUE;
769 }
770
771 char wxInputStream::GetC()
772 {
773 char c;
774 Read(&c, 1);
775 return c;
776 }
777
778 wxInputStream& wxInputStream::Read(void *buf, size_t size)
779 {
780 size_t retsize = GetWBack(buf, size);
781 if (retsize == size)
782 {
783 m_lastcount = size;
784 m_lasterror = wxStream_NOERROR;
785 return *this;
786 }
787 size -= retsize;
788 buf = (char *)buf + retsize;
789
790 m_lastcount = OnSysRead(buf, size) + retsize;
791 return *this;
792 }
793
794 char wxInputStream::Peek()
795 {
796 char c;
797 Read(&c, 1);
798 if (m_lasterror == wxStream_NOERROR)
799 {
800 Ungetch(c);
801 return c;
802 }
803
804 return 0;
805 }
806
807 wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
808 {
809 char buf[BUF_TEMP_SIZE];
810 size_t bytes_read = BUF_TEMP_SIZE;
811
812 while (bytes_read == BUF_TEMP_SIZE)
813 {
814 bytes_read = Read(buf, bytes_read).LastRead();
815 bytes_read = stream_out.Write(buf, bytes_read).LastWrite();
816 }
817 return *this;
818 }
819
820 off_t wxInputStream::SeekI(off_t pos, wxSeekMode mode)
821 {
822 /* Should be check and improve, just to remove a slight bug !
823 I don't know whether it should be put as well in wxFileInputStream::OnSysSeek ? */
824 if (m_lasterror==wxSTREAM_EOF)
825 m_lasterror=wxSTREAM_NOERROR;
826
827 /* A call to SeekI() will automatically invalidate any previous call
828 to Ungetch(), otherwise it would be possible to SeekI() to one
829 one position, unread some bytes there, SeekI() to another position
830 and the data would be corrupted.
831
832 GRG: Could add code here to try to navigate within the wback
833 buffer if possible, but is it really needed? It would only work
834 when seeking in wxFromCurrent mode, else it would invalidate
835 anyway...
836 */
837 if (m_wback)
838 {
839 free(m_wback);
840 m_wback = NULL;
841 m_wbacksize = 0;
842 m_wbackcur = 0;
843 }
844
845 return OnSysSeek(pos, mode);
846 }
847
848 off_t wxInputStream::TellI() const
849 {
850 /* GRG: Changed to make it compatible with the wback buffer */
851 off_t pos = OnSysTell();
852
853 if (pos != wxInvalidOffset)
854 pos -= (m_wbacksize - m_wbackcur);
855
856 return pos;
857 }
858
859
860 // ----------------------------------------------------------------------------
861 // wxOutputStream
862 // ----------------------------------------------------------------------------
863
864 wxOutputStream::wxOutputStream()
865 {
866 }
867
868 wxOutputStream::~wxOutputStream()
869 {
870 }
871
872 size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
873 size_t WXUNUSED(bufsize))
874 {
875 return 0;
876 }
877
878 void wxOutputStream::PutC(char c)
879 {
880 Write(&c, 1);
881 }
882
883 wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size)
884 {
885 m_lastcount = OnSysWrite(buffer, size);
886 return *this;
887 }
888
889 wxOutputStream& wxOutputStream::Write(wxInputStream& stream_in)
890 {
891 stream_in.Read(*this);
892 return *this;
893 }
894
895 off_t wxOutputStream::TellO() const
896 {
897 return OnSysTell();
898 }
899
900 off_t wxOutputStream::SeekO(off_t pos, wxSeekMode mode)
901 {
902 return OnSysSeek(pos, mode);
903 }
904
905 void wxOutputStream::Sync()
906 {
907 }
908
909
910 // ----------------------------------------------------------------------------
911 // wxCountingOutputStream
912 // ----------------------------------------------------------------------------
913
914 wxCountingOutputStream::wxCountingOutputStream ()
915 {
916 m_currentPos = 0;
917 }
918
919 size_t wxCountingOutputStream::GetSize() const
920 {
921 return m_lastcount;
922 }
923
924 size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer),
925 size_t size)
926 {
927 m_currentPos += size;
928 if (m_currentPos > m_lastcount)
929 m_lastcount = m_currentPos;
930
931 return m_currentPos;
932 }
933
934 off_t wxCountingOutputStream::OnSysSeek(off_t pos, wxSeekMode mode)
935 {
936 switch ( mode )
937 {
938 case wxFromStart:
939 m_currentPos = pos;
940 break;
941
942 case wxFromEnd:
943 m_currentPos = m_lastcount + pos;
944 break;
945
946 case wxFromCurrent:
947 m_currentPos += pos;
948 break;
949
950 default:
951 wxFAIL_MSG( _T("invalid seek mode") );
952 return wxInvalidOffset;
953 }
954
955 if (m_currentPos > m_lastcount)
956 m_lastcount = m_currentPos;
957
958 return m_currentPos;
959 }
960
961 off_t wxCountingOutputStream::OnSysTell() const
962 {
963 return m_currentPos;
964 }
965
966 // ----------------------------------------------------------------------------
967 // wxFilterInputStream
968 // ----------------------------------------------------------------------------
969
970 wxFilterInputStream::wxFilterInputStream()
971 {
972 m_parent_i_stream = NULL;
973 }
974
975 wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
976 {
977 m_parent_i_stream = &stream;
978 }
979
980 wxFilterInputStream::~wxFilterInputStream()
981 {
982 }
983
984 // ----------------------------------------------------------------------------
985 // wxFilterOutputStream
986 // ----------------------------------------------------------------------------
987
988 wxFilterOutputStream::wxFilterOutputStream()
989 {
990 m_parent_o_stream = NULL;
991 }
992
993 wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
994 {
995 m_parent_o_stream = &stream;
996 }
997
998 wxFilterOutputStream::~wxFilterOutputStream()
999 {
1000 }
1001
1002 // ----------------------------------------------------------------------------
1003 // wxBufferedInputStream
1004 // ----------------------------------------------------------------------------
1005
1006 wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
1007 wxStreamBuffer *buffer)
1008 : wxFilterInputStream(s)
1009 {
1010 if ( buffer )
1011 {
1012 // use the buffer provided by the user
1013 m_i_streambuf = buffer;
1014 }
1015 else // create a default buffer
1016 {
1017 m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
1018
1019 m_i_streambuf->SetBufferIO(1024);
1020 }
1021 }
1022
1023 wxBufferedInputStream::~wxBufferedInputStream()
1024 {
1025 m_parent_i_stream->SeekI(-(off_t)m_i_streambuf->GetBytesLeft(),
1026 wxFromCurrent);
1027
1028 delete m_i_streambuf;
1029 }
1030
1031 char wxBufferedInputStream::Peek()
1032 {
1033 return m_i_streambuf->Peek();
1034 }
1035
1036 wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
1037 {
1038 // reset the error flag
1039 m_lasterror = wxStream_NOERROR;
1040
1041 // first read from the already cached data
1042 m_lastcount = GetWBack(buf, size);
1043
1044 // do we have to read anything more?
1045 if ( m_lastcount < size )
1046 {
1047 size -= m_lastcount;
1048 buf = (char *)buf + m_lastcount;
1049
1050 // the call to wxStreamBuffer::Read() below will reset our m_lastcount,
1051 // so save it
1052 size_t countOld = m_lastcount;
1053
1054 m_i_streambuf->Read(buf, size);
1055
1056 m_lastcount += countOld;
1057 }
1058
1059 return *this;
1060 }
1061
1062 off_t wxBufferedInputStream::SeekI(off_t pos, wxSeekMode mode)
1063 {
1064 return m_i_streambuf->Seek(pos, mode);
1065 }
1066
1067 off_t wxBufferedInputStream::TellI() const
1068 {
1069 return m_i_streambuf->Tell();
1070 }
1071
1072 size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize)
1073 {
1074 return m_parent_i_stream->Read(buffer, bufsize).LastRead();
1075 }
1076
1077 off_t wxBufferedInputStream::OnSysSeek(off_t seek, wxSeekMode mode)
1078 {
1079 return m_parent_i_stream->SeekI(seek, mode);
1080 }
1081
1082 off_t wxBufferedInputStream::OnSysTell() const
1083 {
1084 return m_parent_i_stream->TellI();
1085 }
1086
1087 void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer)
1088 {
1089 wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") );
1090
1091 delete m_i_streambuf;
1092 m_i_streambuf = buffer;
1093 }
1094
1095 // ----------------------------------------------------------------------------
1096 // wxBufferedOutputStream
1097 // ----------------------------------------------------------------------------
1098
1099 wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s,
1100 wxStreamBuffer *buffer)
1101 : wxFilterOutputStream(s)
1102 {
1103 if ( buffer )
1104 {
1105 m_o_streambuf = buffer;
1106 }
1107 else // create a default one
1108 {
1109 m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
1110
1111 m_o_streambuf->SetBufferIO(1024);
1112 }
1113 }
1114
1115 wxBufferedOutputStream::~wxBufferedOutputStream()
1116 {
1117 Sync();
1118 delete m_o_streambuf;
1119 }
1120
1121 wxOutputStream& wxBufferedOutputStream::Write(const void *buffer, size_t size)
1122 {
1123 m_lastcount = 0;
1124 m_o_streambuf->Write(buffer, size);
1125 return *this;
1126 }
1127
1128 off_t wxBufferedOutputStream::SeekO(off_t pos, wxSeekMode mode)
1129 {
1130 Sync();
1131 return m_o_streambuf->Seek(pos, mode);
1132 }
1133
1134 off_t wxBufferedOutputStream::TellO() const
1135 {
1136 return m_o_streambuf->Tell();
1137 }
1138
1139 void wxBufferedOutputStream::Sync()
1140 {
1141 m_o_streambuf->FlushBuffer();
1142 m_parent_o_stream->Sync();
1143 }
1144
1145 size_t wxBufferedOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
1146 {
1147 return m_parent_o_stream->Write(buffer, bufsize).LastWrite();
1148 }
1149
1150 off_t wxBufferedOutputStream::OnSysSeek(off_t seek, wxSeekMode mode)
1151 {
1152 return m_parent_o_stream->SeekO(seek, mode);
1153 }
1154
1155 off_t wxBufferedOutputStream::OnSysTell() const
1156 {
1157 return m_parent_o_stream->TellO();
1158 }
1159
1160 size_t wxBufferedOutputStream::GetSize() const
1161 {
1162 return m_parent_o_stream->GetSize() + m_o_streambuf->GetIntPosition();
1163 }
1164
1165 void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer)
1166 {
1167 wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") );
1168
1169 delete m_o_streambuf;
1170 m_o_streambuf = buffer;
1171 }
1172
1173 // ----------------------------------------------------------------------------
1174 // Some IOManip function
1175 // ----------------------------------------------------------------------------
1176
1177 wxOutputStream& wxEndL(wxOutputStream& stream)
1178 {
1179 static const wxChar *eol = wxTextFile::GetEOL();
1180
1181 return stream.Write(eol, wxStrlen(eol));
1182 }
1183
1184 #endif
1185 // wxUSE_STREAMS