Chromium Code Reviews| Index: src/core/SkRWBuffer.cpp |
| diff --git a/src/core/SkRWBuffer.cpp b/src/core/SkRWBuffer.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dd4091ab6c868e984fb993e34f8b7df34c755702 |
| --- /dev/null |
| +++ b/src/core/SkRWBuffer.cpp |
| @@ -0,0 +1,354 @@ |
| +/* |
| + * Copyright 2015 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "SkRWBuffer.h" |
| +#include "SkStream.h" |
| + |
| +// Force small chunks to be a page's worth |
| +static const size_t kMinAllocSize = 4096; |
| + |
| +struct SkBufferBlock { |
| + SkBufferBlock* fNext; |
| + size_t fUsed; |
| + size_t fCapacity; |
| + |
| + const void* startData() const { return this + 1; }; |
| + |
| + size_t avail() const { return fCapacity - fUsed; } |
| + void* availData() { return (char*)this->startData() + fUsed; } |
| + |
| + static SkBufferBlock* Alloc(size_t length) { |
| + size_t capacity = LengthToCapacity(length); |
| + SkBufferBlock* block = (SkBufferBlock*)sk_malloc_throw(sizeof(SkBufferBlock) + capacity); |
| + block->fNext = NULL; |
| + block->fUsed = 0; |
| + block->fCapacity = capacity; |
| + return block; |
| + } |
| + |
| + // Return number of bytes actually appended |
| + size_t append(const void* src, size_t length) { |
| + this->validate(); |
| + size_t amount = SkTMin(this->avail(), length); |
| + memcpy(this->availData(), src, amount); |
| + fUsed += amount; |
| + this->validate(); |
| + return amount; |
| + } |
| + |
| + void validate() const { |
| +#ifdef SK_DEBUG |
| + SkASSERT(fCapacity > 0); |
| + SkASSERT(fUsed <= fCapacity); |
| +#endif |
| + } |
| + |
| +private: |
| + static size_t LengthToCapacity(size_t length) { |
| + const size_t minSize = kMinAllocSize - sizeof(SkBufferBlock); |
| + return SkTMax(length, minSize); |
| + } |
| +}; |
| + |
| +struct SkBufferHead { |
| + mutable int32_t fRefCnt; |
| + SkBufferBlock fBlock; |
| + |
| + static size_t LengthToCapacity(size_t length) { |
|
scroggo
2015/04/28 17:27:39
Could be private?
reed2
2015/04/29 00:43:49
Possibly, tho this struct is private just to this
|
| + const size_t minSize = kMinAllocSize - sizeof(SkBufferHead); |
| + return SkTMax(length, minSize); |
| + } |
| + |
| + static SkBufferHead* Alloc(size_t length) { |
| + size_t capacity = LengthToCapacity(length); |
| + size_t size = sizeof(SkBufferHead) + capacity; |
| + SkBufferHead* head = (SkBufferHead*)sk_malloc_throw(size); |
| + head->fRefCnt = 1; |
| + head->fBlock.fNext = NULL; |
| + head->fBlock.fUsed = 0; |
| + head->fBlock.fCapacity = capacity; |
| + return head; |
| + } |
| + |
| + void ref() const { |
| + SkASSERT(fRefCnt > 0); |
| + sk_atomic_inc(&fRefCnt); |
| + } |
| + |
| + void unref() const { |
| + SkASSERT(fRefCnt > 0); |
| + // A release here acts in place of all releases we "should" have been doing in ref(). |
| + if (1 == sk_atomic_fetch_add(&fRefCnt, -1, sk_memory_order_acq_rel)) { |
|
scroggo
2015/04/28 17:27:39
It would be nice to share this code with the other
reed2
2015/04/29 00:43:49
Agreed, but I haven't seen how to do that sharing
|
| + // Like unique(), the acquire is only needed on success. |
| + SkBufferBlock* block = fBlock.fNext; |
| + sk_free((void*)this); |
| + while (block) { |
| + SkBufferBlock* next = block->fNext; |
| + sk_free(block); |
| + block = next; |
| + } |
| + SkDebugf("done\n"); |
|
scroggo
2015/04/28 17:27:39
Do you want to check this in?
reed2
2015/04/29 00:43:49
Doh! Removed.
|
| + } |
| + } |
| + |
| + void validate(size_t minUsed, SkBufferBlock* tail = NULL) const { |
| +#ifdef SK_DEBUG |
| + SkASSERT(fRefCnt > 0); |
| + size_t totalUsed = 0; |
| + const SkBufferBlock* block = &fBlock; |
| + const SkBufferBlock* lastBlock = block; |
| + while (block) { |
| + block->validate(); |
| + totalUsed += block->fUsed; |
| + lastBlock = block; |
| + block = block->fNext; |
| + } |
| + SkASSERT(minUsed <= totalUsed); |
| + if (tail) { |
| + SkASSERT(tail == lastBlock); |
| + } |
| +#endif |
| + } |
| +}; |
| + |
| +SkROBuffer::SkROBuffer(const SkBufferHead* head, size_t used) : fHead(SkRef(head)), fUsed(used) { |
|
scroggo
2015/04/28 17:27:39
This should be SkSafeRef(head), since head may be
reed2
2015/04/29 00:43:49
Done.
|
| + if (head) { |
| + SkASSERT(used > 0); |
| + head->validate(used); |
| + } else { |
| + SkASSERT(0 == used); |
| + } |
| +} |
| + |
| +SkROBuffer::~SkROBuffer() { |
| + if (fHead) { |
| + fHead->validate(fUsed); |
| + fHead->unref(); |
| + } |
| +} |
| + |
| +SkROBuffer::Iter::Iter(const SkROBuffer* buffer) { |
| + this->reset(buffer); |
| +} |
| + |
| +void SkROBuffer::Iter::reset(const SkROBuffer* buffer) { |
| + if (buffer) { |
| + fBlock = &buffer->fHead->fBlock; |
| + fRemaining = buffer->fUsed; |
| + } else { |
| + fBlock = NULL; |
| + fRemaining = 0; |
| + } |
| +} |
| + |
| +const void* SkROBuffer::Iter::data() const { |
| + return fRemaining ? fBlock->startData() : NULL; |
| +} |
| + |
| +size_t SkROBuffer::Iter::size() const { |
| + return SkTMin(fBlock->fUsed, fRemaining); |
| +} |
| + |
| +bool SkROBuffer::Iter::next() { |
| + if (fRemaining) { |
| + fRemaining -= this->size(); |
| + fBlock = fBlock->fNext; |
| + } |
| + return fRemaining != 0; |
| +} |
| + |
| +SkRWBuffer::SkRWBuffer(size_t initialCapacity) : fHead(NULL), fTail(NULL), fTotalUsed(0) {} |
| + |
| +SkRWBuffer::~SkRWBuffer() { |
| + this->validate(); |
| + fHead->unref(); |
| +} |
| + |
| +void SkRWBuffer::append(const void* src, size_t length) { |
| + this->validate(); |
| + if (0 == length) { |
| + return; |
| + } |
| + |
| + fTotalUsed += length; |
| + |
| + if (NULL == fHead) { |
| + fHead = SkBufferHead::Alloc(length); |
| + fTail = &fHead->fBlock; |
| + } |
| + |
| + size_t written = fTail->append(src, length); |
| + SkASSERT(written <= length); |
| + src = (const char*)src + written; |
| + length -= written; |
| + |
| + if (length) { |
| + SkBufferBlock* block = SkBufferBlock::Alloc(length); |
| + fTail->fNext = block; |
| + fTail = block; |
| + written = fTail->append(src, length); |
| + SkASSERT(written == length); |
| + } |
| + this->validate(); |
| +} |
| + |
| +void* SkRWBuffer::append(size_t length) { |
| + this->validate(); |
| + if (0 == length) { |
| + return NULL; |
| + } |
| + |
| + fTotalUsed += length; |
| + |
| + if (NULL == fHead) { |
| + fHead = SkBufferHead::Alloc(length); |
| + fTail = &fHead->fBlock; |
| + } else if (fTail->avail() < length) { |
| + SkBufferBlock* block = SkBufferBlock::Alloc(length); |
| + fTail->fNext = block; |
| + fTail = block; |
| + } |
| + |
| + fTail->fUsed += length; |
| + this->validate(); |
| + return (char*)fTail->availData() - length; |
| +} |
| + |
| +#ifdef SK_DEBUG |
| +void SkRWBuffer::validate() const { |
| + if (fHead) { |
| + fHead->validate(fTotalUsed, fTail); |
| + } else { |
| + SkASSERT(NULL == fTail); |
| + SkASSERT(0 == fTotalUsed); |
| + } |
| +} |
| +#endif |
| + |
| +SkROBuffer* SkRWBuffer::newRBufferSnapshot() const { |
| + return SkNEW_ARGS(SkROBuffer, (fHead, fTotalUsed)); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +class SkROBufferStreamAsset : public SkStreamAsset { |
| + void validate() const { |
| +#ifdef SK_DEBUG |
| + SkASSERT(fGlobalOffset <= fBuffer->size()); |
| + SkASSERT(fLocalOffset <= fIter.size()); |
| + SkASSERT(fLocalOffset <= fGlobalOffset); |
| +#endif |
| + } |
| + |
| + class AutoValidate { |
|
scroggo
2015/04/28 17:27:39
Only needed in SK_DEBUG?
reed2
2015/04/29 00:43:49
Done.
|
| + SkROBufferStreamAsset* fStream; |
| + public: |
| + AutoValidate(SkROBufferStreamAsset* stream) : fStream(stream) { stream->validate(); } |
| + ~AutoValidate() { fStream->validate(); } |
| + }; |
| + |
| +#ifdef SK_DEBUG |
| + #define AUTO_VALIDATE AutoValidate av(this); |
| +#else |
| + #define AUTO_VALIDATE |
| +#endif |
| + |
| +public: |
| + SkROBufferStreamAsset(const SkROBuffer* buffer) : fBuffer(SkRef(buffer)), fIter(buffer) { |
| + fGlobalOffset = fLocalOffset = 0; |
| + } |
| + |
| + virtual ~SkROBufferStreamAsset() { fBuffer->unref(); } |
| + |
| + size_t getLength() const override { return fBuffer->size(); } |
| + |
| + bool rewind() override { |
| + AUTO_VALIDATE |
| + fIter.reset(fBuffer); |
| + fGlobalOffset = fLocalOffset = 0; |
| + return true; |
| + } |
| + |
| + size_t read(void* dst, size_t request) override { |
| + AUTO_VALIDATE |
| + size_t bytesRead = 0; |
| + for (;;) { |
| + size_t size = fIter.size(); |
|
scroggo
2015/04/28 17:27:39
const?
|
| + SkASSERT(fLocalOffset <= size); |
| + size_t avail = SkTMin(size - fLocalOffset, request - bytesRead); |
|
scroggo
2015/04/28 17:27:39
const?
|
| + if (dst) { |
| + memcpy(dst, (const char*)fIter.data() + fLocalOffset, avail); |
| + dst = (char*)dst + avail; |
| + } |
| + bytesRead += avail; |
| + fLocalOffset += avail; |
| + SkASSERT(bytesRead <= request); |
| + if (bytesRead == request) { |
| + break; |
| + } |
| + // If we get here, we've exhausted the current iter |
| + SkASSERT(fLocalOffset == size); |
| + fLocalOffset = 0; |
| + if (!fIter.next()) { |
| + break; // ran out of data |
| + } |
| + } |
| + fGlobalOffset += bytesRead; |
| + SkASSERT(fGlobalOffset <= fBuffer->size()); |
| + return bytesRead; |
| + } |
| + |
| + bool isAtEnd() const override { |
| + return fBuffer->size() == fGlobalOffset; |
| + } |
| + |
| + SkStreamAsset* duplicate() const override { |
| + return SkNEW_ARGS(SkROBufferStreamAsset, (fBuffer)); |
| + } |
| + |
| + size_t getPosition() const { |
| + return fGlobalOffset; |
| + } |
| + |
| + bool seek(size_t position) { |
| + AUTO_VALIDATE |
| + if (position < fGlobalOffset) { |
| + this->rewind(); |
| + } |
| + (void)this->skip(position - fGlobalOffset); |
| + return true; |
| + } |
| + |
| + bool move(long offset) { |
| + AUTO_VALIDATE |
| + offset += fGlobalOffset; |
| + if (offset <= 0) { |
| + this->rewind(); |
| + } else { |
| + (void)this->seek(SkToSizeT(offset)); |
| + } |
| + return true; |
| + } |
| + |
| + SkStreamAsset* fork() const override { |
| + SkStreamAsset* clone = this->duplicate(); |
| + clone->seek(this->getPosition()); |
| + return clone; |
| + } |
| + |
| + |
| +private: |
| + const SkROBuffer* fBuffer; |
| + SkROBuffer::Iter fIter; |
| + size_t fLocalOffset; |
| + size_t fGlobalOffset; |
| +}; |
| + |
| +SkStreamAsset* SkRWBuffer::newStreamSnapshot() const { |
| + SkAutoTUnref<SkROBuffer> buffer(this->newRBufferSnapshot()); |
| + return SkNEW_ARGS(SkROBufferStreamAsset, (buffer)); |
| +} |