1/*
2 * Copyright 2016 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 "SkStreamBuffer.h"
9
10SkStreamBuffer::SkStreamBuffer(std::unique_ptr<SkStream> stream)
11    : fStream(std::move(stream))
12    , fPosition(0)
13    , fBytesBuffered(0)
14    , fHasLengthAndPosition(fStream->hasLength() && fStream->hasPosition())
15    , fTrulyBuffered(0)
16{}
17
18SkStreamBuffer::~SkStreamBuffer() {
19    fMarkedData.foreach([](size_t, SkData** data) { (*data)->unref(); });
20}
21
22const char* SkStreamBuffer::get() const {
23    SkASSERT(fBytesBuffered >= 1);
24    if (fHasLengthAndPosition && fTrulyBuffered < fBytesBuffered) {
25        const size_t bytesToBuffer = fBytesBuffered - fTrulyBuffered;
26        char* dst = SkTAddOffset<char>(const_cast<char*>(fBuffer), fTrulyBuffered);
27        SkDEBUGCODE(const size_t bytesRead =)
28        // This stream is rewindable, so it should be safe to call the non-const
29        // read()
30        const_cast<SkStream*>(fStream.get())->read(dst, bytesToBuffer);
31        SkASSERT(bytesRead == bytesToBuffer);
32        fTrulyBuffered = fBytesBuffered;
33    }
34    return fBuffer;
35}
36
37bool SkStreamBuffer::buffer(size_t totalBytesToBuffer) {
38    // FIXME (scroggo): What should we do if the client tries to read too much?
39    // Should not be a problem in GIF.
40    SkASSERT(totalBytesToBuffer <= kMaxSize);
41
42    if (totalBytesToBuffer <= fBytesBuffered) {
43        return true;
44    }
45
46    if (fHasLengthAndPosition) {
47        const size_t remaining = fStream->getLength() - fStream->getPosition() + fTrulyBuffered;
48        fBytesBuffered = SkTMin(remaining, totalBytesToBuffer);
49    } else {
50        const size_t extraBytes = totalBytesToBuffer - fBytesBuffered;
51        const size_t bytesBuffered = fStream->read(fBuffer + fBytesBuffered, extraBytes);
52        fBytesBuffered += bytesBuffered;
53    }
54    return fBytesBuffered == totalBytesToBuffer;
55}
56
57size_t SkStreamBuffer::markPosition() {
58    SkASSERT(fBytesBuffered >= 1);
59    if (!fHasLengthAndPosition) {
60        sk_sp<SkData> data(SkData::MakeWithCopy(fBuffer, fBytesBuffered));
61        SkASSERT(nullptr == fMarkedData.find(fPosition));
62        fMarkedData.set(fPosition, data.release());
63    }
64    return fPosition;
65}
66
67sk_sp<SkData> SkStreamBuffer::getDataAtPosition(size_t position, size_t length) {
68    if (!fHasLengthAndPosition) {
69        SkData** data = fMarkedData.find(position);
70        SkASSERT(data);
71        SkASSERT((*data)->size() == length);
72        return sk_ref_sp<SkData>(*data);
73    }
74
75    SkASSERT(position + length <= fStream->getLength());
76
77    const size_t oldPosition = fStream->getPosition();
78    if (!fStream->seek(position)) {
79        return nullptr;
80    }
81
82    sk_sp<SkData> data(SkData::MakeUninitialized(length));
83    void* dst = data->writable_data();
84    const bool success = fStream->read(dst, length) == length;
85    fStream->seek(oldPosition);
86    return success ? data : nullptr;
87}
88