1/*
2 * Copyright 2013 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 SkDataTable_DEFINED
9#define SkDataTable_DEFINED
10
11#include "SkChunkAlloc.h"
12#include "SkData.h"
13#include "SkString.h"
14#include "SkTDArray.h"
15
16/**
17 *  Like SkData, SkDataTable holds an immutable data buffer. The data buffer is
18 *  organized into a table of entries, each with a length, so the entries are
19 *  not required to all be the same size.
20 */
21class SK_API SkDataTable : public SkRefCnt {
22public:
23    SK_DECLARE_INST_COUNT(SkDataTable)
24
25    /**
26     *  Returns true if the table is empty (i.e. has no entries).
27     */
28    bool isEmpty() const { return 0 == fCount; }
29
30    /**
31     *  Return the number of entries in the table. 0 for an empty table
32     */
33    int count() const { return fCount; }
34
35    /**
36     *  Return the size of the index'th entry in the table. The caller must
37     *  ensure that index is valid for this table.
38     */
39    size_t atSize(int index) const;
40
41    /**
42     *  Return a pointer to the data of the index'th entry in the table.
43     *  The caller must ensure that index is valid for this table.
44     *
45     *  @param size If non-null, this returns the byte size of this entry. This
46     *              will be the same value that atSize(index) would return.
47     */
48    const void* at(int index, size_t* size = NULL) const;
49
50    template <typename T>
51    const T* atT(int index, size_t* size = NULL) const {
52        return reinterpret_cast<const T*>(this->at(index, size));
53    }
54
55    /**
56     *  Returns the index'th entry as a c-string, and assumes that the trailing
57     *  null byte had been copied into the table as well.
58     */
59    const char* atStr(int index) const {
60        size_t size;
61        const char* str = this->atT<const char>(index, &size);
62        SkASSERT(strlen(str) + 1 == size);
63        return str;
64    }
65
66    typedef void (*FreeProc)(void* context);
67
68    static SkDataTable* NewEmpty();
69
70    /**
71     *  Return a new DataTable that contains a copy of the data stored in each
72     *  "array".
73     *
74     *  @param ptrs array of points to each element to be copied into the table.
75     *  @param sizes array of byte-lengths for each entry in the corresponding
76     *               ptrs[] array.
77     *  @param count the number of array elements in ptrs[] and sizes[] to copy.
78     */
79    static SkDataTable* NewCopyArrays(const void * const * ptrs,
80                                      const size_t sizes[], int count);
81
82    /**
83     *  Return a new table that contains a copy of the data in array.
84     *
85     *  @param array contiguous array of data for all elements to be copied.
86     *  @param elemSize byte-length for a given element.
87     *  @param count the number of entries to be copied out of array. The number
88     *               of bytes that will be copied is count * elemSize.
89     */
90    static SkDataTable* NewCopyArray(const void* array, size_t elemSize,
91                                     int count);
92
93    static SkDataTable* NewArrayProc(const void* array, size_t elemSize,
94                                     int count, FreeProc proc, void* context);
95
96private:
97    struct Dir {
98        const void* fPtr;
99        uintptr_t   fSize;
100    };
101
102    int         fCount;
103    size_t      fElemSize;
104    union {
105        const Dir*  fDir;
106        const char* fElems;
107    } fU;
108
109    FreeProc    fFreeProc;
110    void*       fFreeProcContext;
111
112    SkDataTable();
113    SkDataTable(const void* array, size_t elemSize, int count,
114                FreeProc, void* context);
115    SkDataTable(const Dir*, int count, FreeProc, void* context);
116    virtual ~SkDataTable();
117
118    friend class SkDataTableBuilder;    // access to Dir
119
120    typedef SkRefCnt INHERITED;
121};
122
123/**
124 *  Helper class that allows for incrementally building up the data needed to
125 *  create a SkDataTable.
126 */
127class SK_API SkDataTableBuilder : SkNoncopyable {
128public:
129    SkDataTableBuilder(size_t minChunkSize);
130    ~SkDataTableBuilder();
131
132    int  count() const { return fDir.count(); }
133    size_t minChunkSize() const { return fMinChunkSize; }
134
135    /**
136     *  Forget any previously appended entries, setting count() back to 0.
137     */
138    void reset(size_t minChunkSize);
139    void reset() {
140        this->reset(fMinChunkSize);
141    }
142
143    /**
144     *  Copy size-bytes from data, and append it to the growing SkDataTable.
145     */
146    void append(const void* data, size_t size);
147
148    /**
149     *  Helper version of append() passes strlen() + 1 for the size,
150     *  so the trailing-zero will be copied as well.
151     */
152    void appendStr(const char str[]) {
153        this->append(str, strlen(str) + 1);
154    }
155
156    /**
157     *  Helper version of append() passes string.size() + 1 for the size,
158     *  so the trailing-zero will be copied as well.
159     */
160    void appendString(const SkString& string) {
161        this->append(string.c_str(), string.size() + 1);
162    }
163
164    /**
165     *  Return an SkDataTable from the accumulated entries that were added by
166     *  calls to append(). This call also clears any accumluated entries from
167     *  this builder, so its count() will be 0 after this call.
168     */
169    SkDataTable* detachDataTable();
170
171private:
172    SkTDArray<SkDataTable::Dir> fDir;
173    SkChunkAlloc*               fHeap;
174    size_t                      fMinChunkSize;
175};
176
177#endif
178