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#include "SkData.h"
9#include "SkDataTable.h"
10
11static void malloc_freeproc(void* context) {
12    sk_free(context);
13}
14
15// Makes empty table
16SkDataTable::SkDataTable() {
17    fCount = 0;
18    fElemSize = 0;   // 0 signals that we use fDir instead of fElems
19    fU.fDir = NULL;
20    fFreeProc = NULL;
21    fFreeProcContext = NULL;
22}
23
24SkDataTable::SkDataTable(const void* array, size_t elemSize, int count,
25                         FreeProc proc, void* context) {
26    SkASSERT(count > 0);
27
28    fCount = count;
29    fElemSize = elemSize;   // non-zero signals we use fElems instead of fDir
30    fU.fElems = (const char*)array;
31    fFreeProc = proc;
32    fFreeProcContext = context;
33}
34
35SkDataTable::SkDataTable(const Dir* dir, int count, FreeProc proc, void* ctx) {
36    SkASSERT(count > 0);
37
38    fCount = count;
39    fElemSize = 0;  // 0 signals that we use fDir instead of fElems
40    fU.fDir = dir;
41    fFreeProc = proc;
42    fFreeProcContext = ctx;
43}
44
45SkDataTable::~SkDataTable() {
46    if (fFreeProc) {
47        fFreeProc(fFreeProcContext);
48    }
49}
50
51size_t SkDataTable::atSize(int index) const {
52    SkASSERT((unsigned)index < (unsigned)fCount);
53
54    if (fElemSize) {
55        return fElemSize;
56    } else {
57        return fU.fDir[index].fSize;
58    }
59}
60
61const void* SkDataTable::at(int index, size_t* size) const {
62    SkASSERT((unsigned)index < (unsigned)fCount);
63
64    if (fElemSize) {
65        if (size) {
66            *size = fElemSize;
67        }
68        return fU.fElems + index * fElemSize;
69    } else {
70        if (size) {
71            *size = fU.fDir[index].fSize;
72        }
73        return fU.fDir[index].fPtr;
74    }
75}
76
77///////////////////////////////////////////////////////////////////////////////
78
79SkDataTable* SkDataTable::NewEmpty() {
80    static SkDataTable* gEmpty;
81    if (NULL == gEmpty) {
82        gEmpty = SkNEW(SkDataTable);
83    }
84    gEmpty->ref();
85    return gEmpty;
86}
87
88SkDataTable* SkDataTable::NewCopyArrays(const void * const * ptrs,
89                                        const size_t sizes[], int count) {
90    if (count <= 0) {
91        return SkDataTable::NewEmpty();
92    }
93
94    size_t dataSize = 0;
95    for (int i = 0; i < count; ++i) {
96        dataSize += sizes[i];
97    }
98
99    size_t bufferSize = count * sizeof(Dir) + dataSize;
100    void* buffer = sk_malloc_throw(bufferSize);
101
102    Dir* dir = (Dir*)buffer;
103    char* elem = (char*)(dir + count);
104    for (int i = 0; i < count; ++i) {
105        dir[i].fPtr = elem;
106        dir[i].fSize = sizes[i];
107        memcpy(elem, ptrs[i], sizes[i]);
108        elem += sizes[i];
109    }
110
111    return SkNEW_ARGS(SkDataTable, (dir, count, malloc_freeproc, buffer));
112}
113
114SkDataTable* SkDataTable::NewCopyArray(const void* array, size_t elemSize,
115                                       int count) {
116    if (count <= 0) {
117        return SkDataTable::NewEmpty();
118    }
119
120    size_t bufferSize = elemSize * count;
121    void* buffer = sk_malloc_throw(bufferSize);
122    memcpy(buffer, array, bufferSize);
123
124    return SkNEW_ARGS(SkDataTable,
125                      (buffer, elemSize, count, malloc_freeproc, buffer));
126}
127
128SkDataTable* SkDataTable::NewArrayProc(const void* array, size_t elemSize,
129                                       int count, FreeProc proc, void* ctx) {
130    if (count <= 0) {
131        return SkDataTable::NewEmpty();
132    }
133    return SkNEW_ARGS(SkDataTable, (array, elemSize, count, proc, ctx));
134}
135
136///////////////////////////////////////////////////////////////////////////////
137
138static void chunkalloc_freeproc(void* context) {
139    SkDELETE((SkChunkAlloc*)context);
140}
141
142SkDataTableBuilder::SkDataTableBuilder(size_t minChunkSize)
143    : fHeap(NULL)
144    , fMinChunkSize(minChunkSize) {}
145
146SkDataTableBuilder::~SkDataTableBuilder() { this->reset(); }
147
148void SkDataTableBuilder::reset(size_t minChunkSize) {
149    fMinChunkSize = minChunkSize;
150    fDir.reset();
151    if (fHeap) {
152        SkDELETE(fHeap);
153        fHeap = NULL;
154    }
155}
156
157void SkDataTableBuilder::append(const void* src, size_t size) {
158    if (NULL == fHeap) {
159        fHeap = SkNEW_ARGS(SkChunkAlloc, (fMinChunkSize));
160    }
161
162    void* dst = fHeap->alloc(size, SkChunkAlloc::kThrow_AllocFailType);
163    memcpy(dst, src, size);
164
165    SkDataTable::Dir* dir = fDir.append();
166    dir->fPtr = dst;
167    dir->fSize = size;
168}
169
170SkDataTable* SkDataTableBuilder::detachDataTable() {
171    const int count = fDir.count();
172    if (0 == count) {
173        return SkDataTable::NewEmpty();
174    }
175
176    // Copy the dir into the heap;
177    void* dir = fHeap->alloc(count * sizeof(SkDataTable::Dir),
178                             SkChunkAlloc::kThrow_AllocFailType);
179    memcpy(dir, fDir.begin(), count * sizeof(SkDataTable::Dir));
180
181    SkDataTable* table = SkNEW_ARGS(SkDataTable,
182                                    ((SkDataTable::Dir*)dir, count,
183                                     chunkalloc_freeproc, fHeap));
184    // we have to detach our fHeap, since we are giving that to the table
185    fHeap = NULL;
186    fDir.reset();
187    return table;
188}
189