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 
SkStreamBuffer(std::unique_ptr<SkStream> stream)10 SkStreamBuffer::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 
~SkStreamBuffer()18 SkStreamBuffer::~SkStreamBuffer() {
19     fMarkedData.foreach([](size_t, SkData** data) { (*data)->unref(); });
20 }
21 
get() const22 const 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 
buffer(size_t totalBytesToBuffer)37 bool 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 
markPosition()57 size_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 
getDataAtPosition(size_t position,size_t length)67 sk_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