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#ifndef SkStreamBuffer_DEFINED
9#define SkStreamBuffer_DEFINED
10
11#include "SkData.h"
12#include "SkStream.h"
13#include "SkTypes.h"
14#include "../private/SkTHash.h"
15
16/**
17 *  Helper class for reading from a stream that may not have all its data
18 *  available yet.
19 *
20 *  Used by GIFImageReader, and currently set up for that use case.
21 *
22 *  Buffers up to 256 * 3 bytes (256 colors, with 3 bytes each) to support GIF.
23 *  FIXME (scroggo): Make this more general purpose?
24 */
25class SkStreamBuffer : SkNoncopyable {
26public:
27    SkStreamBuffer(std::unique_ptr<SkStream>);
28    ~SkStreamBuffer();
29
30    /**
31     *  Return a pointer the buffered data.
32     *
33     *  The number of bytes buffered is the number passed to buffer()
34     *  after the last call to flush().
35     */
36    const char* get() const;
37
38    /**
39     *  Buffer from the stream into our buffer.
40     *
41     *  If this call returns true, get() can be used to access |bytes| bytes
42     *  from the stream. In addition, markPosition() can be called to mark this
43     *  position and enable calling getAtPosition() later to retrieve |bytes|
44     *  bytes.
45     *
46     *  @param bytes Total number of bytes desired.
47     *
48     *  @return Whether all bytes were successfully buffered.
49     */
50    bool buffer(size_t bytes);
51
52    /**
53     *  Flush the buffer.
54     *
55     *  After this call, no bytes are buffered.
56     */
57    void flush() {
58        if (fHasLengthAndPosition) {
59            if (fTrulyBuffered < fBytesBuffered) {
60                fStream->move(fBytesBuffered - fTrulyBuffered);
61            }
62            fTrulyBuffered = 0;
63        }
64        fPosition += fBytesBuffered;
65        fBytesBuffered = 0;
66    }
67
68    /**
69     *  Mark the current position in the stream to return to it later.
70     *
71     *  This is the position of the start of the buffer. After this call, a
72     *  a client can call getDataAtPosition to retrieve all the bytes currently
73     *  buffered.
74     *
75     *  @return size_t Position which can be passed to getDataAtPosition later
76     *      to retrieve the data currently buffered.
77     */
78    size_t markPosition();
79
80    /**
81     *  Retrieve data at position, as previously marked by markPosition().
82     *
83     *  @param position Position to retrieve data, as marked by markPosition().
84     *  @param length   Amount of data required at position.
85     *  @return SkData The data at position.
86     */
87    sk_sp<SkData> getDataAtPosition(size_t position, size_t length);
88
89private:
90    static constexpr size_t kMaxSize = 256 * 3;
91
92    std::unique_ptr<SkStream>   fStream;
93    size_t                      fPosition;
94    char                        fBuffer[kMaxSize];
95    size_t                      fBytesBuffered;
96    // If the stream has a length and position, we can make two optimizations:
97    // - We can skip buffering
98    // - During parsing, we can store the position and size of data that is
99    //   needed later during decoding.
100    const bool                  fHasLengthAndPosition;
101    // When fHasLengthAndPosition is true, we do not need to actually buffer
102    // inside buffer(). We'll buffer inside get(). This keeps track of how many
103    // bytes we've buffered inside get(), for the (non-existent) case of:
104    //  buffer(n)
105    //  get()
106    //  buffer(n + u)
107    //  get()
108    // The second call to get() needs to only truly buffer the part that was
109    // not already buffered.
110    mutable size_t              fTrulyBuffered;
111    // Only used if !fHasLengthAndPosition. In that case, markPosition will
112    // copy into an SkData, stored here.
113    SkTHashMap<size_t, SkData*> fMarkedData;
114};
115#endif // SkStreamBuffer_DEFINED
116
117