diff options
Diffstat (limited to 'gfx/skia/skia/src/core/SkStream.cpp')
-rw-r--r-- | gfx/skia/skia/src/core/SkStream.cpp | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/core/SkStream.cpp b/gfx/skia/skia/src/core/SkStream.cpp new file mode 100644 index 0000000000..e43cb7716c --- /dev/null +++ b/gfx/skia/skia/src/core/SkStream.cpp @@ -0,0 +1,986 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkStream.h" + +#include "include/core/SkData.h" +#include "include/core/SkString.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkAlign.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMalloc.h" +#include "include/private/base/SkTFitsIn.h" +#include "include/private/base/SkTPin.h" +#include "include/private/base/SkTemplates.h" +#include "include/private/base/SkTo.h" +#include "src/base/SkSafeMath.h" +#include "src/core/SkOSFile.h" +#include "src/core/SkStreamPriv.h" + +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <limits> +#include <new> + +/////////////////////////////////////////////////////////////////////////////// + +bool SkStream::readS8(int8_t* i) { + return this->read(i, sizeof(*i)) == sizeof(*i); +} + +bool SkStream::readS16(int16_t* i) { + return this->read(i, sizeof(*i)) == sizeof(*i); +} + +bool SkStream::readS32(int32_t* i) { + return this->read(i, sizeof(*i)) == sizeof(*i); +} + +bool SkStream::readScalar(SkScalar* i) { + return this->read(i, sizeof(*i)) == sizeof(*i); +} + +#define SK_MAX_BYTE_FOR_U8 0xFD +#define SK_BYTE_SENTINEL_FOR_U16 0xFE +#define SK_BYTE_SENTINEL_FOR_U32 0xFF + +bool SkStream::readPackedUInt(size_t* i) { + uint8_t byte; + if (!this->read(&byte, 1)) { + return false; + } + if (SK_BYTE_SENTINEL_FOR_U16 == byte) { + uint16_t i16; + if (!this->readU16(&i16)) { return false; } + *i = i16; + } else if (SK_BYTE_SENTINEL_FOR_U32 == byte) { + uint32_t i32; + if (!this->readU32(&i32)) { return false; } + *i = i32; + } else { + *i = byte; + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkWStream::~SkWStream() +{ +} + +void SkWStream::flush() +{ +} + +bool SkWStream::writeDecAsText(int32_t dec) +{ + char buffer[kSkStrAppendS32_MaxSize]; + char* stop = SkStrAppendS32(buffer, dec); + return this->write(buffer, stop - buffer); +} + +bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits) +{ + char buffer[kSkStrAppendU64_MaxSize]; + char* stop = SkStrAppendU64(buffer, dec, minDigits); + return this->write(buffer, stop - buffer); +} + +bool SkWStream::writeHexAsText(uint32_t hex, int digits) +{ + SkString tmp; + tmp.appendHex(hex, digits); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeScalarAsText(SkScalar value) +{ + char buffer[kSkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + return this->write(buffer, stop - buffer); +} + +bool SkWStream::writeScalar(SkScalar value) { + return this->write(&value, sizeof(value)); +} + +int SkWStream::SizeOfPackedUInt(size_t value) { + if (value <= SK_MAX_BYTE_FOR_U8) { + return 1; + } else if (value <= 0xFFFF) { + return 3; + } + return 5; +} + +bool SkWStream::writePackedUInt(size_t value) { + uint8_t data[5]; + size_t len = 1; + if (value <= SK_MAX_BYTE_FOR_U8) { + data[0] = value; + len = 1; + } else if (value <= 0xFFFF) { + uint16_t value16 = value; + data[0] = SK_BYTE_SENTINEL_FOR_U16; + memcpy(&data[1], &value16, 2); + len = 3; + } else { + uint32_t value32 = SkToU32(value); + data[0] = SK_BYTE_SENTINEL_FOR_U32; + memcpy(&data[1], &value32, 4); + len = 5; + } + return this->write(data, len); +} + +bool SkWStream::writeStream(SkStream* stream, size_t length) { + char scratch[1024]; + const size_t MAX = sizeof(scratch); + + while (length != 0) { + size_t n = length; + if (n > MAX) { + n = MAX; + } + stream->read(scratch, n); + if (!this->write(scratch, n)) { + return false; + } + length -= n; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkFILEStream::SkFILEStream(std::shared_ptr<FILE> file, size_t end, size_t start, size_t current) + : fFILE(std::move(file)) + , fEnd(end) + , fStart(std::min(start, fEnd)) + , fCurrent(SkTPin(current, fStart, fEnd)) +{ + SkASSERT(fStart == start); + SkASSERT(fCurrent == current); +} + +SkFILEStream::SkFILEStream(std::shared_ptr<FILE> file, size_t end, size_t start) + : SkFILEStream(std::move(file), end, start, start) +{ } + +SkFILEStream::SkFILEStream(FILE* file, size_t size, size_t start) + : SkFILEStream(std::shared_ptr<FILE>(file, sk_fclose), SkSafeMath::Add(start, size), start) +{ } + +SkFILEStream::SkFILEStream(FILE* file, size_t size) + : SkFILEStream(file, size, file ? sk_ftell(file) : 0) +{ } + +SkFILEStream::SkFILEStream(FILE* file) + : SkFILEStream(std::shared_ptr<FILE>(file, sk_fclose), + file ? sk_fgetsize(file) : 0, + file ? sk_ftell(file) : 0) +{ } + +SkFILEStream::SkFILEStream(const char path[]) + : SkFILEStream(path ? sk_fopen(path, kRead_SkFILE_Flag) : nullptr) +{ } + +SkFILEStream::~SkFILEStream() { + this->close(); +} + +void SkFILEStream::close() { + fFILE.reset(); + fEnd = 0; + fStart = 0; + fCurrent = 0; +} + +size_t SkFILEStream::read(void* buffer, size_t size) { + if (size > fEnd - fCurrent) { + size = fEnd - fCurrent; + } + size_t bytesRead = size; + if (buffer) { + bytesRead = sk_qread(fFILE.get(), buffer, size, fCurrent); + } + if (bytesRead == SIZE_MAX) { + return 0; + } + fCurrent += bytesRead; + return bytesRead; +} + +bool SkFILEStream::isAtEnd() const { + if (fCurrent == fEnd) { + return true; + } + return fCurrent >= sk_fgetsize(fFILE.get()); +} + +bool SkFILEStream::rewind() { + fCurrent = fStart; + return true; +} + +SkStreamAsset* SkFILEStream::onDuplicate() const { + return new SkFILEStream(fFILE, fEnd, fStart, fStart); +} + +size_t SkFILEStream::getPosition() const { + SkASSERT(fCurrent >= fStart); + return fCurrent - fStart; +} + +bool SkFILEStream::seek(size_t position) { + fCurrent = std::min(SkSafeMath::Add(position, fStart), fEnd); + return true; +} + +bool SkFILEStream::move(long offset) { + if (offset < 0) { + if (offset == std::numeric_limits<long>::min() || + !SkTFitsIn<size_t>(-offset) || + (size_t) (-offset) >= this->getPosition()) + { + fCurrent = fStart; + } else { + fCurrent += offset; + } + } else if (!SkTFitsIn<size_t>(offset)) { + fCurrent = fEnd; + } else { + fCurrent = std::min(SkSafeMath::Add(fCurrent, (size_t) offset), fEnd); + } + + SkASSERT(fCurrent >= fStart && fCurrent <= fEnd); + return true; +} + +SkStreamAsset* SkFILEStream::onFork() const { + return new SkFILEStream(fFILE, fEnd, fStart, fCurrent); +} + +size_t SkFILEStream::getLength() const { + return fEnd - fStart; +} + +/////////////////////////////////////////////////////////////////////////////// + +static sk_sp<SkData> newFromParams(const void* src, size_t size, bool copyData) { + if (copyData) { + return SkData::MakeWithCopy(src, size); + } else { + return SkData::MakeWithoutCopy(src, size); + } +} + +SkMemoryStream::SkMemoryStream() { + fData = SkData::MakeEmpty(); + fOffset = 0; +} + +SkMemoryStream::SkMemoryStream(size_t size) { + fData = SkData::MakeUninitialized(size); + fOffset = 0; +} + +SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) { + fData = newFromParams(src, size, copyData); + fOffset = 0; +} + +SkMemoryStream::SkMemoryStream(sk_sp<SkData> data) : fData(std::move(data)) { + if (nullptr == fData) { + fData = SkData::MakeEmpty(); + } + fOffset = 0; +} + +std::unique_ptr<SkMemoryStream> SkMemoryStream::MakeCopy(const void* data, size_t length) { + return std::make_unique<SkMemoryStream>(data, length, true); +} + +std::unique_ptr<SkMemoryStream> SkMemoryStream::MakeDirect(const void* data, size_t length) { + return std::make_unique<SkMemoryStream>(data, length, false); +} + +std::unique_ptr<SkMemoryStream> SkMemoryStream::Make(sk_sp<SkData> data) { + return std::make_unique<SkMemoryStream>(std::move(data)); +} + +void SkMemoryStream::setMemoryOwned(const void* src, size_t size) { + fData = SkData::MakeFromMalloc(src, size); + fOffset = 0; +} + +void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) { + fData = newFromParams(src, size, copyData); + fOffset = 0; +} + +void SkMemoryStream::setData(sk_sp<SkData> data) { + if (nullptr == data) { + fData = SkData::MakeEmpty(); + } else { + fData = data; + } + fOffset = 0; +} + +void SkMemoryStream::skipToAlign4() { + // cast to remove unary-minus warning + fOffset += -(int)fOffset & 0x03; +} + +size_t SkMemoryStream::read(void* buffer, size_t size) { + size_t dataSize = fData->size(); + + if (size > dataSize - fOffset) { + size = dataSize - fOffset; + } + if (buffer) { + memcpy(buffer, fData->bytes() + fOffset, size); + } + fOffset += size; + return size; +} + +size_t SkMemoryStream::peek(void* buffer, size_t size) const { + SkASSERT(buffer != nullptr); + + const size_t currentOffset = fOffset; + SkMemoryStream* nonConstThis = const_cast<SkMemoryStream*>(this); + const size_t bytesRead = nonConstThis->read(buffer, size); + nonConstThis->fOffset = currentOffset; + return bytesRead; +} + +bool SkMemoryStream::isAtEnd() const { + return fOffset == fData->size(); +} + +bool SkMemoryStream::rewind() { + fOffset = 0; + return true; +} + +SkMemoryStream* SkMemoryStream::onDuplicate() const { + return new SkMemoryStream(fData); +} + +size_t SkMemoryStream::getPosition() const { + return fOffset; +} + +bool SkMemoryStream::seek(size_t position) { + fOffset = position > fData->size() + ? fData->size() + : position; + return true; +} + +bool SkMemoryStream::move(long offset) { + return this->seek(fOffset + offset); +} + +SkMemoryStream* SkMemoryStream::onFork() const { + std::unique_ptr<SkMemoryStream> that(this->duplicate()); + that->seek(fOffset); + return that.release(); +} + +size_t SkMemoryStream::getLength() const { + return fData->size(); +} + +const void* SkMemoryStream::getMemoryBase() { + return fData->data(); +} + +const void* SkMemoryStream::getAtPos() { + return fData->bytes() + fOffset; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkFILEWStream::SkFILEWStream(const char path[]) +{ + fFILE = sk_fopen(path, kWrite_SkFILE_Flag); +} + +SkFILEWStream::~SkFILEWStream() +{ + if (fFILE) { + sk_fclose(fFILE); + } +} + +size_t SkFILEWStream::bytesWritten() const { + return sk_ftell(fFILE); +} + +bool SkFILEWStream::write(const void* buffer, size_t size) +{ + if (fFILE == nullptr) { + return false; + } + + if (sk_fwrite(buffer, size, fFILE) != size) + { + SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %zu bytes\n", size);) + sk_fclose(fFILE); + fFILE = nullptr; + return false; + } + return true; +} + +void SkFILEWStream::flush() +{ + if (fFILE) { + sk_fflush(fFILE); + } +} + +void SkFILEWStream::fsync() +{ + flush(); + if (fFILE) { + sk_fsync(fFILE); + } +} + +//////////////////////////////////////////////////////////////////////// + +static inline void sk_memcpy_4bytes(void* dst, const void* src, size_t size) { + if (size == 4) { + memcpy(dst, src, 4); + } else { + memcpy(dst, src, size); + } +} + +#define SkDynamicMemoryWStream_MinBlockSize 4096 + +struct SkDynamicMemoryWStream::Block { + Block* fNext; + char* fCurr; + char* fStop; + + const char* start() const { return (const char*)(this + 1); } + char* start() { return (char*)(this + 1); } + size_t avail() const { return fStop - fCurr; } + size_t written() const { return fCurr - this->start(); } + + void init(size_t size) { + fNext = nullptr; + fCurr = this->start(); + fStop = this->start() + size; + } + + const void* append(const void* data, size_t size) { + SkASSERT((size_t)(fStop - fCurr) >= size); + sk_memcpy_4bytes(fCurr, data, size); + fCurr += size; + return (const void*)((const char*)data + size); + } +}; + +SkDynamicMemoryWStream::SkDynamicMemoryWStream(SkDynamicMemoryWStream&& other) + : fHead(other.fHead) + , fTail(other.fTail) + , fBytesWrittenBeforeTail(other.fBytesWrittenBeforeTail) +{ + other.fHead = nullptr; + other.fTail = nullptr; + other.fBytesWrittenBeforeTail = 0; +} + +SkDynamicMemoryWStream& SkDynamicMemoryWStream::operator=(SkDynamicMemoryWStream&& other) { + if (this != &other) { + this->~SkDynamicMemoryWStream(); + new (this) SkDynamicMemoryWStream(std::move(other)); + } + return *this; +} + +SkDynamicMemoryWStream::~SkDynamicMemoryWStream() { + this->reset(); +} + +void SkDynamicMemoryWStream::reset() { + Block* block = fHead; + while (block != nullptr) { + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; +} + +size_t SkDynamicMemoryWStream::bytesWritten() const { + this->validate(); + + if (fTail) { + return fBytesWrittenBeforeTail + fTail->written(); + } + return 0; +} + +bool SkDynamicMemoryWStream::write(const void* buffer, size_t count) { + if (count > 0) { + SkASSERT(buffer); + size_t size; + + if (fTail) { + if (fTail->avail() > 0) { + size = std::min(fTail->avail(), count); + buffer = fTail->append(buffer, size); + SkASSERT(count >= size); + count -= size; + if (count == 0) { + return true; + } + } + // If we get here, we've just exhausted fTail, so update our tracker + fBytesWrittenBeforeTail += fTail->written(); + } + + size = std::max<size_t>(count, SkDynamicMemoryWStream_MinBlockSize - sizeof(Block)); + size = SkAlign4(size); // ensure we're always a multiple of 4 (see padToAlign4()) + + Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size); + block->init(size); + block->append(buffer, count); + + if (fTail != nullptr) { + fTail->fNext = block; + } else { + fHead = fTail = block; + } + fTail = block; + this->validate(); + } + return true; +} + +bool SkDynamicMemoryWStream::writeToAndReset(SkDynamicMemoryWStream* dst) { + SkASSERT(dst); + SkASSERT(dst != this); + if (0 == this->bytesWritten()) { + return true; + } + if (0 == dst->bytesWritten()) { + *dst = std::move(*this); + return true; + } + dst->fTail->fNext = fHead; + dst->fBytesWrittenBeforeTail += fBytesWrittenBeforeTail + dst->fTail->written(); + dst->fTail = fTail; + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; + return true; +} + +void SkDynamicMemoryWStream::prependToAndReset(SkDynamicMemoryWStream* dst) { + SkASSERT(dst); + SkASSERT(dst != this); + if (0 == this->bytesWritten()) { + return; + } + if (0 == dst->bytesWritten()) { + *dst = std::move(*this); + return; + } + fTail->fNext = dst->fHead; + dst->fHead = fHead; + dst->fBytesWrittenBeforeTail += fBytesWrittenBeforeTail + fTail->written(); + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; + return; +} + + +bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count) { + if (offset + count > this->bytesWritten()) { + return false; // test does not partially modify + } + Block* block = fHead; + while (block != nullptr) { + size_t size = block->written(); + if (offset < size) { + size_t part = offset + count > size ? size - offset : count; + memcpy(buffer, block->start() + offset, part); + if (count <= part) { + return true; + } + count -= part; + buffer = (void*) ((char* ) buffer + part); + } + offset = offset > size ? offset - size : 0; + block = block->fNext; + } + return false; +} + +void SkDynamicMemoryWStream::copyTo(void* dst) const { + SkASSERT(dst); + Block* block = fHead; + while (block != nullptr) { + size_t size = block->written(); + memcpy(dst, block->start(), size); + dst = (void*)((char*)dst + size); + block = block->fNext; + } +} + +bool SkDynamicMemoryWStream::writeToStream(SkWStream* dst) const { + SkASSERT(dst); + for (Block* block = fHead; block != nullptr; block = block->fNext) { + if (!dst->write(block->start(), block->written())) { + return false; + } + } + return true; +} + +void SkDynamicMemoryWStream::padToAlign4() { + // The contract is to write zeros until the entire stream has written a multiple of 4 bytes. + // Our Blocks are guaranteed always be (a) full (except the tail) and (b) a multiple of 4 + // so it is sufficient to just examine the tail (if present). + + if (fTail) { + // cast to remove unary-minus warning + int padBytes = -(int)fTail->written() & 0x03; + if (padBytes) { + int zero = 0; + fTail->append(&zero, padBytes); + } + } +} + + +void SkDynamicMemoryWStream::copyToAndReset(void* ptr) { + if (!ptr) { + this->reset(); + return; + } + // By looping through the source and freeing as we copy, we + // can reduce real memory use with large streams. + char* dst = reinterpret_cast<char*>(ptr); + Block* block = fHead; + while (block != nullptr) { + size_t len = block->written(); + memcpy(dst, block->start(), len); + dst += len; + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; +} + +bool SkDynamicMemoryWStream::writeToAndReset(SkWStream* dst) { + SkASSERT(dst); + // By looping through the source and freeing as we copy, we + // can reduce real memory use with large streams. + bool dstStreamGood = true; + for (Block* block = fHead; block != nullptr; ) { + if (dstStreamGood && !dst->write(block->start(), block->written())) { + dstStreamGood = false; + } + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; + return dstStreamGood; +} + +sk_sp<SkData> SkDynamicMemoryWStream::detachAsData() { + const size_t size = this->bytesWritten(); + if (0 == size) { + return SkData::MakeEmpty(); + } + sk_sp<SkData> data = SkData::MakeUninitialized(size); + this->copyToAndReset(data->writable_data()); + return data; +} + +#ifdef SK_DEBUG +void SkDynamicMemoryWStream::validate() const { + if (!fHead) { + SkASSERT(!fTail); + SkASSERT(fBytesWrittenBeforeTail == 0); + return; + } + SkASSERT(fTail); + + size_t bytes = 0; + const Block* block = fHead; + while (block) { + if (block->fNext) { + bytes += block->written(); + } + block = block->fNext; + } + SkASSERT(bytes == fBytesWrittenBeforeTail); +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkBlockMemoryRefCnt : public SkRefCnt { +public: + explicit SkBlockMemoryRefCnt(SkDynamicMemoryWStream::Block* head) : fHead(head) { } + + ~SkBlockMemoryRefCnt() override { + SkDynamicMemoryWStream::Block* block = fHead; + while (block != nullptr) { + SkDynamicMemoryWStream::Block* next = block->fNext; + sk_free(block); + block = next; + } + } + + SkDynamicMemoryWStream::Block* const fHead; +}; + +class SkBlockMemoryStream : public SkStreamAsset { +public: + SkBlockMemoryStream(sk_sp<SkBlockMemoryRefCnt> headRef, size_t size) + : fBlockMemory(std::move(headRef)), fCurrent(fBlockMemory->fHead) + , fSize(size) , fOffset(0), fCurrentOffset(0) { } + + size_t read(void* buffer, size_t rawCount) override { + size_t count = rawCount; + if (fOffset + count > fSize) { + count = fSize - fOffset; + } + size_t bytesLeftToRead = count; + while (fCurrent != nullptr) { + size_t bytesLeftInCurrent = fCurrent->written() - fCurrentOffset; + size_t bytesFromCurrent = std::min(bytesLeftToRead, bytesLeftInCurrent); + if (buffer) { + memcpy(buffer, fCurrent->start() + fCurrentOffset, bytesFromCurrent); + buffer = SkTAddOffset<void>(buffer, bytesFromCurrent); + } + if (bytesLeftToRead <= bytesFromCurrent) { + fCurrentOffset += bytesFromCurrent; + fOffset += count; + return count; + } + bytesLeftToRead -= bytesFromCurrent; + fCurrent = fCurrent->fNext; + fCurrentOffset = 0; + } + SkASSERT(false); + return 0; + } + + bool isAtEnd() const override { + return fOffset == fSize; + } + + size_t peek(void* buff, size_t bytesToPeek) const override { + SkASSERT(buff != nullptr); + + bytesToPeek = std::min(bytesToPeek, fSize - fOffset); + + size_t bytesLeftToPeek = bytesToPeek; + char* buffer = static_cast<char*>(buff); + const SkDynamicMemoryWStream::Block* current = fCurrent; + size_t currentOffset = fCurrentOffset; + while (bytesLeftToPeek) { + SkASSERT(current); + size_t bytesFromCurrent = std::min(current->written() - currentOffset, bytesLeftToPeek); + memcpy(buffer, current->start() + currentOffset, bytesFromCurrent); + bytesLeftToPeek -= bytesFromCurrent; + buffer += bytesFromCurrent; + current = current->fNext; + currentOffset = 0; + } + return bytesToPeek; + } + + bool rewind() override { + fCurrent = fBlockMemory->fHead; + fOffset = 0; + fCurrentOffset = 0; + return true; + } + + SkBlockMemoryStream* onDuplicate() const override { + return new SkBlockMemoryStream(fBlockMemory, fSize); + } + + size_t getPosition() const override { + return fOffset; + } + + bool seek(size_t position) override { + // If possible, skip forward. + if (position >= fOffset) { + size_t skipAmount = position - fOffset; + return this->skip(skipAmount) == skipAmount; + } + // If possible, move backward within the current block. + size_t moveBackAmount = fOffset - position; + if (moveBackAmount <= fCurrentOffset) { + fCurrentOffset -= moveBackAmount; + fOffset -= moveBackAmount; + return true; + } + // Otherwise rewind and move forward. + return this->rewind() && this->skip(position) == position; + } + + bool move(long offset) override { + return seek(fOffset + offset); + } + + SkBlockMemoryStream* onFork() const override { + SkBlockMemoryStream* that = this->onDuplicate(); + that->fCurrent = this->fCurrent; + that->fOffset = this->fOffset; + that->fCurrentOffset = this->fCurrentOffset; + return that; + } + + size_t getLength() const override { + return fSize; + } + + const void* getMemoryBase() override { + if (fBlockMemory->fHead && !fBlockMemory->fHead->fNext) { + return fBlockMemory->fHead->start(); + } + return nullptr; + } + +private: + sk_sp<SkBlockMemoryRefCnt> const fBlockMemory; + SkDynamicMemoryWStream::Block const * fCurrent; + size_t const fSize; + size_t fOffset; + size_t fCurrentOffset; +}; + +std::unique_ptr<SkStreamAsset> SkDynamicMemoryWStream::detachAsStream() { + if (nullptr == fHead) { + // no need to reset. + return SkMemoryStream::Make(nullptr); + } + if (fHead == fTail) { // one block, may be worth shrinking. + ptrdiff_t used = fTail->fCurr - (char*)fTail; + fHead = fTail = (SkDynamicMemoryWStream::Block*)sk_realloc_throw(fTail, SkToSizeT(used)); + fTail->fStop = fTail->fCurr = (char*)fTail + used; // Update pointers. + SkASSERT(nullptr == fTail->fNext); + SkASSERT(0 == fBytesWrittenBeforeTail); + } + std::unique_ptr<SkStreamAsset> stream + = std::make_unique<SkBlockMemoryStream>(sk_make_sp<SkBlockMemoryRefCnt>(fHead), + this->bytesWritten()); + fHead = nullptr; // signal reset() to not free anything + this->reset(); + return stream; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkDebugfStream::write(const void* buffer, size_t size) { + SkDebugf("%.*s", (int)size, (const char*)buffer); + fBytesWritten += size; + return true; +} + +size_t SkDebugfStream::bytesWritten() const { + return fBytesWritten; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static sk_sp<SkData> mmap_filename(const char path[]) { + FILE* file = sk_fopen(path, kRead_SkFILE_Flag); + if (nullptr == file) { + return nullptr; + } + + auto data = SkData::MakeFromFILE(file); + sk_fclose(file); + return data; +} + +std::unique_ptr<SkStreamAsset> SkStream::MakeFromFile(const char path[]) { + auto data(mmap_filename(path)); + if (data) { + return std::make_unique<SkMemoryStream>(std::move(data)); + } + + // If we get here, then our attempt at using mmap failed, so try normal file access. + auto stream = std::make_unique<SkFILEStream>(path); + if (!stream->isValid()) { + return nullptr; + } + return std::move(stream); +} + +// Declared in SkStreamPriv.h: +sk_sp<SkData> SkCopyStreamToData(SkStream* stream) { + SkASSERT(stream != nullptr); + + if (stream->hasLength()) { + return SkData::MakeFromStream(stream, stream->getLength()); + } + + SkDynamicMemoryWStream tempStream; + const size_t bufferSize = 4096; + char buffer[bufferSize]; + do { + size_t bytesRead = stream->read(buffer, bufferSize); + tempStream.write(buffer, bytesRead); + } while (!stream->isAtEnd()); + return tempStream.detachAsData(); +} + +bool SkStreamCopy(SkWStream* out, SkStream* input) { + const char* base = static_cast<const char*>(input->getMemoryBase()); + if (base && input->hasPosition() && input->hasLength()) { + // Shortcut that avoids the while loop. + size_t position = input->getPosition(); + size_t length = input->getLength(); + SkASSERT(length >= position); + return out->write(&base[position], length - position); + } + char scratch[4096]; + size_t count; + while (true) { + count = input->read(scratch, sizeof(scratch)); + if (0 == count) { + return true; + } + if (!out->write(scratch, count)) { + return false; + } + } +} + +bool StreamRemainingLengthIsBelow(SkStream* stream, size_t len) { + if (stream->hasLength() && stream->hasPosition()) { + size_t remainingBytes = stream->getLength() - stream->getPosition(); + return len > remainingBytes; + } + return false; +} |