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