blob: 747852b6fa023e991eab2db1c7c32ffb3ac31873 [file] [log] [blame]
/*
* 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/SkTPin.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/base/SkMalloc.h"
#include "include/private/base/SkTFitsIn.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;
}