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