1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkStream.h" 11#include "SkData.h" 12#include "SkFixed.h" 13#include "SkString.h" 14#include "SkOSFile.h" 15 16/////////////////////////////////////////////////////////////////////////////// 17 18 19int8_t SkStream::readS8() { 20 int8_t value; 21 SkDEBUGCODE(size_t len =) this->read(&value, 1); 22 SkASSERT(1 == len); 23 return value; 24} 25 26int16_t SkStream::readS16() { 27 int16_t value; 28 SkDEBUGCODE(size_t len =) this->read(&value, 2); 29 SkASSERT(2 == len); 30 return value; 31} 32 33int32_t SkStream::readS32() { 34 int32_t value; 35 SkDEBUGCODE(size_t len =) this->read(&value, 4); 36 SkASSERT(4 == len); 37 return value; 38} 39 40SkScalar SkStream::readScalar() { 41 SkScalar value; 42 SkDEBUGCODE(size_t len =) this->read(&value, sizeof(SkScalar)); 43 SkASSERT(sizeof(SkScalar) == len); 44 return value; 45} 46 47#define SK_MAX_BYTE_FOR_U8 0xFD 48#define SK_BYTE_SENTINEL_FOR_U16 0xFE 49#define SK_BYTE_SENTINEL_FOR_U32 0xFF 50 51size_t SkStream::readPackedUInt() { 52 uint8_t byte; 53 if (!this->read(&byte, 1)) { 54 return 0; 55 } 56 if (SK_BYTE_SENTINEL_FOR_U16 == byte) { 57 return this->readU16(); 58 } else if (SK_BYTE_SENTINEL_FOR_U32 == byte) { 59 return this->readU32(); 60 } else { 61 return byte; 62 } 63} 64 65SkData* SkStream::readData() { 66 size_t size = this->readU32(); 67 if (0 == size) { 68 return SkData::NewEmpty(); 69 } else { 70 void* buffer = sk_malloc_throw(size); 71 this->read(buffer, size); 72 return SkData::NewFromMalloc(buffer, size); 73 } 74} 75 76////////////////////////////////////////////////////////////////////////////////////// 77 78SkWStream::~SkWStream() 79{ 80} 81 82void SkWStream::newline() 83{ 84 this->write("\n", 1); 85} 86 87void SkWStream::flush() 88{ 89} 90 91bool SkWStream::writeText(const char text[]) 92{ 93 SkASSERT(text); 94 return this->write(text, strlen(text)); 95} 96 97bool SkWStream::writeDecAsText(int32_t dec) 98{ 99 SkString tmp; 100 tmp.appendS32(dec); 101 return this->write(tmp.c_str(), tmp.size()); 102} 103 104bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits) 105{ 106 SkString tmp; 107 tmp.appendS64(dec, minDigits); 108 return this->write(tmp.c_str(), tmp.size()); 109} 110 111bool SkWStream::writeHexAsText(uint32_t hex, int digits) 112{ 113 SkString tmp; 114 tmp.appendHex(hex, digits); 115 return this->write(tmp.c_str(), tmp.size()); 116} 117 118bool SkWStream::writeScalarAsText(SkScalar value) 119{ 120 SkString tmp; 121 tmp.appendScalar(value); 122 return this->write(tmp.c_str(), tmp.size()); 123} 124 125bool SkWStream::write8(U8CPU value) { 126 uint8_t v = SkToU8(value); 127 return this->write(&v, 1); 128} 129 130bool SkWStream::write16(U16CPU value) { 131 uint16_t v = SkToU16(value); 132 return this->write(&v, 2); 133} 134 135bool SkWStream::write32(uint32_t value) { 136 return this->write(&value, 4); 137} 138 139bool SkWStream::writeScalar(SkScalar value) { 140 return this->write(&value, sizeof(value)); 141} 142 143bool SkWStream::writePackedUInt(size_t value) { 144 uint8_t data[5]; 145 size_t len = 1; 146 if (value <= SK_MAX_BYTE_FOR_U8) { 147 data[0] = value; 148 len = 1; 149 } else if (value <= 0xFFFF) { 150 uint16_t value16 = value; 151 data[0] = SK_BYTE_SENTINEL_FOR_U16; 152 memcpy(&data[1], &value16, 2); 153 len = 3; 154 } else { 155 uint32_t value32 = value; 156 data[0] = SK_BYTE_SENTINEL_FOR_U32; 157 memcpy(&data[1], &value32, 4); 158 len = 5; 159 } 160 return this->write(data, len); 161} 162 163bool SkWStream::writeStream(SkStream* stream, size_t length) { 164 char scratch[1024]; 165 const size_t MAX = sizeof(scratch); 166 167 while (length != 0) { 168 size_t n = length; 169 if (n > MAX) { 170 n = MAX; 171 } 172 stream->read(scratch, n); 173 if (!this->write(scratch, n)) { 174 return false; 175 } 176 length -= n; 177 } 178 return true; 179} 180 181bool SkWStream::writeData(const SkData* data) { 182 if (data) { 183 this->write32(data->size()); 184 this->write(data->data(), data->size()); 185 } else { 186 this->write32(0); 187 } 188 return true; 189} 190 191/////////////////////////////////////////////////////////////////////////////// 192 193SkFILEStream::SkFILEStream(const char file[]) : fName(file), fOwnership(kCallerPasses_Ownership) { 194 fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL; 195} 196 197SkFILEStream::SkFILEStream(FILE* file, Ownership ownership) 198 : fFILE((SkFILE*)file) 199 , fOwnership(ownership) { 200} 201 202SkFILEStream::~SkFILEStream() { 203 if (fFILE && fOwnership != kCallerRetains_Ownership) { 204 sk_fclose(fFILE); 205 } 206} 207 208void SkFILEStream::setPath(const char path[]) { 209 fName.set(path); 210 if (fFILE) { 211 sk_fclose(fFILE); 212 fFILE = NULL; 213 } 214 if (path) { 215 fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag); 216 } 217} 218 219size_t SkFILEStream::read(void* buffer, size_t size) { 220 if (fFILE) { 221 return sk_fread(buffer, size, fFILE); 222 } 223 return 0; 224} 225 226bool SkFILEStream::isAtEnd() const { 227 return sk_feof(fFILE); 228} 229 230bool SkFILEStream::rewind() { 231 if (fFILE) { 232 if (sk_frewind(fFILE)) { 233 return true; 234 } 235 // we hit an error 236 sk_fclose(fFILE); 237 fFILE = NULL; 238 } 239 return false; 240} 241 242SkStreamAsset* SkFILEStream::duplicate() const { 243 if (NULL == fFILE) { 244 return new SkMemoryStream(); 245 } 246 247 if (NULL != fData.get()) { 248 return new SkMemoryStream(fData); 249 } 250 251 if (!fName.isEmpty()) { 252 SkAutoTUnref<SkFILEStream> that(new SkFILEStream(fName.c_str())); 253 if (sk_fidentical(that->fFILE, this->fFILE)) { 254 return that.detach(); 255 } 256 } 257 258 fData.reset(SkData::NewFromFILE(fFILE)); 259 if (NULL == fData.get()) { 260 return NULL; 261 } 262 return new SkMemoryStream(fData); 263} 264 265size_t SkFILEStream::getPosition() const { 266 return sk_ftell(fFILE); 267} 268 269bool SkFILEStream::seek(size_t position) { 270 return sk_fseek(fFILE, position); 271} 272 273bool SkFILEStream::move(long offset) { 274 return sk_fmove(fFILE, offset); 275} 276 277SkStreamAsset* SkFILEStream::fork() const { 278 SkAutoTUnref<SkStreamAsset> that(this->duplicate()); 279 that->seek(this->getPosition()); 280 return that.detach(); 281} 282 283size_t SkFILEStream::getLength() const { 284 return sk_fgetsize(fFILE); 285} 286 287const void* SkFILEStream::getMemoryBase() { 288 if (NULL == fData.get()) { 289 return NULL; 290 } 291 return fData->data(); 292} 293 294/////////////////////////////////////////////////////////////////////////////// 295 296static SkData* newFromParams(const void* src, size_t size, bool copyData) { 297 if (copyData) { 298 return SkData::NewWithCopy(src, size); 299 } else { 300 return SkData::NewWithProc(src, size, NULL, NULL); 301 } 302} 303 304SkMemoryStream::SkMemoryStream() { 305 fData = SkData::NewEmpty(); 306 fOffset = 0; 307} 308 309SkMemoryStream::SkMemoryStream(size_t size) { 310 fData = SkData::NewFromMalloc(sk_malloc_throw(size), size); 311 fOffset = 0; 312} 313 314SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) { 315 fData = newFromParams(src, size, copyData); 316 fOffset = 0; 317} 318 319SkMemoryStream::SkMemoryStream(SkData* data) { 320 if (NULL == data) { 321 fData = SkData::NewEmpty(); 322 } else { 323 fData = data; 324 fData->ref(); 325 } 326 fOffset = 0; 327} 328 329SkMemoryStream::~SkMemoryStream() { 330 fData->unref(); 331} 332 333void SkMemoryStream::setMemoryOwned(const void* src, size_t size) { 334 fData->unref(); 335 fData = SkData::NewFromMalloc(src, size); 336 fOffset = 0; 337} 338 339void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) { 340 fData->unref(); 341 fData = newFromParams(src, size, copyData); 342 fOffset = 0; 343} 344 345SkData* SkMemoryStream::copyToData() const { 346 fData->ref(); 347 return fData; 348} 349 350SkData* SkMemoryStream::setData(SkData* data) { 351 fData->unref(); 352 if (NULL == data) { 353 fData = SkData::NewEmpty(); 354 } else { 355 fData = data; 356 fData->ref(); 357 } 358 fOffset = 0; 359 return data; 360} 361 362void SkMemoryStream::skipToAlign4() { 363 // cast to remove unary-minus warning 364 fOffset += -(int)fOffset & 0x03; 365} 366 367size_t SkMemoryStream::read(void* buffer, size_t size) { 368 size_t dataSize = fData->size(); 369 370 if (size > dataSize - fOffset) { 371 size = dataSize - fOffset; 372 } 373 if (buffer) { 374 memcpy(buffer, fData->bytes() + fOffset, size); 375 } 376 fOffset += size; 377 return size; 378} 379 380bool SkMemoryStream::isAtEnd() const { 381 return fOffset == fData->size(); 382} 383 384bool SkMemoryStream::rewind() { 385 fOffset = 0; 386 return true; 387} 388 389SkMemoryStream* SkMemoryStream::duplicate() const { 390 return SkNEW_ARGS(SkMemoryStream, (fData)); 391} 392 393size_t SkMemoryStream::getPosition() const { 394 return fOffset; 395} 396 397bool SkMemoryStream::seek(size_t position) { 398 fOffset = position > fData->size() 399 ? fData->size() 400 : position; 401 return true; 402} 403 404bool SkMemoryStream::move(long offset) { 405 return this->seek(fOffset + offset); 406} 407 408SkMemoryStream* SkMemoryStream::fork() const { 409 SkAutoTUnref<SkMemoryStream> that(this->duplicate()); 410 that->seek(fOffset); 411 return that.detach(); 412} 413 414size_t SkMemoryStream::getLength() const { 415 return fData->size(); 416} 417 418const void* SkMemoryStream::getMemoryBase() { 419 return fData->data(); 420} 421 422const void* SkMemoryStream::getAtPos() { 423 return fData->bytes() + fOffset; 424} 425 426///////////////////////////////////////////////////////////////////////////////////////////////////////// 427///////////////////////////////////////////////////////////////////////////////////////////////////////// 428 429SkFILEWStream::SkFILEWStream(const char path[]) 430{ 431 fFILE = sk_fopen(path, kWrite_SkFILE_Flag); 432} 433 434SkFILEWStream::~SkFILEWStream() 435{ 436 if (fFILE) 437 sk_fclose(fFILE); 438} 439 440bool SkFILEWStream::write(const void* buffer, size_t size) 441{ 442 if (fFILE == NULL) 443 return false; 444 445 if (sk_fwrite(buffer, size, fFILE) != size) 446 { 447 SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);) 448 sk_fclose(fFILE); 449 fFILE = NULL; 450 return false; 451 } 452 return true; 453} 454 455void SkFILEWStream::flush() 456{ 457 if (fFILE) 458 sk_fflush(fFILE); 459} 460 461//////////////////////////////////////////////////////////////////////// 462 463SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size) 464 : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0) 465{ 466} 467 468bool SkMemoryWStream::write(const void* buffer, size_t size) 469{ 470 size = SkMin32(size, fMaxLength - fBytesWritten); 471 if (size > 0) 472 { 473 memcpy(fBuffer + fBytesWritten, buffer, size); 474 fBytesWritten += size; 475 return true; 476 } 477 return false; 478} 479 480//////////////////////////////////////////////////////////////////////// 481 482#define SkDynamicMemoryWStream_MinBlockSize 256 483 484struct SkDynamicMemoryWStream::Block { 485 Block* fNext; 486 char* fCurr; 487 char* fStop; 488 489 const char* start() const { return (const char*)(this + 1); } 490 char* start() { return (char*)(this + 1); } 491 size_t avail() const { return fStop - fCurr; } 492 size_t written() const { return fCurr - this->start(); } 493 494 void init(size_t size) 495 { 496 fNext = NULL; 497 fCurr = this->start(); 498 fStop = this->start() + size; 499 } 500 501 const void* append(const void* data, size_t size) 502 { 503 SkASSERT((size_t)(fStop - fCurr) >= size); 504 memcpy(fCurr, data, size); 505 fCurr += size; 506 return (const void*)((const char*)data + size); 507 } 508}; 509 510SkDynamicMemoryWStream::SkDynamicMemoryWStream() 511 : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopy(NULL) 512{ 513} 514 515SkDynamicMemoryWStream::~SkDynamicMemoryWStream() 516{ 517 reset(); 518} 519 520void SkDynamicMemoryWStream::reset() 521{ 522 this->invalidateCopy(); 523 524 Block* block = fHead; 525 526 while (block != NULL) { 527 Block* next = block->fNext; 528 sk_free(block); 529 block = next; 530 } 531 fHead = fTail = NULL; 532 fBytesWritten = 0; 533} 534 535bool SkDynamicMemoryWStream::write(const void* buffer, size_t count) 536{ 537 if (count > 0) { 538 this->invalidateCopy(); 539 540 fBytesWritten += count; 541 542 size_t size; 543 544 if (fTail != NULL && fTail->avail() > 0) { 545 size = SkMin32(fTail->avail(), count); 546 buffer = fTail->append(buffer, size); 547 SkASSERT(count >= size); 548 count -= size; 549 if (count == 0) 550 return true; 551 } 552 553 size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize); 554 Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size); 555 block->init(size); 556 block->append(buffer, count); 557 558 if (fTail != NULL) 559 fTail->fNext = block; 560 else 561 fHead = fTail = block; 562 fTail = block; 563 } 564 return true; 565} 566 567bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count) 568{ 569 if (offset + count > fBytesWritten) { 570 return false; // test does not partially modify 571 } 572 573 this->invalidateCopy(); 574 575 Block* block = fHead; 576 while (block != NULL) { 577 size_t size = block->written(); 578 if (offset < size) { 579 size_t part = offset + count > size ? size - offset : count; 580 memcpy(block->start() + offset, buffer, part); 581 if (count <= part) 582 return true; 583 count -= part; 584 buffer = (const void*) ((char* ) buffer + part); 585 } 586 offset = offset > size ? offset - size : 0; 587 block = block->fNext; 588 } 589 return false; 590} 591 592bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count) 593{ 594 if (offset + count > fBytesWritten) 595 return false; // test does not partially modify 596 Block* block = fHead; 597 while (block != NULL) { 598 size_t size = block->written(); 599 if (offset < size) { 600 size_t part = offset + count > size ? size - offset : count; 601 memcpy(buffer, block->start() + offset, part); 602 if (count <= part) 603 return true; 604 count -= part; 605 buffer = (void*) ((char* ) buffer + part); 606 } 607 offset = offset > size ? offset - size : 0; 608 block = block->fNext; 609 } 610 return false; 611} 612 613void SkDynamicMemoryWStream::copyTo(void* dst) const 614{ 615 if (fCopy) { 616 memcpy(dst, fCopy->data(), fBytesWritten); 617 } else { 618 Block* block = fHead; 619 620 while (block != NULL) { 621 size_t size = block->written(); 622 memcpy(dst, block->start(), size); 623 dst = (void*)((char*)dst + size); 624 block = block->fNext; 625 } 626 } 627} 628 629void SkDynamicMemoryWStream::padToAlign4() 630{ 631 // cast to remove unary-minus warning 632 int padBytes = -(int)fBytesWritten & 0x03; 633 if (padBytes == 0) 634 return; 635 int zero = 0; 636 write(&zero, padBytes); 637} 638 639SkData* SkDynamicMemoryWStream::copyToData() const { 640 if (NULL == fCopy) { 641 void* buffer = sk_malloc_throw(fBytesWritten); 642 this->copyTo(buffer); 643 fCopy = SkData::NewFromMalloc(buffer, fBytesWritten); 644 } 645 fCopy->ref(); 646 return fCopy; 647} 648 649void SkDynamicMemoryWStream::invalidateCopy() { 650 if (fCopy) { 651 fCopy->unref(); 652 fCopy = NULL; 653 } 654} 655 656class SkBlockMemoryRefCnt : public SkRefCnt { 657public: 658 explicit SkBlockMemoryRefCnt(SkDynamicMemoryWStream::Block* head) : fHead(head) { } 659 660 virtual ~SkBlockMemoryRefCnt() { 661 SkDynamicMemoryWStream::Block* block = fHead; 662 while (block != NULL) { 663 SkDynamicMemoryWStream::Block* next = block->fNext; 664 sk_free(block); 665 block = next; 666 } 667 } 668 669 SkDynamicMemoryWStream::Block* const fHead; 670}; 671 672class SkBlockMemoryStream : public SkStreamAsset { 673public: 674 SkBlockMemoryStream(SkDynamicMemoryWStream::Block* head, size_t size) 675 : fBlockMemory(SkNEW_ARGS(SkBlockMemoryRefCnt, (head))), fCurrent(head) 676 , fSize(size) , fOffset(0), fCurrentOffset(0) { } 677 678 SkBlockMemoryStream(SkBlockMemoryRefCnt* headRef, size_t size) 679 : fBlockMemory(SkRef(headRef)), fCurrent(fBlockMemory->fHead) 680 , fSize(size) , fOffset(0), fCurrentOffset(0) { } 681 682 virtual size_t read(void* buffer, size_t rawCount) SK_OVERRIDE { 683 size_t count = rawCount; 684 if (fOffset + count > fSize) { 685 count = fSize - fOffset; 686 } 687 size_t bytesLeftToRead = count; 688 while (fCurrent != NULL) { 689 size_t bytesLeftInCurrent = fCurrent->written() - fCurrentOffset; 690 size_t bytesFromCurrent = bytesLeftToRead <= bytesLeftInCurrent 691 ? bytesLeftToRead : bytesLeftInCurrent; 692 if (buffer) { 693 memcpy(buffer, fCurrent->start() + fCurrentOffset, bytesFromCurrent); 694 } 695 if (bytesLeftToRead <= bytesFromCurrent) { 696 fCurrentOffset += bytesFromCurrent; 697 fOffset += count; 698 return count; 699 } 700 bytesLeftToRead -= bytesFromCurrent; 701 buffer = SkTAddOffset<void>(buffer, bytesFromCurrent); 702 fCurrent = fCurrent->fNext; 703 fCurrentOffset = 0; 704 } 705 SkASSERT(false); 706 return 0; 707 } 708 709 virtual bool isAtEnd() const SK_OVERRIDE { 710 return fOffset == fSize; 711 } 712 713 virtual bool rewind() SK_OVERRIDE { 714 fCurrent = fBlockMemory->fHead; 715 fOffset = 0; 716 fCurrentOffset = 0; 717 return true; 718 } 719 720 virtual SkBlockMemoryStream* duplicate() const SK_OVERRIDE { 721 return SkNEW_ARGS(SkBlockMemoryStream, (fBlockMemory.get(), fSize)); 722 } 723 724 virtual size_t getPosition() const SK_OVERRIDE { 725 return fOffset; 726 } 727 728 virtual bool seek(size_t position) SK_OVERRIDE { 729 // If possible, skip forward. 730 if (position >= fOffset) { 731 size_t skipAmount = position - fOffset; 732 return this->skip(skipAmount) == skipAmount; 733 } 734 // If possible, move backward within the current block. 735 size_t moveBackAmount = fOffset - position; 736 if (moveBackAmount <= fCurrentOffset) { 737 fCurrentOffset -= moveBackAmount; 738 fOffset -= moveBackAmount; 739 return true; 740 } 741 // Otherwise rewind and move forward. 742 return this->rewind() && this->skip(position) == position; 743 } 744 745 virtual bool move(long offset) SK_OVERRIDE { 746 return seek(fOffset + offset); 747 } 748 749 virtual SkBlockMemoryStream* fork() const SK_OVERRIDE { 750 SkAutoTUnref<SkBlockMemoryStream> that(this->duplicate()); 751 that->fCurrent = this->fCurrent; 752 that->fOffset = this->fOffset; 753 that->fCurrentOffset = this->fCurrentOffset; 754 return that.detach(); 755 } 756 757 virtual size_t getLength() const SK_OVERRIDE { 758 return fSize; 759 } 760 761 virtual const void* getMemoryBase() SK_OVERRIDE { 762 if (NULL == fBlockMemory->fHead->fNext) { 763 return fBlockMemory->fHead->start(); 764 } 765 return NULL; 766 } 767 768private: 769 SkAutoTUnref<SkBlockMemoryRefCnt> const fBlockMemory; 770 SkDynamicMemoryWStream::Block const * fCurrent; 771 size_t const fSize; 772 size_t fOffset; 773 size_t fCurrentOffset; 774}; 775 776SkStreamAsset* SkDynamicMemoryWStream::detachAsStream() { 777 if (fCopy) { 778 SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (fCopy)); 779 this->reset(); 780 return stream; 781 } 782 SkBlockMemoryStream* stream = SkNEW_ARGS(SkBlockMemoryStream, (fHead, fBytesWritten)); 783 fHead = 0; 784 this->reset(); 785 return stream; 786} 787 788/////////////////////////////////////////////////////////////////////////////// 789 790void SkDebugWStream::newline() 791{ 792#if defined(SK_DEBUG) || defined(SK_DEVELOPER) 793 SkDebugf("\n"); 794#endif 795} 796 797bool SkDebugWStream::write(const void* buffer, size_t size) 798{ 799#if defined(SK_DEBUG) || defined(SK_DEVELOPER) 800 char* s = new char[size+1]; 801 memcpy(s, buffer, size); 802 s[size] = 0; 803 SkDebugf("%s", s); 804 delete[] s; 805#endif 806 return true; 807} 808 809/////////////////////////////////////////////////////////////////////////////// 810/////////////////////////////////////////////////////////////////////////////// 811 812 813static SkData* mmap_filename(const char path[]) { 814 SkFILE* file = sk_fopen(path, kRead_SkFILE_Flag); 815 if (NULL == file) { 816 return NULL; 817 } 818 819 SkData* data = SkData::NewFromFILE(file); 820 sk_fclose(file); 821 return data; 822} 823 824SkStreamAsset* SkStream::NewFromFile(const char path[]) { 825 SkAutoTUnref<SkData> data(mmap_filename(path)); 826 if (data.get()) { 827 return SkNEW_ARGS(SkMemoryStream, (data.get())); 828 } 829 830 // If we get here, then our attempt at using mmap failed, so try normal 831 // file access. 832 SkFILEStream* stream = SkNEW_ARGS(SkFILEStream, (path)); 833 if (!stream->isValid()) { 834 stream->unref(); 835 stream = NULL; 836 } 837 return stream; 838} 839