/*
* 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 "SkStream.h"
#include "SkData.h"
#include "SkFixed.h"
#include "SkString.h"
#include "SkOSFile.h"
///////////////////////////////////////////////////////////////////////////////
int8_t SkStream::readS8() {
int8_t value;
SkDEBUGCODE(size_t len =) this->read(&value, 1);
SkASSERT(1 == len);
return value;
}
int16_t SkStream::readS16() {
int16_t value;
SkDEBUGCODE(size_t len =) this->read(&value, 2);
SkASSERT(2 == len);
return value;
}
int32_t SkStream::readS32() {
int32_t value;
SkDEBUGCODE(size_t len =) this->read(&value, 4);
SkASSERT(4 == len);
return value;
}
SkScalar SkStream::readScalar() {
SkScalar value;
SkDEBUGCODE(size_t len =) this->read(&value, sizeof(SkScalar));
SkASSERT(sizeof(SkScalar) == len);
return value;
}
#define SK_MAX_BYTE_FOR_U8 0xFD
#define SK_BYTE_SENTINEL_FOR_U16 0xFE
#define SK_BYTE_SENTINEL_FOR_U32 0xFF
size_t SkStream::readPackedUInt() {
uint8_t byte;
if (!this->read(&byte, 1)) {
return 0;
}
if (SK_BYTE_SENTINEL_FOR_U16 == byte) {
return this->readU16();
} else if (SK_BYTE_SENTINEL_FOR_U32 == byte) {
return this->readU32();
} else {
return byte;
}
}
SkData* SkStream::readData() {
size_t size = this->readU32();
if (0 == size) {
return SkData::NewEmpty();
} else {
void* buffer = sk_malloc_throw(size);
this->read(buffer, size);
return SkData::NewFromMalloc(buffer, size);
}
}
//////////////////////////////////////////////////////////////////////////////////////
SkWStream::~SkWStream()
{
}
void SkWStream::newline()
{
this->write("\n", 1);
}
void SkWStream::flush()
{
}
bool SkWStream::writeText(const char text[])
{
SkASSERT(text);
return this->write(text, strlen(text));
}
bool SkWStream::writeDecAsText(int32_t dec)
{
SkString tmp;
tmp.appendS32(dec);
return this->write(tmp.c_str(), tmp.size());
}
bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits)
{
SkString tmp;
tmp.appendS64(dec, minDigits);
return this->write(tmp.c_str(), tmp.size());
}
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)
{
SkString tmp;
tmp.appendScalar(value);
return this->write(tmp.c_str(), tmp.size());
}
bool SkWStream::write8(U8CPU value) {
uint8_t v = SkToU8(value);
return this->write(&v, 1);
}
bool SkWStream::write16(U16CPU value) {
uint16_t v = SkToU16(value);
return this->write(&v, 2);
}
bool SkWStream::write32(uint32_t value) {
return this->write(&value, 4);
}
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;
}
bool SkWStream::writeData(const SkData* data) {
if (data) {
this->write32(SkToU32(data->size()));
this->write(data->data(), data->size());
} else {
this->write32(0);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
SkFILEStream::SkFILEStream(const char file[]) : fName(file), fOwnership(kCallerPasses_Ownership) {
fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL;
}
SkFILEStream::SkFILEStream(FILE* file, Ownership ownership)
: fFILE((SkFILE*)file)
, fOwnership(ownership) {
}
SkFILEStream::~SkFILEStream() {
if (fFILE && fOwnership != kCallerRetains_Ownership) {
sk_fclose(fFILE);
}
}
void SkFILEStream::setPath(const char path[]) {
fName.set(path);
if (fFILE) {
sk_fclose(fFILE);
fFILE = NULL;
}
if (path) {
fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag);
}
}
size_t SkFILEStream::read(void* buffer, size_t size) {
if (fFILE) {
return sk_fread(buffer, size, fFILE);
}
return 0;
}
bool SkFILEStream::isAtEnd() const {
return sk_feof(fFILE);
}
bool SkFILEStream::rewind() {
if (fFILE) {
if (sk_frewind(fFILE)) {
return true;
}
// we hit an error
sk_fclose(fFILE);
fFILE = NULL;
}
return false;
}
SkStreamAsset* SkFILEStream::duplicate() const {
if (NULL == fFILE) {
return new SkMemoryStream();
}
if (NULL != fData.get()) {
return new SkMemoryStream(fData);
}
if (!fName.isEmpty()) {
SkAutoTUnref<SkFILEStream> that(new SkFILEStream(fName.c_str()));
if (sk_fidentical(that->fFILE, this->fFILE)) {
return that.detach();
}
}
fData.reset(SkData::NewFromFILE(fFILE));
if (NULL == fData.get()) {
return NULL;
}
return new SkMemoryStream(fData);
}
size_t SkFILEStream::getPosition() const {
return sk_ftell(fFILE);
}
bool SkFILEStream::seek(size_t position) {
return sk_fseek(fFILE, position);
}
bool SkFILEStream::move(long offset) {
return sk_fmove(fFILE, offset);
}
SkStreamAsset* SkFILEStream::fork() const {
SkAutoTUnref<SkStreamAsset> that(this->duplicate());
that->seek(this->getPosition());
return that.detach();
}
size_t SkFILEStream::getLength() const {
return sk_fgetsize(fFILE);
}
const void* SkFILEStream::getMemoryBase() {
if (NULL == fData.get()) {
return NULL;
}
return fData->data();
}
///////////////////////////////////////////////////////////////////////////////
static SkData* newFromParams(const void* src, size_t size, bool copyData) {
if (copyData) {
return SkData::NewWithCopy(src, size);
} else {
return SkData::NewWithProc(src, size, NULL, NULL);
}
}
SkMemoryStream::SkMemoryStream() {
fData = SkData::NewEmpty();
fOffset = 0;
}
SkMemoryStream::SkMemoryStream(size_t size) {
fData = SkData::NewFromMalloc(sk_malloc_throw(size), size);
fOffset = 0;
}
SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) {
fData = newFromParams(src, size, copyData);
fOffset = 0;
}
SkMemoryStream::SkMemoryStream(SkData* data) {
if (NULL == data) {
fData = SkData::NewEmpty();
} else {
fData = data;
fData->ref();
}
fOffset = 0;
}
SkMemoryStream::~SkMemoryStream() {
fData->unref();
}
void SkMemoryStream::setMemoryOwned(const void* src, size_t size) {
fData->unref();
fData = SkData::NewFromMalloc(src, size);
fOffset = 0;
}
void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) {
fData->unref();
fData = newFromParams(src, size, copyData);
fOffset = 0;
}
SkData* SkMemoryStream::copyToData() const {
fData->ref();
return fData;
}
SkData* SkMemoryStream::setData(SkData* data) {
fData->unref();
if (NULL == data) {
fData = SkData::NewEmpty();
} else {
fData = data;
fData->ref();
}
fOffset = 0;
return data;
}
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;
}
bool SkMemoryStream::isAtEnd() const {
return fOffset == fData->size();
}
bool SkMemoryStream::rewind() {
fOffset = 0;
return true;
}
SkMemoryStream* SkMemoryStream::duplicate() const {
return SkNEW_ARGS(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::fork() const {
SkAutoTUnref<SkMemoryStream> that(this->duplicate());
that->seek(fOffset);
return that.detach();
}
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 == NULL) {
return false;
}
if (sk_fwrite(buffer, size, fFILE) != size)
{
SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);)
sk_fclose(fFILE);
fFILE = NULL;
return false;
}
return true;
}
void SkFILEWStream::flush()
{
if (fFILE) {
sk_fflush(fFILE);
}
}
////////////////////////////////////////////////////////////////////////
SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size)
: fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0)
{
}
bool SkMemoryWStream::write(const void* buffer, size_t size) {
size = SkTMin(size, fMaxLength - fBytesWritten);
if (size > 0) {
memcpy(fBuffer + fBytesWritten, buffer, size);
fBytesWritten += size;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////
#define SkDynamicMemoryWStream_MinBlockSize 256
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 = NULL;
fCurr = this->start();
fStop = this->start() + size;
}
const void* append(const void* data, size_t size)
{
SkASSERT((size_t)(fStop - fCurr) >= size);
memcpy(fCurr, data, size);
fCurr += size;
return (const void*)((const char*)data + size);
}
};
SkDynamicMemoryWStream::SkDynamicMemoryWStream()
: fHead(NULL), fTail(NULL), fBytesWritten(0), fCopy(NULL)
{
}
SkDynamicMemoryWStream::~SkDynamicMemoryWStream()
{
reset();
}
void SkDynamicMemoryWStream::reset()
{
this->invalidateCopy();
Block* block = fHead;
while (block != NULL) {
Block* next = block->fNext;
sk_free(block);
block = next;
}
fHead = fTail = NULL;
fBytesWritten = 0;
}
bool SkDynamicMemoryWStream::write(const void* buffer, size_t count)
{
if (count > 0) {
this->invalidateCopy();
fBytesWritten += count;
size_t size;
if (fTail != NULL && fTail->avail() > 0) {
size = SkTMin(fTail->avail(), count);
buffer = fTail->append(buffer, size);
SkASSERT(count >= size);
count -= size;
if (count == 0)
return true;
}
size = SkTMax<size_t>(count, SkDynamicMemoryWStream_MinBlockSize);
Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
block->init(size);
block->append(buffer, count);
if (fTail != NULL)
fTail->fNext = block;
else
fHead = fTail = block;
fTail = block;
}
return true;
}
bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count)
{
if (offset + count > fBytesWritten) {
return false; // test does not partially modify
}
this->invalidateCopy();
Block* block = fHead;
while (block != NULL) {
size_t size = block->written();
if (offset < size) {
size_t part = offset + count > size ? size - offset : count;
memcpy(block->start() + offset, buffer, part);
if (count <= part)
return true;
count -= part;
buffer = (const void*) ((char* ) buffer + part);
}
offset = offset > size ? offset - size : 0;
block = block->fNext;
}
return false;
}
bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count)
{
if (offset + count > fBytesWritten)
return false; // test does not partially modify
Block* block = fHead;
while (block != NULL) {
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
{
if (fCopy) {
memcpy(dst, fCopy->data(), fBytesWritten);
} else {
Block* block = fHead;
while (block != NULL) {
size_t size = block->written();
memcpy(dst, block->start(), size);
dst = (void*)((char*)dst + size);
block = block->fNext;
}
}
}
void SkDynamicMemoryWStream::padToAlign4()
{
// cast to remove unary-minus warning
int padBytes = -(int)fBytesWritten & 0x03;
if (padBytes == 0)
return;
int zero = 0;
write(&zero, padBytes);
}
SkData* SkDynamicMemoryWStream::copyToData() const {
if (NULL == fCopy) {
void* buffer = sk_malloc_throw(fBytesWritten);
this->copyTo(buffer);
fCopy = SkData::NewFromMalloc(buffer, fBytesWritten);
}
fCopy->ref();
return fCopy;
}
void SkDynamicMemoryWStream::invalidateCopy() {
if (fCopy) {
fCopy->unref();
fCopy = NULL;
}
}
class SkBlockMemoryRefCnt : public SkRefCnt {
public:
explicit SkBlockMemoryRefCnt(SkDynamicMemoryWStream::Block* head) : fHead(head) { }
virtual ~SkBlockMemoryRefCnt() {
SkDynamicMemoryWStream::Block* block = fHead;
while (block != NULL) {
SkDynamicMemoryWStream::Block* next = block->fNext;
sk_free(block);
block = next;
}
}
SkDynamicMemoryWStream::Block* const fHead;
};
class SkBlockMemoryStream : public SkStreamAsset {
public:
SkBlockMemoryStream(SkDynamicMemoryWStream::Block* head, size_t size)
: fBlockMemory(SkNEW_ARGS(SkBlockMemoryRefCnt, (head))), fCurrent(head)
, fSize(size) , fOffset(0), fCurrentOffset(0) { }
SkBlockMemoryStream(SkBlockMemoryRefCnt* headRef, size_t size)
: fBlockMemory(SkRef(headRef)), fCurrent(fBlockMemory->fHead)
, fSize(size) , fOffset(0), fCurrentOffset(0) { }
virtual size_t read(void* buffer, size_t rawCount) SK_OVERRIDE {
size_t count = rawCount;
if (fOffset + count > fSize) {
count = fSize - fOffset;
}
size_t bytesLeftToRead = count;
while (fCurrent != NULL) {
size_t bytesLeftInCurrent = fCurrent->written() - fCurrentOffset;
size_t bytesFromCurrent = SkTMin(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;
}
virtual bool isAtEnd() const SK_OVERRIDE {
return fOffset == fSize;
}
virtual bool rewind() SK_OVERRIDE {
fCurrent = fBlockMemory->fHead;
fOffset = 0;
fCurrentOffset = 0;
return true;
}
virtual SkBlockMemoryStream* duplicate() const SK_OVERRIDE {
return SkNEW_ARGS(SkBlockMemoryStream, (fBlockMemory.get(), fSize));
}
virtual size_t getPosition() const SK_OVERRIDE {
return fOffset;
}
virtual bool seek(size_t position) SK_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;
}
virtual bool move(long offset) SK_OVERRIDE {
return seek(fOffset + offset);
}
virtual SkBlockMemoryStream* fork() const SK_OVERRIDE {
SkAutoTUnref<SkBlockMemoryStream> that(this->duplicate());
that->fCurrent = this->fCurrent;
that->fOffset = this->fOffset;
that->fCurrentOffset = this->fCurrentOffset;
return that.detach();
}
virtual size_t getLength() const SK_OVERRIDE {
return fSize;
}
virtual const void* getMemoryBase() SK_OVERRIDE {
if (NULL == fBlockMemory->fHead->fNext) {
return fBlockMemory->fHead->start();
}
return NULL;
}
private:
SkAutoTUnref<SkBlockMemoryRefCnt> const fBlockMemory;
SkDynamicMemoryWStream::Block const * fCurrent;
size_t const fSize;
size_t fOffset;
size_t fCurrentOffset;
};
SkStreamAsset* SkDynamicMemoryWStream::detachAsStream() {
if (fCopy) {
SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (fCopy));
this->reset();
return stream;
}
SkBlockMemoryStream* stream = SkNEW_ARGS(SkBlockMemoryStream, (fHead, fBytesWritten));
fHead = 0;
this->reset();
return stream;
}
///////////////////////////////////////////////////////////////////////////////
void SkDebugWStream::newline()
{
#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
SkDebugf("\n");
fBytesWritten++;
#endif
}
bool SkDebugWStream::write(const void* buffer, size_t size)
{
#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
char* s = new char[size+1];
memcpy(s, buffer, size);
s[size] = 0;
SkDebugf("%s", s);
delete[] s;
fBytesWritten += size;
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static SkData* mmap_filename(const char path[]) {
SkFILE* file = sk_fopen(path, kRead_SkFILE_Flag);
if (NULL == file) {
return NULL;
}
SkData* data = SkData::NewFromFILE(file);
sk_fclose(file);
return data;
}
SkStreamAsset* SkStream::NewFromFile(const char path[]) {
SkAutoTUnref<SkData> data(mmap_filename(path));
if (data.get()) {
return SkNEW_ARGS(SkMemoryStream, (data.get()));
}
// If we get here, then our attempt at using mmap failed, so try normal
// file access.
SkFILEStream* stream = SkNEW_ARGS(SkFILEStream, (path));
if (!stream->isValid()) {
stream->unref();
stream = NULL;
}
return stream;
}