]> git.saurik.com Git - wxWidgets.git/blame - src/common/stream.cpp
Permission for flood-fill code clarified
[wxWidgets.git] / src / common / stream.cpp
CommitLineData
3d4c6a21 1/////////////////////////////////////////////////////////////////////////////
49e399d8 2// Name: src/common/stream.cpp
3d4c6a21
GL
3// Purpose: wxStream base classes
4// Author: Guilhem Lavaux
49e399d8
VZ
5// Modified by: VZ (23.11.00) to fix realloc()ing new[]ed memory,
6// general code review
3d4c6a21
GL
7// Created: 11/07/98
8// RCS-ID: $Id$
9// Copyright: (c) Guilhem Lavaux
65571936 10// Licence: wxWindows licence
3d4c6a21
GL
11/////////////////////////////////////////////////////////////////////////////
12
49e399d8
VZ
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
47fc03d2 20
14f355c2 21#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
47fc03d2 22 #pragma implementation "stream.h"
3d4c6a21
GL
23#endif
24
db138a4c
JS
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
ce4169a4
RR
27
28#ifdef __BORLANDC__
29 #pragma hdrstop
30#endif
31
32#ifndef WX_PRECOMP
33 #include "wx/defs.h"
34#endif
35
36#if wxUSE_STREAMS
37
6d44bf31 38#include <ctype.h>
3096bd2f
VZ
39#include "wx/stream.h"
40#include "wx/datstrm.h"
733b8ed3 41#include "wx/textfile.h"
adc35078 42#include "wx/log.h"
db138a4c 43
49e399d8
VZ
44// ----------------------------------------------------------------------------
45// constants
46// ----------------------------------------------------------------------------
47
48// the temporary buffer size used when copying from stream to stream
2b5f62a0 49#define BUF_TEMP_SIZE 4096
8ef6a930 50
49e399d8
VZ
51// ============================================================================
52// implementation
53// ============================================================================
54
6d44bf31
GL
55// ----------------------------------------------------------------------------
56// wxStreamBuffer
57// ----------------------------------------------------------------------------
58
49e399d8
VZ
59void wxStreamBuffer::SetError(wxStreamError err)
60{
2b5f62a0 61 if ( m_stream && m_stream->m_lasterror == wxSTREAM_NO_ERROR )
49e399d8
VZ
62 m_stream->m_lasterror = err;
63}
64
65void wxStreamBuffer::InitBuffer()
66{
67 m_buffer_start =
68 m_buffer_end =
69 m_buffer_pos = NULL;
70 m_buffer_size = 0;
71
d4b45f48 72 // if we are going to allocate the buffer, we should free it later as well
d775fa82 73 m_destroybuf = true;
49e399d8
VZ
74}
75
76void wxStreamBuffer::Init()
77{
78 InitBuffer();
79
d775fa82 80 m_fixed = true;
49e399d8 81}
926c550d 82
2b5f62a0 83wxStreamBuffer::wxStreamBuffer(BufMode mode)
6d44bf31 84{
a2bd1fbb
VZ
85 Init();
86
2b5f62a0 87 m_stream = NULL;
49e399d8
VZ
88 m_mode = mode;
89
d775fa82 90 m_flushable = false;
6d44bf31
GL
91}
92
2b5f62a0 93wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
84b46c35 94{
a2bd1fbb
VZ
95 Init();
96
2b5f62a0 97 m_stream = &stream;
49e399d8
VZ
98 m_mode = mode;
99
d775fa82 100 m_flushable = true;
84b46c35
GL
101}
102
103wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer)
104{
2b5f62a0 105 // doing this has big chances to lead to a crash when the source buffer is
49e399d8 106 // destroyed (otherwise assume the caller knows what he does)
2b5f62a0 107 wxASSERT_MSG( !buffer.m_destroybuf,
49e399d8
VZ
108 _T("it's a bad idea to copy this buffer") );
109
110 m_buffer_start = buffer.m_buffer_start;
111 m_buffer_end = buffer.m_buffer_end;
112 m_buffer_pos = buffer.m_buffer_pos;
113 m_buffer_size = buffer.m_buffer_size;
114 m_fixed = buffer.m_fixed;
115 m_flushable = buffer.m_flushable;
116 m_stream = buffer.m_stream;
117 m_mode = buffer.m_mode;
d775fa82 118 m_destroybuf = false;
84b46c35
GL
119}
120
49e399d8 121void wxStreamBuffer::FreeBuffer()
6d44bf31 122{
49e399d8
VZ
123 if ( m_destroybuf )
124 free(m_buffer_start);
6d44bf31
GL
125}
126
49e399d8 127wxStreamBuffer::~wxStreamBuffer()
6d44bf31 128{
49e399d8 129 FreeBuffer();
6d44bf31
GL
130}
131
47fc03d2
VZ
132wxInputStream *wxStreamBuffer::GetInputStream() const
133{
134 return m_mode == write ? NULL : (wxInputStream *)m_stream;
135}
136
137wxOutputStream *wxStreamBuffer::GetOutputStream() const
138{
139 return m_mode == read ? NULL : (wxOutputStream *)m_stream;
140}
141
49e399d8
VZ
142void wxStreamBuffer::SetBufferIO(void *buffer_start,
143 void *buffer_end,
144 bool takeOwnership)
67c8c225
VZ
145{
146 SetBufferIO(buffer_start, (char *)buffer_end - (char *)buffer_start,
147 takeOwnership);
148}
149
150void wxStreamBuffer::SetBufferIO(void *start,
151 size_t len,
152 bool takeOwnership)
6d44bf31 153{
49e399d8
VZ
154 // start by freeing the old buffer
155 FreeBuffer();
885ee235 156
67c8c225
VZ
157 m_buffer_start = (char *)start;
158 m_buffer_end = m_buffer_start + len;
6d44bf31 159
67c8c225 160 m_buffer_size = len;
49e399d8
VZ
161
162 // if we own it, we free it
421db2ad 163 m_destroybuf = takeOwnership;
49e399d8
VZ
164
165 ResetBuffer();
166}
6d44bf31 167
49e399d8
VZ
168void wxStreamBuffer::SetBufferIO(size_t bufsize)
169{
170 // start by freeing the old buffer
171 FreeBuffer();
172
173 if ( bufsize )
174 {
d775fa82 175 SetBufferIO(malloc(bufsize), bufsize, true /* take ownership */);
49e399d8
VZ
176 }
177 else // no buffer size => no buffer
178 {
179 InitBuffer();
180 }
6d44bf31
GL
181}
182
183void wxStreamBuffer::ResetBuffer()
184{
2b5f62a0
VZ
185 if ( m_stream )
186 {
187 m_stream->Reset();
188 m_stream->m_lastcount = 0;
189 }
49e399d8 190
2b5f62a0
VZ
191 m_buffer_pos = m_mode == read && m_flushable
192 ? m_buffer_end
193 : m_buffer_start;
6d44bf31
GL
194}
195
49e399d8 196// fill the buffer with as much data as possible (only for read buffers)
75ed1d15
GL
197bool wxStreamBuffer::FillBuffer()
198{
47fc03d2
VZ
199 wxInputStream *inStream = GetInputStream();
200
d775fa82 201 // It's legal to have no stream, so we don't complain about it just return false
2b5f62a0 202 if ( !inStream )
d775fa82 203 return false;
75ed1d15 204
47fc03d2 205 size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size);
49e399d8 206 if ( !count )
d775fa82 207 return false;
75ed1d15 208
49e399d8
VZ
209 m_buffer_end = m_buffer_start + count;
210 m_buffer_pos = m_buffer_start;
211
d775fa82 212 return true;
75ed1d15
GL
213}
214
49e399d8 215// write the buffer contents to the stream (only for write buffers)
75ed1d15
GL
216bool wxStreamBuffer::FlushBuffer()
217{
d775fa82 218 wxCHECK_MSG( m_flushable, false, _T("can't flush this buffer") );
75ed1d15 219
49e399d8
VZ
220 // FIXME: what is this check for? (VZ)
221 if ( m_buffer_pos == m_buffer_start )
d775fa82 222 return false;
75ed1d15 223
47fc03d2
VZ
224 wxOutputStream *outStream = GetOutputStream();
225
d775fa82 226 wxCHECK_MSG( outStream, false, _T("should have a stream in wxStreamBuffer") );
49e399d8
VZ
227
228 size_t current = m_buffer_pos - m_buffer_start;
47fc03d2 229 size_t count = outStream->OnSysWrite(m_buffer_start, current);
49e399d8 230 if ( count != current )
d775fa82 231 return false;
49e399d8
VZ
232
233 m_buffer_pos = m_buffer_start;
75ed1d15 234
d775fa82 235 return true;
75ed1d15
GL
236}
237
49e399d8
VZ
238size_t wxStreamBuffer::GetDataLeft()
239{
240 /* Why is this done? RR. */
241 if ( m_buffer_pos == m_buffer_end && m_flushable)
242 FillBuffer();
243
244 return GetBytesLeft();
245}
246
247// copy up to size bytes from our buffer into the provided one
75ed1d15
GL
248void wxStreamBuffer::GetFromBuffer(void *buffer, size_t size)
249{
49e399d8
VZ
250 // don't get more bytes than left in the buffer
251 size_t left = GetBytesLeft();
75ed1d15 252
49e399d8
VZ
253 if ( size > left )
254 size = left;
75ed1d15 255
49e399d8
VZ
256 memcpy(buffer, m_buffer_pos, size);
257 m_buffer_pos += size;
75ed1d15
GL
258}
259
49e399d8 260// copy the contents of the provided buffer into this one
75ed1d15
GL
261void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size)
262{
49e399d8 263 size_t left = GetBytesLeft();
2b5f62a0 264
49e399d8
VZ
265 if ( size > left )
266 {
267 if ( m_fixed )
268 {
269 // we can't realloc the buffer, so just copy what we can
270 size = left;
271 }
272 else // !m_fixed
273 {
274 // realloc the buffer to have enough space for the data
275 size_t delta = m_buffer_pos - m_buffer_start;
276
277 char *startOld = m_buffer_start;
278 m_buffer_size += size;
279 m_buffer_start = (char *)realloc(m_buffer_start, m_buffer_size);
280 if ( !m_buffer_start )
281 {
282 // don't leak memory if realloc() failed
283 m_buffer_start = startOld;
284 m_buffer_size -= size;
285
286 // what else can we do?
287 return;
288 }
289
290 // adjust the pointers invalidated by realloc()
291 m_buffer_pos = m_buffer_start + delta;
292 m_buffer_end = m_buffer_start + m_buffer_size;
293 }
56dc1ffd 294 }
49e399d8
VZ
295
296 memcpy(m_buffer_pos, buffer, size);
297 m_buffer_pos += size;
75ed1d15
GL
298}
299
84b46c35
GL
300void wxStreamBuffer::PutChar(char c)
301{
47fc03d2
VZ
302 wxOutputStream *outStream = GetOutputStream();
303
304 wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") );
84b46c35 305
49e399d8
VZ
306 // if we don't have buffer at all, just forward this call to the stream,
307 if ( !HasBuffer() )
308 {
2b5f62a0 309 outStream->OnSysWrite(&c, sizeof(c));
49e399d8
VZ
310 }
311 else
312 {
313 // otherwise check we have enough space left
314 if ( !GetDataLeft() && !FlushBuffer() )
315 {
316 // we don't
2b5f62a0 317 SetError(wxSTREAM_WRITE_ERROR);
49e399d8
VZ
318 }
319 else
320 {
2b5f62a0 321 PutToBuffer(&c, sizeof(c));
49e399d8
VZ
322 m_stream->m_lastcount = 1;
323 }
324 }
84b46c35
GL
325}
326
6319afe3
GL
327char wxStreamBuffer::Peek()
328{
49e399d8
VZ
329 wxCHECK_MSG( m_stream && HasBuffer(), 0,
330 _T("should have the stream and the buffer in wxStreamBuffer") );
6319afe3 331
49e399d8
VZ
332 if ( !GetDataLeft() )
333 {
2b5f62a0 334 SetError(wxSTREAM_READ_ERROR);
49e399d8
VZ
335 return 0;
336 }
6319afe3 337
49e399d8 338 char c;
2b5f62a0 339 GetFromBuffer(&c, sizeof(c));
49e399d8 340 m_buffer_pos--;
6319afe3 341
49e399d8 342 return c;
6319afe3
GL
343}
344
84b46c35
GL
345char wxStreamBuffer::GetChar()
346{
47fc03d2
VZ
347 wxInputStream *inStream = GetInputStream();
348
349 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
84b46c35 350
49e399d8
VZ
351 char c;
352 if ( !HasBuffer() )
353 {
2b5f62a0 354 inStream->OnSysRead(&c, sizeof(c));
49e399d8
VZ
355 }
356 else
357 {
358 if ( !GetDataLeft() )
359 {
2b5f62a0 360 SetError(wxSTREAM_READ_ERROR);
49e399d8
VZ
361 c = 0;
362 }
363 else
364 {
2b5f62a0 365 GetFromBuffer(&c, sizeof(c));
49e399d8
VZ
366 m_stream->m_lastcount = 1;
367 }
368 }
84b46c35 369
84b46c35 370 return c;
84b46c35
GL
371}
372
8ef6a930 373size_t wxStreamBuffer::Read(void *buffer, size_t size)
6d44bf31 374{
49e399d8 375 // lasterror is reset before all new IO calls
2b5f62a0
VZ
376 if ( m_stream )
377 m_stream->Reset();
49e399d8 378
2b5f62a0 379 size_t read;
49e399d8
VZ
380 if ( !HasBuffer() )
381 {
2b5f62a0
VZ
382 wxInputStream *inStream = GetInputStream();
383
384 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
385
386 read = inStream->OnSysRead(buffer, size);
49e399d8
VZ
387 }
388 else // we have a buffer, use it
389 {
390 size_t orig_size = size;
391
392 while ( size > 0 )
393 {
394 size_t left = GetDataLeft();
395
396 // if the requested number of bytes if greater than the buffer
397 // size, read data in chunks
398 if ( size > left )
399 {
400 GetFromBuffer(buffer, left);
401 size -= left;
402 buffer = (char *)buffer + left;
403
404 if ( !FillBuffer() )
405 {
2b5f62a0 406 SetError(wxSTREAM_EOF);
49e399d8
VZ
407 break;
408 }
409 }
410 else // otherwise just do it in one gulp
411 {
412 GetFromBuffer(buffer, size);
413 size = 0;
414 }
415 }
416
2b5f62a0 417 read = orig_size - size;
6d44bf31 418 }
49e399d8 419
2b5f62a0
VZ
420 if ( m_stream )
421 m_stream->m_lastcount = read;
422
423 return read;
6d44bf31
GL
424}
425
49e399d8
VZ
426// this should really be called "Copy()"
427size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf)
8ef6a930 428{
49e399d8 429 wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") );
8ef6a930 430
49e399d8
VZ
431 char buf[BUF_TEMP_SIZE];
432 size_t nRead,
433 total = 0;
7f42cff1 434
49e399d8
VZ
435 do
436 {
437 nRead = Read(dbuf, WXSIZEOF(buf));
438 if ( nRead )
439 {
440 nRead = dbuf->Write(buf, nRead);
441 total += nRead;
442 }
443 }
444 while ( nRead );
445
446 return total;
8ef6a930
GL
447}
448
449size_t wxStreamBuffer::Write(const void *buffer, size_t size)
6d44bf31 450{
2b5f62a0
VZ
451 if (m_stream)
452 {
453 // lasterror is reset before all new IO calls
454 m_stream->Reset();
455 }
6d44bf31 456
999836aa 457 size_t ret;
6d44bf31 458
49e399d8
VZ
459 if ( !HasBuffer() && m_fixed )
460 {
2b5f62a0
VZ
461 wxOutputStream *outStream = GetOutputStream();
462
463 wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") );
464
49e399d8 465 // no buffer, just forward the call to the stream
2b5f62a0 466 ret = outStream->OnSysWrite(buffer, size);
49e399d8
VZ
467 }
468 else // we [may] have a buffer, use it
469 {
470 size_t orig_size = size;
471
472 while ( size > 0 )
473 {
474 size_t left = GetBytesLeft();
475
476 // if the buffer is too large to fit in the stream buffer, split
477 // it in smaller parts
478 //
479 // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
480 // we always go to the second case.
481 //
482 // FIXME: fine, but if it fails we should (re)try writing it by
483 // chunks as this will (hopefully) always work (VZ)
2b5f62a0 484
49e399d8
VZ
485 if ( size > left && m_fixed )
486 {
487 PutToBuffer(buffer, left);
488 size -= left;
489 buffer = (char *)buffer + left;
490
491 if ( !FlushBuffer() )
492 {
2b5f62a0 493 SetError(wxSTREAM_WRITE_ERROR);
49e399d8
VZ
494
495 break;
496 }
497
498 m_buffer_pos = m_buffer_start;
499 }
500 else // we can do it in one gulp
501 {
502 PutToBuffer(buffer, size);
503 size = 0;
504 }
505 }
506
2b5f62a0 507 ret = orig_size - size;
6d44bf31 508 }
49e399d8 509
2b5f62a0
VZ
510 if (m_stream)
511 {
512 // i am not entirely sure what we do this for
513 m_stream->m_lastcount = ret;
514 }
515
516 return ret;
8ef6a930
GL
517}
518
519size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf)
520{
49e399d8
VZ
521 wxCHECK_MSG( m_mode != read, 0, _T("can't write to this buffer") );
522 wxCHECK_MSG( sbuf->m_mode != write, 0, _T("can't read from that buffer") );
8ef6a930 523
49e399d8
VZ
524 char buf[BUF_TEMP_SIZE];
525 size_t nWrite,
526 total = 0;
7f42cff1 527
49e399d8
VZ
528 do
529 {
530 size_t nRead = sbuf->Read(buf, WXSIZEOF(buf));
531 if ( nRead )
532 {
533 nWrite = Write(buf, nRead);
534 if ( nWrite < nRead )
535 {
536 // put back data we couldn't copy
537 wxInputStream *in_stream = (wxInputStream *)sbuf->GetStream();
538
539 in_stream->Ungetch(buf + nWrite, nRead - nWrite);
540 }
541
542 total += nWrite;
543 }
544 else
545 {
546 nWrite = 0;
547 }
548 }
549 while ( nWrite == WXSIZEOF(buf) );
fae05df5 550
49e399d8 551 return total;
75ed1d15
GL
552}
553
4004775e 554wxFileOffset wxStreamBuffer::Seek(wxFileOffset pos, wxSeekMode mode)
75ed1d15 555{
4004775e 556 wxFileOffset ret_off, diff;
75ed1d15 557
4004775e 558 wxFileOffset last_access = GetLastAccess();
75ed1d15 559
49e399d8
VZ
560 if ( !m_flushable )
561 {
562 switch (mode)
563 {
564 case wxFromStart:
565 diff = pos;
566 break;
567
568 case wxFromCurrent:
569 diff = pos + GetIntPosition();
570 break;
571
572 case wxFromEnd:
573 diff = pos + last_access;
574 break;
575
576 default:
577 wxFAIL_MSG( _T("invalid seek mode") );
578
30984dea 579 return wxInvalidOffset;
49e399d8
VZ
580 }
581 if (diff < 0 || diff > last_access)
30984dea 582 return wxInvalidOffset;
287d71d9
WS
583 size_t int_diff = (size_t)diff;
584 wxCHECK_MSG( (wxFileOffset)int_diff == diff, wxInvalidOffset, wxT("huge file not supported") );
585 SetIntPosition(int_diff);
49e399d8 586 return diff;
d984207c 587 }
49e399d8
VZ
588
589 switch ( mode )
590 {
591 case wxFromStart:
592 // We'll try to compute an internal position later ...
593 ret_off = m_stream->OnSysSeek(pos, wxFromStart);
594 ResetBuffer();
595 return ret_off;
596
597 case wxFromCurrent:
598 diff = pos + GetIntPosition();
599
600 if ( (diff > last_access) || (diff < 0) )
601 {
602 // We must take into account the fact that we have read
603 // something previously.
604 ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent);
605 ResetBuffer();
606 return ret_off;
607 }
608 else
609 {
287d71d9
WS
610 size_t int_diff = (size_t)diff;
611 wxCHECK_MSG( (wxFileOffset)int_diff == diff, wxInvalidOffset, wxT("huge file not supported") );
612 SetIntPosition(int_diff);
49e399d8
VZ
613 return pos;
614 }
615
616 case wxFromEnd:
617 // Hard to compute: always seek to the requested position.
618 ret_off = m_stream->OnSysSeek(pos, wxFromEnd);
619 ResetBuffer();
620 return ret_off;
75ed1d15 621 }
49e399d8 622
30984dea 623 return wxInvalidOffset;
75ed1d15
GL
624}
625
4004775e 626wxFileOffset wxStreamBuffer::Tell() const
75ed1d15 627{
30984dea 628 wxFileOffset pos;
915a955c 629
2b5f62a0
VZ
630 // ask the stream for position if we have a real one
631 if ( m_stream )
915a955c
VZ
632 {
633 pos = m_stream->OnSysTell();
634 if ( pos == wxInvalidOffset )
30984dea 635 return wxInvalidOffset;
915a955c
VZ
636 }
637 else // no associated stream
638 {
639 pos = 0;
640 }
c7a9fa36
RR
641
642 pos += GetIntPosition();
cd6ce4a9 643
49e399d8 644 if ( m_mode == read && m_flushable )
c7a9fa36 645 pos -= GetLastAccess();
cd6ce4a9 646
c7a9fa36 647 return pos;
75ed1d15
GL
648}
649
75ed1d15
GL
650// ----------------------------------------------------------------------------
651// wxStreamBase
652// ----------------------------------------------------------------------------
653
654wxStreamBase::wxStreamBase()
655{
2b5f62a0 656 m_lasterror = wxSTREAM_NO_ERROR;
c7a9fa36 657 m_lastcount = 0;
75ed1d15
GL
658}
659
660wxStreamBase::~wxStreamBase()
661{
662}
663
588066b7
VZ
664size_t wxStreamBase::GetSize() const
665{
666 wxFileOffset length = GetLength();
667 return length == wxInvalidOffset ? 0 : (size_t)length;
668}
669
4004775e 670wxFileOffset wxStreamBase::OnSysSeek(wxFileOffset WXUNUSED(seek), wxSeekMode WXUNUSED(mode))
b9138710 671{
30984dea 672 return wxInvalidOffset;
b9138710
VZ
673}
674
4004775e 675wxFileOffset wxStreamBase::OnSysTell() const
b9138710 676{
30984dea 677 return wxInvalidOffset;
b9138710
VZ
678}
679
2d67974d
WS
680#if WXWIN_COMPATIBILITY_2_2
681
682wxStreamError wxStreamBase::LastError() const
683{
684 return m_lasterror;
685}
686
687size_t wxStreamBase::StreamSize() const
688{
689 return GetSize();
690}
691
692#endif // WXWIN_COMPATIBILITY_2_2
693
1678ad78
GL
694// ----------------------------------------------------------------------------
695// wxInputStream
696// ----------------------------------------------------------------------------
697
3d4c6a21 698wxInputStream::wxInputStream()
3d4c6a21 699{
49e399d8
VZ
700 m_wback = NULL;
701 m_wbacksize =
702 m_wbackcur = 0;
6d44bf31
GL
703}
704
fae05df5 705wxInputStream::~wxInputStream()
6d44bf31 706{
fae05df5 707 free(m_wback);
3d4c6a21
GL
708}
709
2b5f62a0 710bool wxInputStream::CanRead() const
47fc03d2 711{
2b5f62a0
VZ
712 // we don't know if there is anything to read or not and by default we
713 // prefer to be optimistic and try to read data unless we know for sure
714 // there is no more of it
715 return m_lasterror != wxSTREAM_EOF;
47fc03d2
VZ
716}
717
cd6ce4a9
VZ
718bool wxInputStream::Eof() const
719{
2b5f62a0
VZ
720 // the only way the base class can know we're at EOF is when we'd already
721 // tried to read beyond it in which case last error is set accordingly
722 return GetLastError() == wxSTREAM_EOF;
cd6ce4a9
VZ
723}
724
fae05df5 725char *wxInputStream::AllocSpaceWBack(size_t needed_size)
3d4c6a21 726{
49e399d8 727 // get number of bytes left from previous wback buffer
c7a9fa36 728 size_t toget = m_wbacksize - m_wbackcur;
fae05df5 729
49e399d8
VZ
730 // allocate a buffer large enough to hold prev + new data
731 char *temp_b = (char *)malloc(needed_size + toget);
fae05df5 732
c7a9fa36
RR
733 if (!temp_b)
734 return NULL;
fae05df5 735
adc35078 736 // copy previous data (and free old buffer) if needed
c7a9fa36
RR
737 if (m_wback)
738 {
739 memmove(temp_b + needed_size, m_wback + m_wbackcur, toget);
740 free(m_wback);
741 }
5ac8158a 742
adc35078 743 // done
c7a9fa36
RR
744 m_wback = temp_b;
745 m_wbackcur = 0;
746 m_wbacksize = needed_size + toget;
783ff666 747
49e399d8 748 return m_wback;
6d44bf31
GL
749}
750
2b5f62a0 751size_t wxInputStream::GetWBack(void *buf, size_t size)
6d44bf31 752{
c7a9fa36
RR
753 if (!m_wback)
754 return 0;
a324a7bc 755
e9f69291
VZ
756 // how many bytes do we have in the buffer?
757 size_t toget = m_wbacksize - m_wbackcur;
758
2b5f62a0 759 if ( size < toget )
e9f69291
VZ
760 {
761 // we won't read everything
2b5f62a0 762 toget = size;
e9f69291 763 }
fae05df5 764
2b5f62a0 765 // copy the data from the cache
e9f69291 766 memcpy(buf, m_wback + m_wbackcur, toget);
fae05df5 767
49e399d8 768 m_wbackcur += toget;
e9f69291 769 if ( m_wbackcur == m_wbacksize )
c7a9fa36 770 {
e9f69291 771 // TODO: should we really free it here all the time? maybe keep it?
c7a9fa36 772 free(m_wback);
49e399d8 773 m_wback = NULL;
c7a9fa36
RR
774 m_wbacksize = 0;
775 m_wbackcur = 0;
776 }
cd6ce4a9 777
e9f69291 778 // return the number of bytes copied
49e399d8 779 return toget;
6d44bf31
GL
780}
781
8f7173ab 782size_t wxInputStream::Ungetch(const void *buf, size_t bufsize)
fae05df5 783{
42a3aedb
VZ
784 if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF )
785 {
786 // can't operate on this stream until the error is cleared
787 return 0;
788 }
789
c7a9fa36
RR
790 char *ptrback = AllocSpaceWBack(bufsize);
791 if (!ptrback)
792 return 0;
cd6ce4a9 793
d775fa82 794 // Eof() shouldn't return true any longer
42a3aedb
VZ
795 if ( m_lasterror == wxSTREAM_EOF )
796 m_lasterror = wxSTREAM_NO_ERROR;
797
c7a9fa36
RR
798 memcpy(ptrback, buf, bufsize);
799 return bufsize;
fae05df5
GL
800}
801
802bool wxInputStream::Ungetch(char c)
1e3eca9d 803{
2b5f62a0 804 return Ungetch(&c, sizeof(c)) != 0;
fae05df5
GL
805}
806
807char wxInputStream::GetC()
808{
c7a9fa36 809 char c;
2b5f62a0 810 Read(&c, sizeof(c));
c7a9fa36 811 return c;
1e3eca9d
GL
812}
813
49e399d8 814wxInputStream& wxInputStream::Read(void *buf, size_t size)
6d44bf31 815{
9a76510b
VZ
816 char *p = (char *)buf;
817 m_lastcount = 0;
818
819 size_t read = GetWBack(buf, size);
820 for ( ;; )
c7a9fa36 821 {
9a76510b
VZ
822 size -= read;
823 m_lastcount += read;
824 p += read;
825
826 if ( !size )
827 {
828 // we read the requested amount of data
829 break;
830 }
831
2b5f62a0
VZ
832 if ( p != buf && !CanRead() )
833 {
834 // we have already read something and we would block in OnSysRead()
835 // now: don't do it but return immediately
836 break;
837 }
838
9b11cb6e 839 read = OnSysRead(p, size);
9a76510b
VZ
840 if ( !read )
841 {
842 // no more data available
843 break;
844 }
c7a9fa36 845 }
fae05df5 846
c7a9fa36 847 return *this;
3d4c6a21
GL
848}
849
75ed1d15
GL
850char wxInputStream::Peek()
851{
c7a9fa36 852 char c;
2b5f62a0
VZ
853 Read(&c, sizeof(c));
854 if (m_lasterror == wxSTREAM_NO_ERROR)
c7a9fa36
RR
855 {
856 Ungetch(c);
857 return c;
858 }
cd6ce4a9 859
c7a9fa36 860 return 0;
75ed1d15
GL
861}
862
3d4c6a21
GL
863wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
864{
cd6ce4a9 865 char buf[BUF_TEMP_SIZE];
3d4c6a21 866
2b5f62a0 867 for ( ;; )
c7a9fa36 868 {
2b5f62a0
VZ
869 size_t bytes_read = Read(buf, WXSIZEOF(buf)).LastRead();
870 if ( !bytes_read )
871 break;
872
873 if ( stream_out.Write(buf, bytes_read).LastWrite() != bytes_read )
874 break;
c7a9fa36 875 }
2b5f62a0 876
c7a9fa36 877 return *this;
3d4c6a21
GL
878}
879
4004775e 880wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
75ed1d15 881{
adc35078
RR
882 // RR: This code is duplicated in wxBufferedInputStream. This is
883 // not really a good design, but buffered stream are different
884 // from all other in that they handle two stream-related objects,
885 // the stream buffer and parent stream.
886
887 // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek
cd6ce4a9 888 if (m_lasterror==wxSTREAM_EOF)
2b5f62a0 889 m_lasterror=wxSTREAM_NO_ERROR;
c7a9fa36 890
adc35078
RR
891 /* RR: A call to SeekI() will automatically invalidate any previous
892 call to Ungetch(), otherwise it would be possible to SeekI() to
c7a9fa36 893 one position, unread some bytes there, SeekI() to another position
19da7237
GRG
894 and the data would be corrupted.
895
896 GRG: Could add code here to try to navigate within the wback
897 buffer if possible, but is it really needed? It would only work
898 when seeking in wxFromCurrent mode, else it would invalidate
adc35078 899 anyway... */
2b5f62a0 900
cd6ce4a9 901 if (m_wback)
c7a9fa36 902 {
adc35078 903 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
2b5f62a0 904
c7a9fa36 905 free(m_wback);
49e399d8 906 m_wback = NULL;
c7a9fa36
RR
907 m_wbacksize = 0;
908 m_wbackcur = 0;
909 }
fe8aa971 910
c7a9fa36 911 return OnSysSeek(pos, mode);
75ed1d15
GL
912}
913
4004775e 914wxFileOffset wxInputStream::TellI() const
75ed1d15 915{
30984dea 916 wxFileOffset pos = OnSysTell();
19da7237
GRG
917
918 if (pos != wxInvalidOffset)
919 pos -= (m_wbacksize - m_wbackcur);
920
921 return pos;
75ed1d15
GL
922}
923
1678ad78 924
fae05df5
GL
925// ----------------------------------------------------------------------------
926// wxOutputStream
927// ----------------------------------------------------------------------------
49e399d8 928
fae05df5 929wxOutputStream::wxOutputStream()
1678ad78 930{
1678ad78
GL
931}
932
fae05df5 933wxOutputStream::~wxOutputStream()
123a7fdd 934{
123a7fdd
GL
935}
936
47fc03d2
VZ
937size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
938 size_t WXUNUSED(bufsize))
939{
940 return 0;
941}
942
7513f9ff
GRG
943void wxOutputStream::PutC(char c)
944{
2b5f62a0 945 Write(&c, sizeof(c));
7513f9ff
GRG
946}
947
fae05df5 948wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size)
1678ad78 949{
c7a9fa36
RR
950 m_lastcount = OnSysWrite(buffer, size);
951 return *this;
1678ad78
GL
952}
953
fae05df5 954wxOutputStream& wxOutputStream::Write(wxInputStream& stream_in)
38830220 955{
c7a9fa36
RR
956 stream_in.Read(*this);
957 return *this;
38830220
RR
958}
959
4004775e 960wxFileOffset wxOutputStream::TellO() const
38830220 961{
c7a9fa36 962 return OnSysTell();
38830220
RR
963}
964
4004775e 965wxFileOffset wxOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
38830220 966{
c7a9fa36 967 return OnSysSeek(pos, mode);
38830220
RR
968}
969
fae05df5 970void wxOutputStream::Sync()
1678ad78 971{
1678ad78
GL
972}
973
123a7fdd 974
e2acb9ae
RR
975// ----------------------------------------------------------------------------
976// wxCountingOutputStream
977// ----------------------------------------------------------------------------
978
979wxCountingOutputStream::wxCountingOutputStream ()
e2acb9ae 980{
c7a9fa36 981 m_currentPos = 0;
e2acb9ae
RR
982}
983
588066b7 984wxFileOffset wxCountingOutputStream::GetLength() const
e2acb9ae 985{
c7a9fa36 986 return m_lastcount;
e2acb9ae
RR
987}
988
49e399d8
VZ
989size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer),
990 size_t size)
e2acb9ae 991{
c7a9fa36 992 m_currentPos += size;
49e399d8
VZ
993 if (m_currentPos > m_lastcount)
994 m_lastcount = m_currentPos;
995
c7a9fa36 996 return m_currentPos;
e2acb9ae
RR
997}
998
4004775e 999wxFileOffset wxCountingOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
e2acb9ae 1000{
287d71d9
WS
1001 ssize_t new_pos = (ssize_t)pos;
1002
49e399d8
VZ
1003 switch ( mode )
1004 {
1005 case wxFromStart:
287d71d9 1006 wxCHECK_MSG( (wxFileOffset)new_pos == pos, wxInvalidOffset, wxT("huge position not supported") );
49e399d8
VZ
1007 break;
1008
1009 case wxFromEnd:
287d71d9
WS
1010 new_pos = m_lastcount + new_pos;
1011 wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_lastcount + pos), wxInvalidOffset, wxT("huge position not supported") );
49e399d8
VZ
1012 break;
1013
1014 case wxFromCurrent:
287d71d9
WS
1015 new_pos = m_currentPos + new_pos;
1016 wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_currentPos + pos), wxInvalidOffset, wxT("huge position not supported") );
49e399d8
VZ
1017 break;
1018
1019 default:
1020 wxFAIL_MSG( _T("invalid seek mode") );
30984dea 1021 return wxInvalidOffset;
49e399d8 1022 }
cd6ce4a9 1023
287d71d9
WS
1024 m_currentPos = new_pos;
1025
49e399d8
VZ
1026 if (m_currentPos > m_lastcount)
1027 m_lastcount = m_currentPos;
cd6ce4a9 1028
c7a9fa36 1029 return m_currentPos;
e2acb9ae
RR
1030}
1031
4004775e 1032wxFileOffset wxCountingOutputStream::OnSysTell() const
e2acb9ae 1033{
c7a9fa36 1034 return m_currentPos;
e2acb9ae 1035}
cd6ce4a9 1036
1678ad78 1037// ----------------------------------------------------------------------------
fae05df5 1038// wxFilterInputStream
1678ad78 1039// ----------------------------------------------------------------------------
e2acb9ae 1040
fae05df5 1041wxFilterInputStream::wxFilterInputStream()
3d4c6a21 1042{
47fc03d2 1043 m_parent_i_stream = NULL;
6d44bf31
GL
1044}
1045
fae05df5 1046wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
6d44bf31 1047{
c7a9fa36 1048 m_parent_i_stream = &stream;
3d4c6a21
GL
1049}
1050
fae05df5 1051wxFilterInputStream::~wxFilterInputStream()
3d4c6a21 1052{
6d44bf31
GL
1053}
1054
fae05df5
GL
1055// ----------------------------------------------------------------------------
1056// wxFilterOutputStream
1057// ----------------------------------------------------------------------------
49e399d8 1058
fae05df5 1059wxFilterOutputStream::wxFilterOutputStream()
6d44bf31 1060{
47fc03d2 1061 m_parent_o_stream = NULL;
3d4c6a21
GL
1062}
1063
fae05df5 1064wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
3d4c6a21 1065{
c7a9fa36 1066 m_parent_o_stream = &stream;
6d44bf31
GL
1067}
1068
fae05df5 1069wxFilterOutputStream::~wxFilterOutputStream()
6d44bf31 1070{
6d44bf31
GL
1071}
1072
fae05df5
GL
1073// ----------------------------------------------------------------------------
1074// wxBufferedInputStream
1075// ----------------------------------------------------------------------------
49e399d8 1076
47fc03d2
VZ
1077wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
1078 wxStreamBuffer *buffer)
49e399d8 1079 : wxFilterInputStream(s)
6d44bf31 1080{
47fc03d2
VZ
1081 if ( buffer )
1082 {
1083 // use the buffer provided by the user
1084 m_i_streambuf = buffer;
1085 }
1086 else // create a default buffer
1087 {
1088 m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
49e399d8 1089
47fc03d2
VZ
1090 m_i_streambuf->SetBufferIO(1024);
1091 }
6d44bf31
GL
1092}
1093
fae05df5 1094wxBufferedInputStream::~wxBufferedInputStream()
6d44bf31 1095{
4004775e 1096 m_parent_i_stream->SeekI(-(wxFileOffset)m_i_streambuf->GetBytesLeft(),
1fb45475 1097 wxFromCurrent);
672cedf8 1098
c7a9fa36 1099 delete m_i_streambuf;
6d44bf31
GL
1100}
1101
6319afe3
GL
1102char wxBufferedInputStream::Peek()
1103{
c7a9fa36 1104 return m_i_streambuf->Peek();
6319afe3
GL
1105}
1106
49e399d8 1107wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
1e3eca9d 1108{
e9f69291 1109 // reset the error flag
2b5f62a0 1110 Reset();
1e3eca9d 1111
e9f69291
VZ
1112 // first read from the already cached data
1113 m_lastcount = GetWBack(buf, size);
1114
1115 // do we have to read anything more?
1116 if ( m_lastcount < size )
c7a9fa36 1117 {
e9f69291
VZ
1118 size -= m_lastcount;
1119 buf = (char *)buf + m_lastcount;
1e3eca9d 1120
e9f69291
VZ
1121 // the call to wxStreamBuffer::Read() below will reset our m_lastcount,
1122 // so save it
1123 size_t countOld = m_lastcount;
1124
1125 m_i_streambuf->Read(buf, size);
1126
1127 m_lastcount += countOld;
1128 }
6d44bf31 1129
c7a9fa36 1130 return *this;
6d44bf31
GL
1131}
1132
4004775e 1133wxFileOffset wxBufferedInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
6d44bf31 1134{
adc35078
RR
1135 // RR: Look at wxInputStream for comments.
1136
1137 if (m_lasterror==wxSTREAM_EOF)
2b5f62a0 1138 Reset();
adc35078
RR
1139
1140 if (m_wback)
1141 {
1142 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
2b5f62a0 1143
adc35078
RR
1144 free(m_wback);
1145 m_wback = NULL;
1146 m_wbacksize = 0;
1147 m_wbackcur = 0;
1148 }
2b5f62a0 1149
c7a9fa36 1150 return m_i_streambuf->Seek(pos, mode);
6d44bf31
GL
1151}
1152
4004775e 1153wxFileOffset wxBufferedInputStream::TellI() const
6d44bf31 1154{
30984dea 1155 wxFileOffset pos = m_i_streambuf->Tell();
adc35078
RR
1156
1157 if (pos != wxInvalidOffset)
1158 pos -= (m_wbacksize - m_wbackcur);
2b5f62a0 1159
adc35078 1160 return pos;
38830220 1161}
6d44bf31 1162
fae05df5 1163size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize)
38830220 1164{
c7a9fa36 1165 return m_parent_i_stream->Read(buffer, bufsize).LastRead();
38830220
RR
1166}
1167
4004775e 1168wxFileOffset wxBufferedInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
38830220 1169{
c7a9fa36 1170 return m_parent_i_stream->SeekI(seek, mode);
6d44bf31
GL
1171}
1172
4004775e 1173wxFileOffset wxBufferedInputStream::OnSysTell() const
6d44bf31 1174{
c7a9fa36 1175 return m_parent_i_stream->TellI();
38830220 1176}
6d44bf31 1177
47fc03d2
VZ
1178void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer)
1179{
1180 wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") );
1181
1182 delete m_i_streambuf;
1183 m_i_streambuf = buffer;
1184}
1185
fae05df5
GL
1186// ----------------------------------------------------------------------------
1187// wxBufferedOutputStream
1188// ----------------------------------------------------------------------------
6d44bf31 1189
47fc03d2
VZ
1190wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s,
1191 wxStreamBuffer *buffer)
49e399d8 1192 : wxFilterOutputStream(s)
6d44bf31 1193{
47fc03d2
VZ
1194 if ( buffer )
1195 {
1196 m_o_streambuf = buffer;
1197 }
1198 else // create a default one
1199 {
1200 m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
1201
1202 m_o_streambuf->SetBufferIO(1024);
1203 }
6d44bf31
GL
1204}
1205
fae05df5 1206wxBufferedOutputStream::~wxBufferedOutputStream()
6d44bf31 1207{
c7a9fa36
RR
1208 Sync();
1209 delete m_o_streambuf;
3d4c6a21
GL
1210}
1211
8f0ff178
RN
1212bool wxBufferedOutputStream::Close()
1213{
1214 Sync();
1215 return IsOk();
1216}
1217
1218
fae05df5 1219wxOutputStream& wxBufferedOutputStream::Write(const void *buffer, size_t size)
123a7fdd 1220{
c7a9fa36
RR
1221 m_lastcount = 0;
1222 m_o_streambuf->Write(buffer, size);
1223 return *this;
123a7fdd
GL
1224}
1225
4004775e 1226wxFileOffset wxBufferedOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
f4ada568 1227{
c7a9fa36
RR
1228 Sync();
1229 return m_o_streambuf->Seek(pos, mode);
f4ada568
GL
1230}
1231
4004775e 1232wxFileOffset wxBufferedOutputStream::TellO() const
3d4c6a21 1233{
c7a9fa36 1234 return m_o_streambuf->Tell();
3d4c6a21
GL
1235}
1236
fae05df5 1237void wxBufferedOutputStream::Sync()
3d4c6a21 1238{
c7a9fa36
RR
1239 m_o_streambuf->FlushBuffer();
1240 m_parent_o_stream->Sync();
3d4c6a21 1241}
219f895a 1242
fae05df5 1243size_t wxBufferedOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
f4ada568 1244{
c7a9fa36 1245 return m_parent_o_stream->Write(buffer, bufsize).LastWrite();
f4ada568
GL
1246}
1247
4004775e 1248wxFileOffset wxBufferedOutputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
219f895a 1249{
c7a9fa36 1250 return m_parent_o_stream->SeekO(seek, mode);
219f895a
RR
1251}
1252
4004775e 1253wxFileOffset wxBufferedOutputStream::OnSysTell() const
219f895a 1254{
c7a9fa36
RR
1255 return m_parent_o_stream->TellO();
1256}
1257
588066b7 1258wxFileOffset wxBufferedOutputStream::GetLength() const
c7a9fa36 1259{
588066b7 1260 return m_parent_o_stream->GetLength() + m_o_streambuf->GetIntPosition();
219f895a 1261}
6d44bf31 1262
47fc03d2
VZ
1263void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer)
1264{
1265 wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") );
1266
1267 delete m_o_streambuf;
1268 m_o_streambuf = buffer;
1269}
1270
6d44bf31
GL
1271// ----------------------------------------------------------------------------
1272// Some IOManip function
1273// ----------------------------------------------------------------------------
1274
1275wxOutputStream& wxEndL(wxOutputStream& stream)
1276{
733b8ed3
VZ
1277 static const wxChar *eol = wxTextFile::GetEOL();
1278
1279 return stream.Write(eol, wxStrlen(eol));
6d44bf31 1280}
ce4169a4
RR
1281
1282#endif
1283 // wxUSE_STREAMS