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 Make.
     15     FrontBufferedStream(std::unique_ptr<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 private:
     30     SkStreamRewindable* onDuplicate() const override { return nullptr; }
     31 
     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 std::unique_ptr<SkStreamRewindable> SkFrontBufferedStream::Make(std::unique_ptr<SkStream> stream,
     65                                                                 size_t bufferSize) {
     66     if (!stream) {
     67         return nullptr;
     68     }
     69     return std::unique_ptr<SkStreamRewindable>(new FrontBufferedStream(std::move(stream),
     70                                                                        bufferSize));
     71 }
     72 
     73 FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
     74     : fStream(std::move(stream))
     75     , fHasLength(fStream->hasPosition() && fStream->hasLength())
     76     , fLength(fStream->getLength() - fStream->getPosition())
     77     , fOffset(0)
     78     , fBufferedSoFar(0)
     79     , fBufferSize(bufferSize)
     80     , fBuffer(bufferSize) {}
     81 
     82 bool FrontBufferedStream::isAtEnd() const {
     83     if (fOffset < fBufferedSoFar) {
     84         // Even if the underlying stream is at the end, this stream has been
     85         // rewound after buffering, so it is not at the end.
     86         return false;
     87     }
     88 
     89     return fStream->isAtEnd();
     90 }
     91 
     92 bool FrontBufferedStream::rewind() {
     93     // Only allow a rewind if we have not exceeded the buffer.
     94     if (fOffset <= fBufferSize) {
     95         fOffset = 0;
     96         return true;
     97     }
     98     return false;
     99 }
    100 
    101 size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
    102     SkASSERT(fOffset < fBufferedSoFar);
    103     // Some data has already been copied to fBuffer. Read up to the
    104     // lesser of the size requested and the remainder of the buffered
    105     // data.
    106     const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
    107     if (dst != nullptr) {
    108         memcpy(dst, fBuffer + fOffset, bytesToCopy);
    109     }
    110 
    111     // Update fOffset to the new position. It is guaranteed to be
    112     // within the buffered data.
    113     fOffset += bytesToCopy;
    114     SkASSERT(fOffset <= fBufferedSoFar);
    115 
    116     return bytesToCopy;
    117 }
    118 
    119 size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
    120     SkASSERT(size > 0);
    121     SkASSERT(fOffset >= fBufferedSoFar);
    122     SkASSERT(fBuffer);
    123     // Data needs to be buffered. Buffer up to the lesser of the size requested
    124     // and the remainder of the max buffer size.
    125     const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
    126     char* buffer = fBuffer + fOffset;
    127     const size_t buffered = fStream->read(buffer, bytesToBuffer);
    128 
    129     fBufferedSoFar += buffered;
    130     fOffset = fBufferedSoFar;
    131     SkASSERT(fBufferedSoFar <= fBufferSize);
    132 
    133     // Copy the buffer to the destination buffer and update the amount read.
    134     if (dst != nullptr) {
    135         memcpy(dst, buffer, buffered);
    136     }
    137 
    138     return buffered;
    139 }
    140 
    141 size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
    142     SkASSERT(size > 0);
    143     // If we get here, we have buffered all that can be buffered.
    144     SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
    145 
    146     const size_t bytesReadDirectly = fStream->read(dst, size);
    147     fOffset += bytesReadDirectly;
    148 
    149     // If we have read past the end of the buffer, rewinding is no longer
    150     // supported, so we can go ahead and free the memory.
    151     if (bytesReadDirectly > 0) {
    152         sk_free(fBuffer.release());
    153     }
    154 
    155     return bytesReadDirectly;
    156 }
    157 
    158 size_t FrontBufferedStream::peek(void* dst, size_t size) const {
    159     // Keep track of the offset so we can return to it.
    160     const size_t start = fOffset;
    161 
    162     if (start >= fBufferSize) {
    163         // This stream is not able to buffer.
    164         return 0;
    165     }
    166 
    167     size = SkTMin(size, fBufferSize - start);
    168     FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
    169     const size_t bytesRead = nonConstThis->read(dst, size);
    170     nonConstThis->fOffset = start;
    171     return bytesRead;
    172 }
    173 
    174 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
    175     // Cast voidDst to a char* for easy addition.
    176     char* dst = reinterpret_cast<char*>(voidDst);
    177     SkDEBUGCODE(const size_t totalSize = size;)
    178     const size_t start = fOffset;
    179 
    180     // First, read any data that was previously buffered.
    181     if (fOffset < fBufferedSoFar) {
    182         const size_t bytesCopied = this->readFromBuffer(dst, size);
    183 
    184         // Update the remaining number of bytes needed to read
    185         // and the destination buffer.
    186         size -= bytesCopied;
    187         SkASSERT(size + (fOffset - start) == totalSize);
    188         if (dst != nullptr) {
    189             dst += bytesCopied;
    190         }
    191     }
    192 
    193     // Buffer any more data that should be buffered, and copy it to the
    194     // destination.
    195     if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
    196         const size_t buffered = this->bufferAndWriteTo(dst, size);
    197 
    198         // Update the remaining number of bytes needed to read
    199         // and the destination buffer.
    200         size -= buffered;
    201         SkASSERT(size + (fOffset - start) == totalSize);
    202         if (dst != nullptr) {
    203             dst += buffered;
    204         }
    205     }
    206 
    207     if (size > 0 && !fStream->isAtEnd()) {
    208         SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
    209         SkDEBUGCODE(size -= bytesReadDirectly;)
    210         SkASSERT(size + (fOffset - start) == totalSize);
    211     }
    212 
    213     return fOffset - start;
    214 }
    215