/* * 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 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(const_cast(fBuffer), fTrulyBuffered); SkDEBUGCODE(const size_t bytesRead =) // This stream is rewindable, so it should be safe to call the non-const // read() const_cast(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 = SkTMin(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 data(SkData::MakeWithCopy(fBuffer, fBytesBuffered)); SkASSERT(nullptr == fMarkedData.find(fPosition)); fMarkedData.set(fPosition, data.release()); } return fPosition; } sk_sp 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(*data); } SkASSERT(length <= fStream->getLength() && position <= fStream->getLength() - length); const size_t oldPosition = fStream->getPosition(); if (!fStream->seek(position)) { return nullptr; } sk_sp data(SkData::MakeUninitialized(length)); void* dst = data->writable_data(); const bool success = fStream->read(dst, length) == length; fStream->seek(oldPosition); return success ? data : nullptr; }