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