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 | |
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 | |
18 | SkStreamBuffer::~SkStreamBuffer() { |
19 | fMarkedData.foreach([](size_t, SkData** data) { (*data)->unref(); }); |
20 | } |
21 | |
22 | 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 | |
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 = std::min(remaining, totalBytesToBuffer); |
49 | } else { |
50 | const size_t = totalBytesToBuffer - fBytesBuffered; |
51 | const size_t bytesBuffered = fStream->read(fBuffer + fBytesBuffered, extraBytes); |
52 | fBytesBuffered += bytesBuffered; |
53 | } |
54 | return fBytesBuffered == totalBytesToBuffer; |
55 | } |
56 | |
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 | |
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 | |