added wxRTTI macros to stream classes (patch 1687073)
[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 IMPLEMENT_ABSTRACT_CLASS(wxStreamBase, wxObject)
661
662 wxStreamBase::wxStreamBase()
663 {
664 m_lasterror = wxSTREAM_NO_ERROR;
665 m_lastcount = 0;
666 }
667
668 wxStreamBase::~wxStreamBase()
669 {
670 }
671
672 size_t wxStreamBase::GetSize() const
673 {
674 wxFileOffset length = GetLength();
675 if ( length == (wxFileOffset)wxInvalidOffset )
676 return 0;
677
678 const size_t len = wx_truncate_cast(size_t, length);
679 wxASSERT_MSG( len == length + size_t(0), _T("large files not supported") );
680
681 return len;
682 }
683
684 wxFileOffset wxStreamBase::OnSysSeek(wxFileOffset WXUNUSED(seek), wxSeekMode WXUNUSED(mode))
685 {
686 return wxInvalidOffset;
687 }
688
689 wxFileOffset wxStreamBase::OnSysTell() const
690 {
691 return wxInvalidOffset;
692 }
693
694 // ----------------------------------------------------------------------------
695 // wxInputStream
696 // ----------------------------------------------------------------------------
697
698 IMPLEMENT_ABSTRACT_CLASS(wxInputStream, wxStreamBase)
699
700 wxInputStream::wxInputStream()
701 {
702 m_wback = NULL;
703 m_wbacksize =
704 m_wbackcur = 0;
705 }
706
707 wxInputStream::~wxInputStream()
708 {
709 free(m_wback);
710 }
711
712 bool wxInputStream::CanRead() const
713 {
714 // we don't know if there is anything to read or not and by default we
715 // prefer to be optimistic and try to read data unless we know for sure
716 // there is no more of it
717 return m_lasterror != wxSTREAM_EOF;
718 }
719
720 bool wxInputStream::Eof() const
721 {
722 // the only way the base class can know we're at EOF is when we'd already
723 // tried to read beyond it in which case last error is set accordingly
724 return GetLastError() == wxSTREAM_EOF;
725 }
726
727 char *wxInputStream::AllocSpaceWBack(size_t needed_size)
728 {
729 // get number of bytes left from previous wback buffer
730 size_t toget = m_wbacksize - m_wbackcur;
731
732 // allocate a buffer large enough to hold prev + new data
733 char *temp_b = (char *)malloc(needed_size + toget);
734
735 if (!temp_b)
736 return NULL;
737
738 // copy previous data (and free old buffer) if needed
739 if (m_wback)
740 {
741 memmove(temp_b + needed_size, m_wback + m_wbackcur, toget);
742 free(m_wback);
743 }
744
745 // done
746 m_wback = temp_b;
747 m_wbackcur = 0;
748 m_wbacksize = needed_size + toget;
749
750 return m_wback;
751 }
752
753 size_t wxInputStream::GetWBack(void *buf, size_t size)
754 {
755 wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be used") );
756
757 /* Clear buffer first */
758 memset(buf, 0x00, size);
759
760 if (!m_wback)
761 return 0;
762
763 // how many bytes do we have in the buffer?
764 size_t toget = m_wbacksize - m_wbackcur;
765
766 if ( size < toget )
767 {
768 // we won't read everything
769 toget = size;
770 }
771
772 // copy the data from the cache
773 memcpy(buf, m_wback + m_wbackcur, toget);
774
775 m_wbackcur += toget;
776 if ( m_wbackcur == m_wbacksize )
777 {
778 // TODO: should we really free it here all the time? maybe keep it?
779 free(m_wback);
780 m_wback = NULL;
781 m_wbacksize = 0;
782 m_wbackcur = 0;
783 }
784
785 // return the number of bytes copied
786 return toget;
787 }
788
789 size_t wxInputStream::Ungetch(const void *buf, size_t bufsize)
790 {
791 wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be used in Ungetch()") );
792
793 if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF )
794 {
795 // can't operate on this stream until the error is cleared
796 return 0;
797 }
798
799 char *ptrback = AllocSpaceWBack(bufsize);
800 if (!ptrback)
801 return 0;
802
803 // Eof() shouldn't return true any longer
804 if ( m_lasterror == wxSTREAM_EOF )
805 m_lasterror = wxSTREAM_NO_ERROR;
806
807 memcpy(ptrback, buf, bufsize);
808 return bufsize;
809 }
810
811 bool wxInputStream::Ungetch(char c)
812 {
813 return Ungetch(&c, sizeof(c)) != 0;
814 }
815
816 int wxInputStream::GetC()
817 {
818 unsigned char c;
819 Read(&c, sizeof(c));
820 return LastRead() ? c : wxEOF;
821 }
822
823 wxInputStream& wxInputStream::Read(void *buf, size_t size)
824 {
825 wxASSERT_MSG( buf, _T("Warning: Null pointer is about to be read") );
826
827 char *p = (char *)buf;
828 m_lastcount = 0;
829
830 size_t read = GetWBack(buf, size);
831 for ( ;; )
832 {
833 size -= read;
834 m_lastcount += read;
835 p += read;
836
837 if ( !size )
838 {
839 // we read the requested amount of data
840 break;
841 }
842
843 if ( p != buf && !CanRead() )
844 {
845 // we have already read something and we would block in OnSysRead()
846 // now: don't do it but return immediately
847 break;
848 }
849
850 read = OnSysRead(p, size);
851 if ( !read )
852 {
853 // no more data available
854 break;
855 }
856 }
857
858 return *this;
859 }
860
861 char wxInputStream::Peek()
862 {
863 char c;
864 Read(&c, sizeof(c));
865 if (m_lasterror == wxSTREAM_NO_ERROR)
866 {
867 Ungetch(c);
868 return c;
869 }
870
871 return 0;
872 }
873
874 wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
875 {
876 size_t lastcount = 0;
877 char buf[BUF_TEMP_SIZE];
878
879 for ( ;; )
880 {
881 size_t bytes_read = Read(buf, WXSIZEOF(buf)).LastRead();
882 if ( !bytes_read )
883 break;
884
885 if ( stream_out.Write(buf, bytes_read).LastWrite() != bytes_read )
886 break;
887
888 lastcount += bytes_read;
889 }
890
891 m_lastcount = lastcount;
892
893 return *this;
894 }
895
896 wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
897 {
898 // RR: This code is duplicated in wxBufferedInputStream. This is
899 // not really a good design, but buffered stream are different
900 // from all other in that they handle two stream-related objects,
901 // the stream buffer and parent stream.
902
903 // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek
904 if (m_lasterror==wxSTREAM_EOF)
905 m_lasterror=wxSTREAM_NO_ERROR;
906
907 /* RR: A call to SeekI() will automatically invalidate any previous
908 call to Ungetch(), otherwise it would be possible to SeekI() to
909 one position, unread some bytes there, SeekI() to another position
910 and the data would be corrupted.
911
912 GRG: Could add code here to try to navigate within the wback
913 buffer if possible, but is it really needed? It would only work
914 when seeking in wxFromCurrent mode, else it would invalidate
915 anyway... */
916
917 if (m_wback)
918 {
919 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
920
921 free(m_wback);
922 m_wback = NULL;
923 m_wbacksize = 0;
924 m_wbackcur = 0;
925 }
926
927 return OnSysSeek(pos, mode);
928 }
929
930 wxFileOffset wxInputStream::TellI() const
931 {
932 wxFileOffset pos = OnSysTell();
933
934 if (pos != wxInvalidOffset)
935 pos -= (m_wbacksize - m_wbackcur);
936
937 return pos;
938 }
939
940
941 // ----------------------------------------------------------------------------
942 // wxOutputStream
943 // ----------------------------------------------------------------------------
944
945 IMPLEMENT_ABSTRACT_CLASS(wxOutputStream, wxStreamBase)
946
947 wxOutputStream::wxOutputStream()
948 {
949 }
950
951 wxOutputStream::~wxOutputStream()
952 {
953 }
954
955 size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
956 size_t WXUNUSED(bufsize))
957 {
958 return 0;
959 }
960
961 void wxOutputStream::PutC(char c)
962 {
963 Write(&c, sizeof(c));
964 }
965
966 wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size)
967 {
968 m_lastcount = OnSysWrite(buffer, size);
969 return *this;
970 }
971
972 wxOutputStream& wxOutputStream::Write(wxInputStream& stream_in)
973 {
974 stream_in.Read(*this);
975 return *this;
976 }
977
978 wxFileOffset wxOutputStream::TellO() const
979 {
980 return OnSysTell();
981 }
982
983 wxFileOffset wxOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
984 {
985 return OnSysSeek(pos, mode);
986 }
987
988 void wxOutputStream::Sync()
989 {
990 }
991
992
993 // ----------------------------------------------------------------------------
994 // wxCountingOutputStream
995 // ----------------------------------------------------------------------------
996
997 IMPLEMENT_DYNAMIC_CLASS(wxCountingOutputStream, wxOutputStream)
998
999 wxCountingOutputStream::wxCountingOutputStream ()
1000 {
1001 m_currentPos = 0;
1002 }
1003
1004 wxFileOffset wxCountingOutputStream::GetLength() const
1005 {
1006 return m_lastcount;
1007 }
1008
1009 size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer),
1010 size_t size)
1011 {
1012 m_currentPos += size;
1013 if (m_currentPos > m_lastcount)
1014 m_lastcount = m_currentPos;
1015
1016 return m_currentPos;
1017 }
1018
1019 wxFileOffset wxCountingOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
1020 {
1021 ssize_t new_pos = wx_truncate_cast(ssize_t, pos);
1022
1023 switch ( mode )
1024 {
1025 case wxFromStart:
1026 wxCHECK_MSG( (wxFileOffset)new_pos == pos, wxInvalidOffset, wxT("huge position not supported") );
1027 break;
1028
1029 case wxFromEnd:
1030 new_pos = m_lastcount + new_pos;
1031 wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_lastcount + pos), wxInvalidOffset, wxT("huge position not supported") );
1032 break;
1033
1034 case wxFromCurrent:
1035 new_pos = m_currentPos + new_pos;
1036 wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_currentPos + pos), wxInvalidOffset, wxT("huge position not supported") );
1037 break;
1038
1039 default:
1040 wxFAIL_MSG( _T("invalid seek mode") );
1041 return wxInvalidOffset;
1042 }
1043
1044 m_currentPos = new_pos;
1045
1046 if (m_currentPos > m_lastcount)
1047 m_lastcount = m_currentPos;
1048
1049 return m_currentPos;
1050 }
1051
1052 wxFileOffset wxCountingOutputStream::OnSysTell() const
1053 {
1054 return m_currentPos;
1055 }
1056
1057 // ----------------------------------------------------------------------------
1058 // wxFilterInputStream
1059 // ----------------------------------------------------------------------------
1060
1061 IMPLEMENT_ABSTRACT_CLASS(wxFilterInputStream, wxInputStream)
1062
1063 wxFilterInputStream::wxFilterInputStream()
1064 : m_parent_i_stream(NULL),
1065 m_owns(false)
1066 {
1067 }
1068
1069 wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
1070 : m_parent_i_stream(&stream),
1071 m_owns(false)
1072 {
1073 }
1074
1075 wxFilterInputStream::wxFilterInputStream(wxInputStream *stream)
1076 : m_parent_i_stream(stream),
1077 m_owns(true)
1078 {
1079 }
1080
1081 wxFilterInputStream::~wxFilterInputStream()
1082 {
1083 if (m_owns)
1084 delete m_parent_i_stream;
1085 }
1086
1087 // ----------------------------------------------------------------------------
1088 // wxFilterOutputStream
1089 // ----------------------------------------------------------------------------
1090
1091 IMPLEMENT_ABSTRACT_CLASS(wxFilterOutputStream, wxOutputStream)
1092
1093 wxFilterOutputStream::wxFilterOutputStream()
1094 : m_parent_o_stream(NULL),
1095 m_owns(false)
1096 {
1097 }
1098
1099 wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
1100 : m_parent_o_stream(&stream),
1101 m_owns(false)
1102 {
1103 }
1104
1105 wxFilterOutputStream::wxFilterOutputStream(wxOutputStream *stream)
1106 : m_parent_o_stream(stream),
1107 m_owns(true)
1108 {
1109 }
1110
1111 bool wxFilterOutputStream::Close()
1112 {
1113 if (m_parent_o_stream && m_owns)
1114 return m_parent_o_stream->Close();
1115 else
1116 return true;
1117 }
1118
1119 wxFilterOutputStream::~wxFilterOutputStream()
1120 {
1121 if (m_owns)
1122 delete m_parent_o_stream;
1123 }
1124
1125 // ----------------------------------------------------------------------------
1126 // wxFilterClassFactoryBase
1127 // ----------------------------------------------------------------------------
1128
1129 IMPLEMENT_ABSTRACT_CLASS(wxFilterClassFactoryBase, wxObject)
1130
1131 wxString wxFilterClassFactoryBase::PopExtension(const wxString& location) const
1132 {
1133 return location.substr(0, FindExtension(location));
1134 }
1135
1136 wxString::size_type wxFilterClassFactoryBase::FindExtension(
1137 const wxChar *location) const
1138 {
1139 size_t len = wxStrlen(location);
1140
1141 for (const wxChar *const *p = GetProtocols(wxSTREAM_FILEEXT); *p; p++)
1142 {
1143 size_t l = wxStrlen(*p);
1144
1145 if (l <= len && wxStrcmp(*p, location + len - l) == 0)
1146 return len - l;
1147 }
1148
1149 return wxString::npos;
1150 }
1151
1152 bool wxFilterClassFactoryBase::CanHandle(const wxChar *protocol,
1153 wxStreamProtocolType type) const
1154 {
1155 if (type == wxSTREAM_FILEEXT)
1156 return FindExtension(protocol) != wxString::npos;
1157 else
1158 for (const wxChar *const *p = GetProtocols(type); *p; p++)
1159 if (wxStrcmp(*p, protocol) == 0)
1160 return true;
1161
1162 return false;
1163 }
1164
1165 // ----------------------------------------------------------------------------
1166 // wxFilterClassFactory
1167 // ----------------------------------------------------------------------------
1168
1169 IMPLEMENT_ABSTRACT_CLASS(wxFilterClassFactory, wxFilterClassFactoryBase)
1170
1171 wxFilterClassFactory *wxFilterClassFactory::sm_first = NULL;
1172
1173 void wxFilterClassFactory::Remove()
1174 {
1175 if (m_next != this)
1176 {
1177 wxFilterClassFactory **pp = &sm_first;
1178
1179 while (*pp != this)
1180 pp = &(*pp)->m_next;
1181
1182 *pp = m_next;
1183
1184 m_next = this;
1185 }
1186 }
1187
1188 // ----------------------------------------------------------------------------
1189 // wxBufferedInputStream
1190 // ----------------------------------------------------------------------------
1191
1192 wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
1193 wxStreamBuffer *buffer)
1194 : wxFilterInputStream(s)
1195 {
1196 if ( buffer )
1197 {
1198 // use the buffer provided by the user
1199 m_i_streambuf = buffer;
1200 }
1201 else // create a default buffer
1202 {
1203 m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
1204
1205 m_i_streambuf->SetBufferIO(1024);
1206 }
1207 }
1208
1209 wxBufferedInputStream::~wxBufferedInputStream()
1210 {
1211 m_parent_i_stream->SeekI(-(wxFileOffset)m_i_streambuf->GetBytesLeft(),
1212 wxFromCurrent);
1213
1214 delete m_i_streambuf;
1215 }
1216
1217 char wxBufferedInputStream::Peek()
1218 {
1219 return m_i_streambuf->Peek();
1220 }
1221
1222 wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
1223 {
1224 // reset the error flag
1225 Reset();
1226
1227 // first read from the already cached data
1228 m_lastcount = GetWBack(buf, size);
1229
1230 // do we have to read anything more?
1231 if ( m_lastcount < size )
1232 {
1233 size -= m_lastcount;
1234 buf = (char *)buf + m_lastcount;
1235
1236 // the call to wxStreamBuffer::Read() below will reset our m_lastcount,
1237 // so save it
1238 size_t countOld = m_lastcount;
1239
1240 m_i_streambuf->Read(buf, size);
1241
1242 m_lastcount += countOld;
1243 }
1244
1245 return *this;
1246 }
1247
1248 wxFileOffset wxBufferedInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
1249 {
1250 // RR: Look at wxInputStream for comments.
1251
1252 if (m_lasterror==wxSTREAM_EOF)
1253 Reset();
1254
1255 if (m_wback)
1256 {
1257 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
1258
1259 free(m_wback);
1260 m_wback = NULL;
1261 m_wbacksize = 0;
1262 m_wbackcur = 0;
1263 }
1264
1265 return m_i_streambuf->Seek(pos, mode);
1266 }
1267
1268 wxFileOffset wxBufferedInputStream::TellI() const
1269 {
1270 wxFileOffset pos = m_i_streambuf->Tell();
1271
1272 if (pos != wxInvalidOffset)
1273 pos -= (m_wbacksize - m_wbackcur);
1274
1275 return pos;
1276 }
1277
1278 size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize)
1279 {
1280 return m_parent_i_stream->Read(buffer, bufsize).LastRead();
1281 }
1282
1283 wxFileOffset wxBufferedInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
1284 {
1285 return m_parent_i_stream->SeekI(seek, mode);
1286 }
1287
1288 wxFileOffset wxBufferedInputStream::OnSysTell() const
1289 {
1290 return m_parent_i_stream->TellI();
1291 }
1292
1293 void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer)
1294 {
1295 wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") );
1296
1297 delete m_i_streambuf;
1298 m_i_streambuf = buffer;
1299 }
1300
1301 // ----------------------------------------------------------------------------
1302 // wxBufferedOutputStream
1303 // ----------------------------------------------------------------------------
1304
1305 wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s,
1306 wxStreamBuffer *buffer)
1307 : wxFilterOutputStream(s)
1308 {
1309 if ( buffer )
1310 {
1311 m_o_streambuf = buffer;
1312 }
1313 else // create a default one
1314 {
1315 m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
1316
1317 m_o_streambuf->SetBufferIO(1024);
1318 }
1319 }
1320
1321 wxBufferedOutputStream::~wxBufferedOutputStream()
1322 {
1323 Sync();
1324 delete m_o_streambuf;
1325 }
1326
1327 bool wxBufferedOutputStream::Close()
1328 {
1329 Sync();
1330 return IsOk();
1331 }
1332
1333
1334 wxOutputStream& wxBufferedOutputStream::Write(const void *buffer, size_t size)
1335 {
1336 m_lastcount = 0;
1337 m_o_streambuf->Write(buffer, size);
1338 return *this;
1339 }
1340
1341 wxFileOffset wxBufferedOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
1342 {
1343 Sync();
1344 return m_o_streambuf->Seek(pos, mode);
1345 }
1346
1347 wxFileOffset wxBufferedOutputStream::TellO() const
1348 {
1349 return m_o_streambuf->Tell();
1350 }
1351
1352 void wxBufferedOutputStream::Sync()
1353 {
1354 m_o_streambuf->FlushBuffer();
1355 m_parent_o_stream->Sync();
1356 }
1357
1358 size_t wxBufferedOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
1359 {
1360 return m_parent_o_stream->Write(buffer, bufsize).LastWrite();
1361 }
1362
1363 wxFileOffset wxBufferedOutputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
1364 {
1365 return m_parent_o_stream->SeekO(seek, mode);
1366 }
1367
1368 wxFileOffset wxBufferedOutputStream::OnSysTell() const
1369 {
1370 return m_parent_o_stream->TellO();
1371 }
1372
1373 wxFileOffset wxBufferedOutputStream::GetLength() const
1374 {
1375 return m_parent_o_stream->GetLength() + m_o_streambuf->GetIntPosition();
1376 }
1377
1378 void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer)
1379 {
1380 wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") );
1381
1382 delete m_o_streambuf;
1383 m_o_streambuf = buffer;
1384 }
1385
1386 // ----------------------------------------------------------------------------
1387 // Some IOManip function
1388 // ----------------------------------------------------------------------------
1389
1390 wxOutputStream& wxEndL(wxOutputStream& stream)
1391 {
1392 static const wxChar *eol = wxTextFile::GetEOL();
1393
1394 return stream.Write(eol, wxStrlen(eol));
1395 }
1396
1397 #endif // wxUSE_STREAMS