blob: 52c3c2e091454f8f4c57ceb87d628cd22db9dbb2 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDeque.h"
11
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000012struct SkDeque::Block {
13 Block* fNext;
14 Block* fPrev;
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 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
reed@google.com4c09d5c2011-02-22 13:16:38 +000018
reed@android.com8a1c16f2008-12-17 15:59:43 +000019 char* start() { return (char*)(this + 1); }
20 const char* start() const { return (const char*)(this + 1); }
reed@google.com4c09d5c2011-02-22 13:16:38 +000021
reed@android.com8a1c16f2008-12-17 15:59:43 +000022 void init(size_t size) {
23 fNext = fPrev = NULL;
24 fBegin = fEnd = NULL;
25 fStop = (char*)this + size;
26 }
27};
28
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000029SkDeque::SkDeque(size_t elemSize, int allocCount)
30 : fElemSize(elemSize)
31 , fInitialStorage(NULL)
32 , fCount(0)
33 , fAllocCount(allocCount) {
34 SkASSERT(allocCount >= 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 fFront = fBack = NULL;
36}
37
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000038SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount)
39 : fElemSize(elemSize)
40 , fInitialStorage(storage)
41 , fCount(0)
42 , fAllocCount(allocCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 SkASSERT(storageSize == 0 || storage != NULL);
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000044 SkASSERT(allocCount >= 1);
reed@google.com4c09d5c2011-02-22 13:16:38 +000045
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000046 if (storageSize >= sizeof(Block) + elemSize) {
47 fFront = (Block*)storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 fFront->init(storageSize);
49 } else {
50 fFront = NULL;
51 }
52 fBack = fFront;
53}
54
55SkDeque::~SkDeque() {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000056 Block* head = fFront;
57 Block* initialHead = (Block*)fInitialStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +000058
59 while (head) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000060 Block* next = head->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 if (head != initialHead) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000062 this->freeBlock(head);
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 }
64 head = next;
65 }
66}
67
68const void* SkDeque::front() const {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000069 Block* front = fFront;
reed@google.com4c09d5c2011-02-22 13:16:38 +000070
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 if (NULL == front) {
72 return NULL;
73 }
74 if (NULL == front->fBegin) {
75 front = front->fNext;
76 if (NULL == front) {
77 return NULL;
78 }
79 }
80 SkASSERT(front->fBegin);
81 return front->fBegin;
82}
83
84const void* SkDeque::back() const {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000085 Block* back = fBack;
reed@android.com8a1c16f2008-12-17 15:59:43 +000086
87 if (NULL == back) {
88 return NULL;
89 }
90 if (NULL == back->fEnd) { // marked as deleted
91 back = back->fPrev;
92 if (NULL == back) {
93 return NULL;
94 }
95 }
96 SkASSERT(back->fEnd);
97 return back->fEnd - fElemSize;
98}
99
100void* SkDeque::push_front() {
101 fCount += 1;
102
103 if (NULL == fFront) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000104 fFront = this->allocateBlock(fAllocCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 fBack = fFront; // update our linklist
106 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000107
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000108 Block* first = fFront;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 char* begin;
110
111 if (NULL == first->fBegin) {
112 INIT_CHUNK:
113 first->fEnd = first->fStop;
114 begin = first->fStop - fElemSize;
115 } else {
116 begin = first->fBegin - fElemSize;
117 if (begin < first->start()) { // no more room in this chunk
118 // should we alloc more as we accumulate more elements?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000119 first = this->allocateBlock(fAllocCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 first->fNext = fFront;
121 fFront->fPrev = first;
122 fFront = first;
123 goto INIT_CHUNK;
124 }
125 }
126
127 first->fBegin = begin;
128 return begin;
129}
130
131void* SkDeque::push_back() {
132 fCount += 1;
133
134 if (NULL == fBack) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000135 fBack = this->allocateBlock(fAllocCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 fFront = fBack; // update our linklist
137 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000138
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000139 Block* last = fBack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 char* end;
141
142 if (NULL == last->fBegin) {
143 INIT_CHUNK:
144 last->fBegin = last->start();
145 end = last->fBegin + fElemSize;
146 } else {
147 end = last->fEnd + fElemSize;
148 if (end > last->fStop) { // no more room in this chunk
149 // should we alloc more as we accumulate more elements?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000150 last = this->allocateBlock(fAllocCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 last->fPrev = fBack;
152 fBack->fNext = last;
153 fBack = last;
154 goto INIT_CHUNK;
155 }
156 }
157
158 last->fEnd = end;
159 return end - fElemSize;
160}
161
162void SkDeque::pop_front() {
163 SkASSERT(fCount > 0);
164 fCount -= 1;
165
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000166 Block* first = fFront;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
168 SkASSERT(first != NULL);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 if (first->fBegin == NULL) { // we were marked empty from before
171 first = first->fNext;
172 first->fPrev = NULL;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000173 this->freeBlock(fFront);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 fFront = first;
175 SkASSERT(first != NULL); // else we popped too far
176 }
177
178 char* begin = first->fBegin + fElemSize;
179 SkASSERT(begin <= first->fEnd);
180
181 if (begin < fFront->fEnd) {
182 first->fBegin = begin;
183 } else {
184 first->fBegin = first->fEnd = NULL; // mark as empty
185 }
186}
187
188void SkDeque::pop_back() {
189 SkASSERT(fCount > 0);
190 fCount -= 1;
191
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000192 Block* last = fBack;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 SkASSERT(last != NULL);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000195
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 if (last->fEnd == NULL) { // we were marked empty from before
197 last = last->fPrev;
198 last->fNext = NULL;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000199 this->freeBlock(fBack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fBack = last;
201 SkASSERT(last != NULL); // else we popped too far
202 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000203
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 char* end = last->fEnd - fElemSize;
205 SkASSERT(end >= last->fBegin);
206
207 if (end > last->fBegin) {
208 last->fEnd = end;
209 } else {
210 last->fBegin = last->fEnd = NULL; // mark as empty
211 }
212}
213
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000214int SkDeque::numBlocksAllocated() const {
215 int numBlocks = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000217 for (const Block* temp = fFront; temp; temp = temp->fNext) {
218 ++numBlocks;
219 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000220
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000221 return numBlocks;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222}
223
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000224SkDeque::Block* SkDeque::allocateBlock(int allocCount) {
225 Block* newBlock = (Block*)sk_malloc_throw(sizeof(Block) + allocCount * fElemSize);
226 newBlock->init(sizeof(Block) + allocCount * fElemSize);
227 return newBlock;
228}
229
230void SkDeque::freeBlock(Block* block) {
231 sk_free(block);
232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236SkDeque::Iter::Iter() : fCurBlock(NULL), fPos(NULL), fElemSize(0) {}
237
238SkDeque::Iter::Iter(const SkDeque& d, IterStart startLoc) {
239 this->reset(d, startLoc);
240}
241
242// Due to how reset and next work, next actually returns the current element
243// pointed to by fPos and then updates fPos to point to the next one.
244void* SkDeque::Iter::next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 char* pos = fPos;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 if (pos) { // if we were valid, try to move to the next setting
248 char* next = pos + fElemSize;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000249 SkASSERT(next <= fCurBlock->fEnd);
250 if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 do {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000252 fCurBlock = fCurBlock->fNext;
253 } while (fCurBlock != NULL && fCurBlock->fBegin == NULL);
254 next = fCurBlock ? fCurBlock->fBegin : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 }
256 fPos = next;
257 }
258 return pos;
259}
260
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000261// Like next, prev actually returns the current element pointed to by fPos and
262// then makes fPos point to the previous element.
263void* SkDeque::Iter::prev() {
264 char* pos = fPos;
265
266 if (pos) { // if we were valid, try to move to the prior setting
267 char* prev = pos - fElemSize;
268 SkASSERT(prev >= fCurBlock->fBegin - fElemSize);
269 if (prev < fCurBlock->fBegin) { // exhausted this chunk, move to prior
270 do {
271 fCurBlock = fCurBlock->fPrev;
272 } while (fCurBlock != NULL && fCurBlock->fEnd == NULL);
273 prev = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
274 }
275 fPos = prev;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000276 }
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000277 return pos;
278}
279
280// reset works by skipping through the spare blocks at the start (or end)
281// of the doubly linked list until a non-empty one is found. The fPos
282// member is then set to the first (or last) element in the block. If
283// there are no elements in the deque both fCurBlock and fPos will come
284// out of this routine NULL.
285void SkDeque::Iter::reset(const SkDeque& d, IterStart startLoc) {
286 fElemSize = d.fElemSize;
287
288 if (kFront_IterStart == startLoc) {
289 // initialize the iterator to start at the front
290 fCurBlock = d.fFront;
291 while (NULL != fCurBlock && NULL == fCurBlock->fBegin) {
292 fCurBlock = fCurBlock->fNext;
293 }
294 fPos = fCurBlock ? fCurBlock->fBegin : NULL;
295 } else {
296 // initialize the iterator to start at the back
297 fCurBlock = d.fBack;
298 while (NULL != fCurBlock && NULL == fCurBlock->fEnd) {
299 fCurBlock = fCurBlock->fPrev;
300 }
301 fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
302 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000303}