You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.8 KiB
244 lines
7.8 KiB
/*
|
|
* Copyright 2013 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "include/codec/SkCodec.h"
|
|
#include "include/core/SkStream.h"
|
|
#include "FrontBufferedStream.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
namespace {
|
|
class FrontBufferedStream : public SkStreamRewindable {
|
|
public:
|
|
// Called by Make.
|
|
FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize);
|
|
~FrontBufferedStream() override;
|
|
|
|
bool failedToAllocateBuffer() const { return !fBuffer; }
|
|
|
|
size_t read(void* buffer, size_t size) override;
|
|
|
|
size_t peek(void* buffer, size_t size) const override;
|
|
|
|
bool isAtEnd() const override;
|
|
|
|
bool rewind() override;
|
|
|
|
bool hasLength() const override { return fHasLength; }
|
|
|
|
size_t getLength() const override { return fLength; }
|
|
|
|
private:
|
|
SkStreamRewindable* onDuplicate() const override { return nullptr; }
|
|
|
|
std::unique_ptr<SkStream> fStream;
|
|
const bool fHasLength;
|
|
const size_t fLength;
|
|
// Current offset into the stream. Always >= 0.
|
|
size_t fOffset;
|
|
// Amount that has been buffered by calls to read. Will always be less than
|
|
// fBufferSize.
|
|
size_t fBufferedSoFar;
|
|
// Total size of the buffer.
|
|
const size_t fBufferSize;
|
|
char* fBuffer;
|
|
static constexpr size_t kStorageSize = SkCodec::MinBufferedBytesNeeded();
|
|
char fStorage[kStorageSize];
|
|
|
|
// Read up to size bytes from already buffered data, and copy to
|
|
// dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less
|
|
// than fBufferedSoFar.
|
|
size_t readFromBuffer(char* dst, size_t size);
|
|
|
|
// Buffer up to size bytes from the stream, and copy to dst if non-
|
|
// nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
|
|
// less than fBufferedSoFar, and size is greater than 0.
|
|
size_t bufferAndWriteTo(char* dst, size_t size);
|
|
|
|
// Read up to size bytes directly from the stream and into dst if non-
|
|
// nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered
|
|
// data, and size is greater than 0.
|
|
size_t readDirectlyFromStream(char* dst, size_t size);
|
|
|
|
using INHERITED = SkStream;
|
|
};
|
|
} // anonymous namespace
|
|
|
|
namespace android {
|
|
namespace skia {
|
|
|
|
std::unique_ptr<SkStreamRewindable> FrontBufferedStream::Make(std::unique_ptr<SkStream> stream,
|
|
size_t bufferSize) {
|
|
if (!stream) {
|
|
return nullptr;
|
|
}
|
|
auto frontBufferedStream = std::make_unique<::FrontBufferedStream>(
|
|
std::move(stream), bufferSize);
|
|
if (frontBufferedStream->failedToAllocateBuffer()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Work around a warning regarding a copy on older compilers.
|
|
return std::move(frontBufferedStream);
|
|
}
|
|
} // namespace skia
|
|
} // namespace android
|
|
|
|
namespace {
|
|
FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
|
|
: fStream(std::move(stream))
|
|
, fHasLength(fStream->hasPosition() && fStream->hasLength())
|
|
, fLength(fStream->getLength() - fStream->getPosition())
|
|
, fOffset(0)
|
|
, fBufferedSoFar(0)
|
|
, fBufferSize(bufferSize)
|
|
, fBuffer(bufferSize <= kStorageSize ? fStorage
|
|
: reinterpret_cast<char*>(malloc(bufferSize))) {}
|
|
|
|
FrontBufferedStream::~FrontBufferedStream() {
|
|
if (fBuffer != fStorage) {
|
|
free(fBuffer);
|
|
}
|
|
}
|
|
|
|
bool FrontBufferedStream::isAtEnd() const {
|
|
if (fOffset < fBufferedSoFar) {
|
|
// Even if the underlying stream is at the end, this stream has been
|
|
// rewound after buffering, so it is not at the end.
|
|
return false;
|
|
}
|
|
|
|
return fStream->isAtEnd();
|
|
}
|
|
|
|
bool FrontBufferedStream::rewind() {
|
|
// Only allow a rewind if we have not exceeded the buffer.
|
|
if (fOffset <= fBufferSize) {
|
|
fOffset = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
|
|
SkASSERT(fOffset < fBufferedSoFar);
|
|
// Some data has already been copied to fBuffer. Read up to the
|
|
// lesser of the size requested and the remainder of the buffered
|
|
// data.
|
|
const size_t bytesToCopy = std::min(size, fBufferedSoFar - fOffset);
|
|
if (dst != nullptr) {
|
|
memcpy(dst, fBuffer + fOffset, bytesToCopy);
|
|
}
|
|
|
|
// Update fOffset to the new position. It is guaranteed to be
|
|
// within the buffered data.
|
|
fOffset += bytesToCopy;
|
|
SkASSERT(fOffset <= fBufferedSoFar);
|
|
|
|
return bytesToCopy;
|
|
}
|
|
|
|
size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
|
|
SkASSERT(size > 0);
|
|
SkASSERT(fOffset >= fBufferedSoFar);
|
|
SkASSERT(fBuffer);
|
|
// Data needs to be buffered. Buffer up to the lesser of the size requested
|
|
// and the remainder of the max buffer size.
|
|
const size_t bytesToBuffer = std::min(size, fBufferSize - fBufferedSoFar);
|
|
char* buffer = fBuffer + fOffset;
|
|
const size_t buffered = fStream->read(buffer, bytesToBuffer);
|
|
|
|
fBufferedSoFar += buffered;
|
|
fOffset = fBufferedSoFar;
|
|
SkASSERT(fBufferedSoFar <= fBufferSize);
|
|
|
|
// Copy the buffer to the destination buffer and update the amount read.
|
|
if (dst != nullptr) {
|
|
memcpy(dst, buffer, buffered);
|
|
}
|
|
|
|
return buffered;
|
|
}
|
|
|
|
size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
|
|
SkASSERT(size > 0);
|
|
// If we get here, we have buffered all that can be buffered.
|
|
SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
|
|
|
|
const size_t bytesReadDirectly = fStream->read(dst, size);
|
|
fOffset += bytesReadDirectly;
|
|
|
|
// If we have read past the end of the buffer, rewinding is no longer
|
|
// supported, so we can go ahead and free the memory.
|
|
if (bytesReadDirectly > 0 && fBuffer != fStorage) {
|
|
free(fBuffer);
|
|
fBuffer = nullptr;
|
|
}
|
|
|
|
return bytesReadDirectly;
|
|
}
|
|
|
|
size_t FrontBufferedStream::peek(void* dst, size_t size) const {
|
|
// Keep track of the offset so we can return to it.
|
|
const size_t start = fOffset;
|
|
|
|
if (start >= fBufferSize) {
|
|
// This stream is not able to buffer.
|
|
return 0;
|
|
}
|
|
|
|
size = std::min(size, fBufferSize - start);
|
|
FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
|
|
const size_t bytesRead = nonConstThis->read(dst, size);
|
|
nonConstThis->fOffset = start;
|
|
return bytesRead;
|
|
}
|
|
|
|
size_t FrontBufferedStream::read(void* voidDst, size_t size) {
|
|
// Cast voidDst to a char* for easy addition.
|
|
char* dst = reinterpret_cast<char*>(voidDst);
|
|
SkDEBUGCODE(const size_t totalSize = size;)
|
|
const size_t start = fOffset;
|
|
|
|
// First, read any data that was previously buffered.
|
|
if (fOffset < fBufferedSoFar) {
|
|
const size_t bytesCopied = this->readFromBuffer(dst, size);
|
|
|
|
// Update the remaining number of bytes needed to read
|
|
// and the destination buffer.
|
|
size -= bytesCopied;
|
|
SkASSERT(size + (fOffset - start) == totalSize);
|
|
if (dst != nullptr) {
|
|
dst += bytesCopied;
|
|
}
|
|
}
|
|
|
|
// Buffer any more data that should be buffered, and copy it to the
|
|
// destination.
|
|
if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
|
|
const size_t buffered = this->bufferAndWriteTo(dst, size);
|
|
|
|
// Update the remaining number of bytes needed to read
|
|
// and the destination buffer.
|
|
size -= buffered;
|
|
SkASSERT(size + (fOffset - start) == totalSize);
|
|
if (dst != nullptr) {
|
|
dst += buffered;
|
|
}
|
|
}
|
|
|
|
if (size > 0 && !fStream->isAtEnd()) {
|
|
SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
|
|
SkDEBUGCODE(size -= bytesReadDirectly;)
|
|
SkASSERT(size + (fOffset - start) == totalSize);
|
|
}
|
|
|
|
return fOffset - start;
|
|
}
|
|
} // anonymous namespace
|