1
2 /*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10 #include "SkDeque.h"
11
12 struct SkDeque::Block {
13 Block* fNext;
14 Block* fPrev;
15 char* fBegin; // start of used section in this chunk
16 char* fEnd; // end of used section in this chunk
17 char* fStop; // end of the allocated chunk
18
startSkDeque::Block19 char* start() { return (char*)(this + 1); }
startSkDeque::Block20 const char* start() const { return (const char*)(this + 1); }
21
initSkDeque::Block22 void init(size_t size) {
23 fNext = fPrev = NULL;
24 fBegin = fEnd = NULL;
25 fStop = (char*)this + size;
26 }
27 };
28
SkDeque(size_t elemSize,int allocCount)29 SkDeque::SkDeque(size_t elemSize, int allocCount)
30 : fElemSize(elemSize)
31 , fInitialStorage(NULL)
32 , fCount(0)
33 , fAllocCount(allocCount) {
34 SkASSERT(allocCount >= 1);
35 fFrontBlock = fBackBlock = NULL;
36 fFront = fBack = NULL;
37 }
38
SkDeque(size_t elemSize,void * storage,size_t storageSize,int allocCount)39 SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount)
40 : fElemSize(elemSize)
41 , fInitialStorage(storage)
42 , fCount(0)
43 , fAllocCount(allocCount) {
44 SkASSERT(storageSize == 0 || storage != NULL);
45 SkASSERT(allocCount >= 1);
46
47 if (storageSize >= sizeof(Block) + elemSize) {
48 fFrontBlock = (Block*)storage;
49 fFrontBlock->init(storageSize);
50 } else {
51 fFrontBlock = NULL;
52 }
53 fBackBlock = fFrontBlock;
54 fFront = fBack = NULL;
55 }
56
~SkDeque()57 SkDeque::~SkDeque() {
58 Block* head = fFrontBlock;
59 Block* initialHead = (Block*)fInitialStorage;
60
61 while (head) {
62 Block* next = head->fNext;
63 if (head != initialHead) {
64 this->freeBlock(head);
65 }
66 head = next;
67 }
68 }
69
push_front()70 void* SkDeque::push_front() {
71 fCount += 1;
72
73 if (NULL == fFrontBlock) {
74 fFrontBlock = this->allocateBlock(fAllocCount);
75 fBackBlock = fFrontBlock; // update our linklist
76 }
77
78 Block* first = fFrontBlock;
79 char* begin;
80
81 if (NULL == first->fBegin) {
82 INIT_CHUNK:
83 first->fEnd = first->fStop;
84 begin = first->fStop - fElemSize;
85 } else {
86 begin = first->fBegin - fElemSize;
87 if (begin < first->start()) { // no more room in this chunk
88 // should we alloc more as we accumulate more elements?
89 first = this->allocateBlock(fAllocCount);
90 first->fNext = fFrontBlock;
91 fFrontBlock->fPrev = first;
92 fFrontBlock = first;
93 goto INIT_CHUNK;
94 }
95 }
96
97 first->fBegin = begin;
98
99 if (NULL == fFront) {
100 SkASSERT(NULL == fBack);
101 fFront = fBack = begin;
102 } else {
103 SkASSERT(fBack);
104 fFront = begin;
105 }
106
107 return begin;
108 }
109
push_back()110 void* SkDeque::push_back() {
111 fCount += 1;
112
113 if (NULL == fBackBlock) {
114 fBackBlock = this->allocateBlock(fAllocCount);
115 fFrontBlock = fBackBlock; // update our linklist
116 }
117
118 Block* last = fBackBlock;
119 char* end;
120
121 if (NULL == last->fBegin) {
122 INIT_CHUNK:
123 last->fBegin = last->start();
124 end = last->fBegin + fElemSize;
125 } else {
126 end = last->fEnd + fElemSize;
127 if (end > last->fStop) { // no more room in this chunk
128 // should we alloc more as we accumulate more elements?
129 last = this->allocateBlock(fAllocCount);
130 last->fPrev = fBackBlock;
131 fBackBlock->fNext = last;
132 fBackBlock = last;
133 goto INIT_CHUNK;
134 }
135 }
136
137 last->fEnd = end;
138 end -= fElemSize;
139
140 if (NULL == fBack) {
141 SkASSERT(NULL == fFront);
142 fFront = fBack = end;
143 } else {
144 SkASSERT(fFront);
145 fBack = end;
146 }
147
148 return end;
149 }
150
pop_front()151 void SkDeque::pop_front() {
152 SkASSERT(fCount > 0);
153 fCount -= 1;
154
155 Block* first = fFrontBlock;
156
157 SkASSERT(first != NULL);
158
159 if (first->fBegin == NULL) { // we were marked empty from before
160 first = first->fNext;
161 first->fPrev = NULL;
162 this->freeBlock(fFrontBlock);
163 fFrontBlock = first;
164 SkASSERT(first != NULL); // else we popped too far
165 }
166
167 char* begin = first->fBegin + fElemSize;
168 SkASSERT(begin <= first->fEnd);
169
170 if (begin < fFrontBlock->fEnd) {
171 first->fBegin = begin;
172 SkASSERT(first->fBegin);
173 fFront = first->fBegin;
174 } else {
175 first->fBegin = first->fEnd = NULL; // mark as empty
176 if (NULL == first->fNext) {
177 fFront = fBack = NULL;
178 } else {
179 SkASSERT(first->fNext->fBegin);
180 fFront = first->fNext->fBegin;
181 }
182 }
183 }
184
pop_back()185 void SkDeque::pop_back() {
186 SkASSERT(fCount > 0);
187 fCount -= 1;
188
189 Block* last = fBackBlock;
190
191 SkASSERT(last != NULL);
192
193 if (last->fEnd == NULL) { // we were marked empty from before
194 last = last->fPrev;
195 last->fNext = NULL;
196 this->freeBlock(fBackBlock);
197 fBackBlock = last;
198 SkASSERT(last != NULL); // else we popped too far
199 }
200
201 char* end = last->fEnd - fElemSize;
202 SkASSERT(end >= last->fBegin);
203
204 if (end > last->fBegin) {
205 last->fEnd = end;
206 SkASSERT(last->fEnd);
207 fBack = last->fEnd - fElemSize;
208 } else {
209 last->fBegin = last->fEnd = NULL; // mark as empty
210 if (NULL == last->fPrev) {
211 fFront = fBack = NULL;
212 } else {
213 SkASSERT(last->fPrev->fEnd);
214 fBack = last->fPrev->fEnd - fElemSize;
215 }
216 }
217 }
218
numBlocksAllocated() const219 int SkDeque::numBlocksAllocated() const {
220 int numBlocks = 0;
221
222 for (const Block* temp = fFrontBlock; temp; temp = temp->fNext) {
223 ++numBlocks;
224 }
225
226 return numBlocks;
227 }
228
allocateBlock(int allocCount)229 SkDeque::Block* SkDeque::allocateBlock(int allocCount) {
230 Block* newBlock = (Block*)sk_malloc_throw(sizeof(Block) + allocCount * fElemSize);
231 newBlock->init(sizeof(Block) + allocCount * fElemSize);
232 return newBlock;
233 }
234
freeBlock(Block * block)235 void SkDeque::freeBlock(Block* block) {
236 sk_free(block);
237 }
238
239 ///////////////////////////////////////////////////////////////////////////////
240
Iter()241 SkDeque::Iter::Iter() : fCurBlock(NULL), fPos(NULL), fElemSize(0) {}
242
Iter(const SkDeque & d,IterStart startLoc)243 SkDeque::Iter::Iter(const SkDeque& d, IterStart startLoc) {
244 this->reset(d, startLoc);
245 }
246
247 // Due to how reset and next work, next actually returns the current element
248 // pointed to by fPos and then updates fPos to point to the next one.
next()249 void* SkDeque::Iter::next() {
250 char* pos = fPos;
251
252 if (pos) { // if we were valid, try to move to the next setting
253 char* next = pos + fElemSize;
254 SkASSERT(next <= fCurBlock->fEnd);
255 if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
256 do {
257 fCurBlock = fCurBlock->fNext;
258 } while (fCurBlock != NULL && fCurBlock->fBegin == NULL);
259 next = fCurBlock ? fCurBlock->fBegin : NULL;
260 }
261 fPos = next;
262 }
263 return pos;
264 }
265
266 // Like next, prev actually returns the current element pointed to by fPos and
267 // then makes fPos point to the previous element.
prev()268 void* SkDeque::Iter::prev() {
269 char* pos = fPos;
270
271 if (pos) { // if we were valid, try to move to the prior setting
272 char* prev = pos - fElemSize;
273 SkASSERT(prev >= fCurBlock->fBegin - fElemSize);
274 if (prev < fCurBlock->fBegin) { // exhausted this chunk, move to prior
275 do {
276 fCurBlock = fCurBlock->fPrev;
277 } while (fCurBlock != NULL && fCurBlock->fEnd == NULL);
278 prev = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
279 }
280 fPos = prev;
281 }
282 return pos;
283 }
284
285 // reset works by skipping through the spare blocks at the start (or end)
286 // of the doubly linked list until a non-empty one is found. The fPos
287 // member is then set to the first (or last) element in the block. If
288 // there are no elements in the deque both fCurBlock and fPos will come
289 // out of this routine NULL.
reset(const SkDeque & d,IterStart startLoc)290 void SkDeque::Iter::reset(const SkDeque& d, IterStart startLoc) {
291 fElemSize = d.fElemSize;
292
293 if (kFront_IterStart == startLoc) {
294 // initialize the iterator to start at the front
295 fCurBlock = d.fFrontBlock;
296 while (fCurBlock && NULL == fCurBlock->fBegin) {
297 fCurBlock = fCurBlock->fNext;
298 }
299 fPos = fCurBlock ? fCurBlock->fBegin : NULL;
300 } else {
301 // initialize the iterator to start at the back
302 fCurBlock = d.fBackBlock;
303 while (fCurBlock && NULL == fCurBlock->fEnd) {
304 fCurBlock = fCurBlock->fPrev;
305 }
306 fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
307 }
308 }
309