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