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