text: /*
   * 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 "SkFrontBufferedStream.h"
  #include "SkStream.h"
  #include "SkTemplates.h"
  
  class FrontBufferedStream : public SkStreamRewindable {
  public:
      // Called by Create.
      FrontBufferedStream(SkStream*, size_t bufferSize);
  
      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; }
  
      SkStreamRewindable* duplicate() const override { return nullptr; }
  
  private:
      SkAutoTDelete<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;
      // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
      // nullptr stream.
      SkAutoTMalloc<char>     fBuffer;
  
      // 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);
  
      typedef SkStream INHERITED;
  };
  
  SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
      if (nullptr == stream) {
          return nullptr;
      }
      return new FrontBufferedStream(stream, bufferSize);
  }
  
  FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
      : fStream(stream)
      , fHasLength(stream->hasPosition() && stream->hasLength())
      , fLength(stream->getLength() - stream->getPosition())
      , fOffset(0)
      , fBufferedSoFar(0)
      , fBufferSize(bufferSize)
      , fBuffer(bufferSize) {}
  
  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 = SkTMin(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 = SkTMin(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) {
          sk_free(fBuffer.release());
      }
  
      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 = SkTMin(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;
  }
  