Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkFrontBufferedStream.h"
      9 #include "SkStream.h"
     10 #include "SkTemplates.h"
     11 
     12 class FrontBufferedStream : public SkStreamRewindable {
     13 public:
     14     // Called by Create.
     15     FrontBufferedStream(SkStream*, size_t bufferSize);
     16 
     17     size_t read(void* buffer, size_t size) override;
     18 
     19     size_t peek(void* buffer, size_t size) const override;
     20 
     21     bool isAtEnd() const override;
     22 
     23     bool rewind() override;
     24 
     25     bool hasLength() const override { return fHasLength; }
     26 
     27     size_t getLength() const override { return fLength; }
     28 
     29     SkStreamRewindable* duplicate() const override { return nullptr; }
     30 
     31 private:
     32     std::unique_ptr<SkStream> fStream;
     33     const bool                fHasLength;
     34     const size_t              fLength;
     35     // Current offset into the stream. Always >= 0.
     36     size_t                    fOffset;
     37     // Amount that has been buffered by calls to read. Will always be less than
     38     // fBufferSize.
     39     size_t                    fBufferedSoFar;
     40     // Total size of the buffer.
     41     const size_t              fBufferSize;
     42     // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
     43     // nullptr stream.
     44     SkAutoTMalloc<char>       fBuffer;
     45 
     46     // Read up to size bytes from already buffered data, and copy to
     47     // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less
     48     // than fBufferedSoFar.
     49     size_t readFromBuffer(char* dst, size_t size);
     50 
     51     // Buffer up to size bytes from the stream, and copy to dst if non-
     52     // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
     53     // less than fBufferedSoFar, and size is greater than 0.
     54     size_t bufferAndWriteTo(char* dst, size_t size);
     55 
     56     // Read up to size bytes directly from the stream and into dst if non-
     57     // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered
     58     // data, and size is greater than 0.
     59     size_t readDirectlyFromStream(char* dst, size_t size);
     60 
     61     typedef SkStream INHERITED;
     62 };
     63 
     64 SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
     65     if (nullptr == stream) {
     66         return nullptr;
     67     }
     68     return new FrontBufferedStream(stream, bufferSize);
     69 }
     70 
     71 FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
     72     : fStream(stream)
     73     , fHasLength(stream->hasPosition() && stream->hasLength())
     74     , fLength(stream->getLength() - stream->getPosition())
     75     , fOffset(0)
     76     , fBufferedSoFar(0)
     77     , fBufferSize(bufferSize)
     78     , fBuffer(bufferSize) {}
     79 
     80 bool FrontBufferedStream::isAtEnd() const {
     81     if (fOffset < fBufferedSoFar) {
     82         // Even if the underlying stream is at the end, this stream has been
     83         // rewound after buffering, so it is not at the end.
     84         return false;
     85     }
     86 
     87     return fStream->isAtEnd();
     88 }
     89 
     90 bool FrontBufferedStream::rewind() {
     91     // Only allow a rewind if we have not exceeded the buffer.
     92     if (fOffset <= fBufferSize) {
     93         fOffset = 0;
     94         return true;
     95     }
     96     return false;
     97 }
     98 
     99 size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
    100     SkASSERT(fOffset < fBufferedSoFar);
    101     // Some data has already been copied to fBuffer. Read up to the
    102     // lesser of the size requested and the remainder of the buffered
    103     // data.
    104     const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
    105     if (dst != nullptr) {
    106         memcpy(dst, fBuffer + fOffset, bytesToCopy);
    107     }
    108 
    109     // Update fOffset to the new position. It is guaranteed to be
    110     // within the buffered data.
    111     fOffset += bytesToCopy;
    112     SkASSERT(fOffset <= fBufferedSoFar);
    113 
    114     return bytesToCopy;
    115 }
    116 
    117 size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
    118     SkASSERT(size > 0);
    119     SkASSERT(fOffset >= fBufferedSoFar);
    120     SkASSERT(fBuffer);
    121     // Data needs to be buffered. Buffer up to the lesser of the size requested
    122     // and the remainder of the max buffer size.
    123     const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
    124     char* buffer = fBuffer + fOffset;
    125     const size_t buffered = fStream->read(buffer, bytesToBuffer);
    126 
    127     fBufferedSoFar += buffered;
    128     fOffset = fBufferedSoFar;
    129     SkASSERT(fBufferedSoFar <= fBufferSize);
    130 
    131     // Copy the buffer to the destination buffer and update the amount read.
    132     if (dst != nullptr) {
    133         memcpy(dst, buffer, buffered);
    134     }
    135 
    136     return buffered;
    137 }
    138 
    139 size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
    140     SkASSERT(size > 0);
    141     // If we get here, we have buffered all that can be buffered.
    142     SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
    143 
    144     const size_t bytesReadDirectly = fStream->read(dst, size);
    145     fOffset += bytesReadDirectly;
    146 
    147     // If we have read past the end of the buffer, rewinding is no longer
    148     // supported, so we can go ahead and free the memory.
    149     if (bytesReadDirectly > 0) {
    150         sk_free(fBuffer.release());
    151     }
    152 
    153     return bytesReadDirectly;
    154 }
    155 
    156 size_t FrontBufferedStream::peek(void* dst, size_t size) const {
    157     // Keep track of the offset so we can return to it.
    158     const size_t start = fOffset;
    159 
    160     if (start >= fBufferSize) {
    161         // This stream is not able to buffer.
    162         return 0;
    163     }
    164 
    165     size = SkTMin(size, fBufferSize - start);
    166     FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
    167     const size_t bytesRead = nonConstThis->read(dst, size);
    168     nonConstThis->fOffset = start;
    169     return bytesRead;
    170 }
    171 
    172 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
    173     // Cast voidDst to a char* for easy addition.
    174     char* dst = reinterpret_cast<char*>(voidDst);
    175     SkDEBUGCODE(const size_t totalSize = size;)
    176     const size_t start = fOffset;
    177 
    178     // First, read any data that was previously buffered.
    179     if (fOffset < fBufferedSoFar) {
    180         const size_t bytesCopied = this->readFromBuffer(dst, size);
    181 
    182         // Update the remaining number of bytes needed to read
    183         // and the destination buffer.
    184         size -= bytesCopied;
    185         SkASSERT(size + (fOffset - start) == totalSize);
    186         if (dst != nullptr) {
    187             dst += bytesCopied;
    188         }
    189     }
    190 
    191     // Buffer any more data that should be buffered, and copy it to the
    192     // destination.
    193     if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
    194         const size_t buffered = this->bufferAndWriteTo(dst, size);
    195 
    196         // Update the remaining number of bytes needed to read
    197         // and the destination buffer.
    198         size -= buffered;
    199         SkASSERT(size + (fOffset - start) == totalSize);
    200         if (dst != nullptr) {
    201             dst += buffered;
    202         }
    203     }
    204 
    205     if (size > 0 && !fStream->isAtEnd()) {
    206         SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
    207         SkDEBUGCODE(size -= bytesReadDirectly;)
    208         SkASSERT(size + (fOffset - start) == totalSize);
    209     }
    210 
    211     return fOffset - start;
    212 }
    213