| /* | |
| * Copyright 2016 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/codec/SkStreamBuffer.h" | |
| SkStreamBuffer::SkStreamBuffer(std::unique_ptr<SkStream> stream) | |
| : fStream(std::move(stream)) | |
| , fPosition(0) | |
| , fBytesBuffered(0) | |
| , fHasLengthAndPosition(fStream->hasLength() && fStream->hasPosition()) | |
| , fTrulyBuffered(0) | |
| {} | |
| SkStreamBuffer::~SkStreamBuffer() { | |
| fMarkedData.foreach([](size_t, SkData** data) { (*data)->unref(); }); | |
| } | |
| const char* SkStreamBuffer::get() const { | |
| SkASSERT(fBytesBuffered >= 1); | |
| if (fHasLengthAndPosition && fTrulyBuffered < fBytesBuffered) { | |
| const size_t bytesToBuffer = fBytesBuffered - fTrulyBuffered; | |
| char* dst = SkTAddOffset<char>(const_cast<char*>(fBuffer), fTrulyBuffered); | |
| SkDEBUGCODE(const size_t bytesRead =) | |
| // This stream is rewindable, so it should be safe to call the non-const | |
| // read() | |
| const_cast<SkStream*>(fStream.get())->read(dst, bytesToBuffer); | |
| SkASSERT(bytesRead == bytesToBuffer); | |
| fTrulyBuffered = fBytesBuffered; | |
| } | |
| return fBuffer; | |
| } | |
| bool SkStreamBuffer::buffer(size_t totalBytesToBuffer) { | |
| // FIXME (scroggo): What should we do if the client tries to read too much? | |
| // Should not be a problem in GIF. | |
| SkASSERT(totalBytesToBuffer <= kMaxSize); | |
| if (totalBytesToBuffer <= fBytesBuffered) { | |
| return true; | |
| } | |
| if (fHasLengthAndPosition) { | |
| const size_t remaining = fStream->getLength() - fStream->getPosition() + fTrulyBuffered; | |
| fBytesBuffered = std::min(remaining, totalBytesToBuffer); | |
| } else { | |
| const size_t extraBytes = totalBytesToBuffer - fBytesBuffered; | |
| const size_t bytesBuffered = fStream->read(fBuffer + fBytesBuffered, extraBytes); | |
| fBytesBuffered += bytesBuffered; | |
| } | |
| return fBytesBuffered == totalBytesToBuffer; | |
| } | |
| size_t SkStreamBuffer::markPosition() { | |
| SkASSERT(fBytesBuffered >= 1); | |
| if (!fHasLengthAndPosition) { | |
| sk_sp<SkData> data(SkData::MakeWithCopy(fBuffer, fBytesBuffered)); | |
| SkASSERT(nullptr == fMarkedData.find(fPosition)); | |
| fMarkedData.set(fPosition, data.release()); | |
| } | |
| return fPosition; | |
| } | |
| sk_sp<SkData> SkStreamBuffer::getDataAtPosition(size_t position, size_t length) { | |
| if (!fHasLengthAndPosition) { | |
| SkData** data = fMarkedData.find(position); | |
| SkASSERT(data); | |
| SkASSERT((*data)->size() == length); | |
| return sk_ref_sp<SkData>(*data); | |
| } | |
| SkASSERT(length <= fStream->getLength() && | |
| position <= fStream->getLength() - length); | |
| const size_t oldPosition = fStream->getPosition(); | |
| if (!fStream->seek(position)) { | |
| return nullptr; | |
| } | |
| sk_sp<SkData> data(SkData::MakeUninitialized(length)); | |
| void* dst = data->writable_data(); | |
| const bool success = fStream->read(dst, length) == length; | |
| fStream->seek(oldPosition); | |
| return success ? data : nullptr; | |
| } |