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