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     virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
     18 
     19     virtual bool isAtEnd() const SK_OVERRIDE;
     20 
     21     virtual bool rewind() SK_OVERRIDE;
     22 
     23     virtual bool hasPosition() const SK_OVERRIDE { return true; }
     24 
     25     virtual size_t getPosition() const SK_OVERRIDE { return fOffset; }
     26 
     27     virtual bool hasLength() const SK_OVERRIDE { return fHasLength; }
     28 
     29     virtual size_t getLength() const SK_OVERRIDE { return fLength; }
     30 
     31     virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
     32 
     33 private:
     34     SkAutoTUnref<SkStream>  fStream;
     35     const bool              fHasLength;
     36     const size_t            fLength;
     37     // Current offset into the stream. Always >= 0.
     38     size_t                  fOffset;
     39     // Amount that has been buffered by calls to read. Will always be less than
     40     // fBufferSize.
     41     size_t                  fBufferedSoFar;
     42     // Total size of the buffer.
     43     const size_t            fBufferSize;
     44     // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
     45     // NULL stream.
     46     SkAutoTMalloc<char>     fBuffer;
     47 
     48     // Read up to size bytes from already buffered data, and copy to
     49     // dst, if non-NULL. Updates fOffset. Assumes that fOffset is less
     50     // than fBufferedSoFar.
     51     size_t readFromBuffer(char* dst, size_t size);
     52 
     53     // Buffer up to size bytes from the stream, and copy to dst if non-
     54     // NULL. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
     55     // less than fBufferedSoFar, and size is greater than 0.
     56     size_t bufferAndWriteTo(char* dst, size_t size);
     57 
     58     // Read up to size bytes directly from the stream and into dst if non-
     59     // NULL. Updates fOffset. Assumes fOffset is at or beyond the buffered
     60     // data, and size is greater than 0.
     61     size_t readDirectlyFromStream(char* dst, size_t size);
     62 
     63     typedef SkStream INHERITED;
     64 };
     65 
     66 SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
     67     if (NULL == stream) {
     68         return NULL;
     69     }
     70     return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize));
     71 }
     72 
     73 FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
     74     : fStream(SkRef(stream))
     75     , fHasLength(stream->hasPosition() && stream->hasLength())
     76     , fLength(stream->getLength() - stream->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 != NULL) {
    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     // Data needs to be buffered. Buffer up to the lesser of the size requested
    123     // and the remainder of the max buffer size.
    124     const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
    125     char* buffer = fBuffer + fOffset;
    126     const size_t buffered = fStream->read(buffer, bytesToBuffer);
    127 
    128     fBufferedSoFar += buffered;
    129     fOffset = fBufferedSoFar;
    130     SkASSERT(fBufferedSoFar <= fBufferSize);
    131 
    132     // Copy the buffer to the destination buffer and update the amount read.
    133     if (dst != NULL) {
    134         memcpy(dst, buffer, buffered);
    135     }
    136 
    137     return buffered;
    138 }
    139 
    140 size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
    141     SkASSERT(size > 0);
    142     // If we get here, we have buffered all that can be buffered.
    143     SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
    144 
    145     const size_t bytesReadDirectly = fStream->read(dst, size);
    146     fOffset += bytesReadDirectly;
    147 
    148     // If we have read past the end of the buffer, rewinding is no longer
    149     // supported, so we can go ahead and free the memory.
    150     if (bytesReadDirectly > 0) {
    151         fBuffer.reset(0);
    152     }
    153 
    154     return bytesReadDirectly;
    155 }
    156 
    157 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
    158     // Cast voidDst to a char* for easy addition.
    159     char* dst = reinterpret_cast<char*>(voidDst);
    160     SkDEBUGCODE(const size_t totalSize = size;)
    161     const size_t start = fOffset;
    162 
    163     // First, read any data that was previously buffered.
    164     if (fOffset < fBufferedSoFar) {
    165         const size_t bytesCopied = this->readFromBuffer(dst, size);
    166 
    167         // Update the remaining number of bytes needed to read
    168         // and the destination buffer.
    169         size -= bytesCopied;
    170         SkASSERT(size + (fOffset - start) == totalSize);
    171         if (dst != NULL) {
    172             dst += bytesCopied;
    173         }
    174     }
    175 
    176     // Buffer any more data that should be buffered, and copy it to the
    177     // destination.
    178     if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
    179         const size_t buffered = this->bufferAndWriteTo(dst, size);
    180 
    181         // Update the remaining number of bytes needed to read
    182         // and the destination buffer.
    183         size -= buffered;
    184         SkASSERT(size + (fOffset - start) == totalSize);
    185         if (dst != NULL) {
    186             dst += buffered;
    187         }
    188     }
    189 
    190     if (size > 0 && !fStream->isAtEnd()) {
    191         SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
    192         SkDEBUGCODE(size -= bytesReadDirectly;)
    193         SkASSERT(size + (fOffset - start) == totalSize);
    194     }
    195 
    196     return fOffset - start;
    197 }
    198