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 "src/codec/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 = std::min(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(length <= fStream->getLength() &&
76 position <= fStream->getLength() - length);
77
78 const size_t oldPosition = fStream->getPosition();
79 if (!fStream->seek(position)) {
80 return nullptr;
81 }
82
83 sk_sp<SkData> data(SkData::MakeUninitialized(length));
84 void* dst = data->writable_data();
85 const bool success = fStream->read(dst, length) == length;
86 fStream->seek(oldPosition);
87 return success ? data : nullptr;
88}
89