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