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