| /* |
| * 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; |
| } |