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