blob: 2e469d1faf175ea93b3ac8254e073677f5537444 [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) {
halcanary96fcdcc2015-08-27 07:41:13 -070023 fNext = fPrev = nullptr;
24 fBegin = fEnd = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000025 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)
halcanary96fcdcc2015-08-27 07:41:13 -070031 , fInitialStorage(nullptr)
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000032 , fCount(0)
33 , fAllocCount(allocCount) {
34 SkASSERT(allocCount >= 1);
halcanary96fcdcc2015-08-27 07:41:13 -070035 fFrontBlock = fBackBlock = nullptr;
36 fFront = fBack = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000037}
38
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000039SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount)
40 : fElemSize(elemSize)
41 , fInitialStorage(storage)
42 , fCount(0)
43 , fAllocCount(allocCount) {
halcanary96fcdcc2015-08-27 07:41:13 -070044 SkASSERT(storageSize == 0 || storage != nullptr);
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000045 SkASSERT(allocCount >= 1);
reed@google.com4c09d5c2011-02-22 13:16:38 +000046
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000047 if (storageSize >= sizeof(Block) + elemSize) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000048 fFrontBlock = (Block*)storage;
49 fFrontBlock->init(storageSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 } else {
halcanary96fcdcc2015-08-27 07:41:13 -070051 fFrontBlock = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 }
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000053 fBackBlock = fFrontBlock;
halcanary96fcdcc2015-08-27 07:41:13 -070054 fFront = fBack = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000055}
56
57SkDeque::~SkDeque() {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000058 Block* head = fFrontBlock;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000059 Block* initialHead = (Block*)fInitialStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060
61 while (head) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000062 Block* next = head->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 if (head != initialHead) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000064 this->freeBlock(head);
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 }
66 head = next;
67 }
68}
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070void* SkDeque::push_front() {
71 fCount += 1;
72
halcanary96fcdcc2015-08-27 07:41:13 -070073 if (nullptr == fFrontBlock) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000074 fFrontBlock = this->allocateBlock(fAllocCount);
75 fBackBlock = fFrontBlock; // update our linklist
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 }
reed@google.com4c09d5c2011-02-22 13:16:38 +000077
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000078 Block* first = fFrontBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 char* begin;
80
halcanary96fcdcc2015-08-27 07:41:13 -070081 if (nullptr == first->fBegin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 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?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000089 first = this->allocateBlock(fAllocCount);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000090 first->fNext = fFrontBlock;
91 fFrontBlock->fPrev = first;
92 fFrontBlock = first;
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 goto INIT_CHUNK;
94 }
95 }
96
97 first->fBegin = begin;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000098
halcanary96fcdcc2015-08-27 07:41:13 -070099 if (nullptr == fFront) {
100 SkASSERT(nullptr == fBack);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000101 fFront = fBack = begin;
102 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700103 SkASSERT(fBack);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000104 fFront = begin;
105 }
106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 return begin;
108}
109
110void* SkDeque::push_back() {
111 fCount += 1;
112
halcanary96fcdcc2015-08-27 07:41:13 -0700113 if (nullptr == fBackBlock) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000114 fBackBlock = this->allocateBlock(fAllocCount);
115 fFrontBlock = fBackBlock; // update our linklist
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000117
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000118 Block* last = fBackBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 char* end;
120
halcanary96fcdcc2015-08-27 07:41:13 -0700121 if (nullptr == last->fBegin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 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?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000129 last = this->allocateBlock(fAllocCount);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000130 last->fPrev = fBackBlock;
131 fBackBlock->fNext = last;
132 fBackBlock = last;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 goto INIT_CHUNK;
134 }
135 }
136
137 last->fEnd = end;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000138 end -= fElemSize;
139
halcanary96fcdcc2015-08-27 07:41:13 -0700140 if (nullptr == fBack) {
141 SkASSERT(nullptr == fFront);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000142 fFront = fBack = end;
143 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700144 SkASSERT(fFront);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000145 fBack = end;
146 }
147
148 return end;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149}
150
151void SkDeque::pop_front() {
152 SkASSERT(fCount > 0);
153 fCount -= 1;
154
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000155 Block* first = fFrontBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156
halcanary96fcdcc2015-08-27 07:41:13 -0700157 SkASSERT(first != nullptr);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000158
halcanary96fcdcc2015-08-27 07:41:13 -0700159 if (first->fBegin == nullptr) { // we were marked empty from before
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 first = first->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700161 first->fPrev = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000162 this->freeBlock(fFrontBlock);
163 fFrontBlock = first;
halcanary96fcdcc2015-08-27 07:41:13 -0700164 SkASSERT(first != nullptr); // else we popped too far
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 }
166
167 char* begin = first->fBegin + fElemSize;
168 SkASSERT(begin <= first->fEnd);
169
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000170 if (begin < fFrontBlock->fEnd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 first->fBegin = begin;
bsalomon49f085d2014-09-05 13:34:00 -0700172 SkASSERT(first->fBegin);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000173 fFront = first->fBegin;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700175 first->fBegin = first->fEnd = nullptr; // mark as empty
176 if (nullptr == first->fNext) {
177 fFront = fBack = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000178 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700179 SkASSERT(first->fNext->fBegin);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000180 fFront = first->fNext->fBegin;
181 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 }
183}
184
185void SkDeque::pop_back() {
186 SkASSERT(fCount > 0);
187 fCount -= 1;
188
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000189 Block* last = fBackBlock;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000190
halcanary96fcdcc2015-08-27 07:41:13 -0700191 SkASSERT(last != nullptr);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000192
halcanary96fcdcc2015-08-27 07:41:13 -0700193 if (last->fEnd == nullptr) { // we were marked empty from before
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 last = last->fPrev;
halcanary96fcdcc2015-08-27 07:41:13 -0700195 last->fNext = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000196 this->freeBlock(fBackBlock);
197 fBackBlock = last;
halcanary96fcdcc2015-08-27 07:41:13 -0700198 SkASSERT(last != nullptr); // else we popped too far
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000200
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 char* end = last->fEnd - fElemSize;
202 SkASSERT(end >= last->fBegin);
203
204 if (end > last->fBegin) {
205 last->fEnd = end;
bsalomon49f085d2014-09-05 13:34:00 -0700206 SkASSERT(last->fEnd);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000207 fBack = last->fEnd - fElemSize;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700209 last->fBegin = last->fEnd = nullptr; // mark as empty
210 if (nullptr == last->fPrev) {
211 fFront = fBack = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000212 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700213 SkASSERT(last->fPrev->fEnd);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000214 fBack = last->fPrev->fEnd - fElemSize;
215 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 }
217}
218
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000219int SkDeque::numBlocksAllocated() const {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000220 int numBlocks = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000222 for (const Block* temp = fFrontBlock; temp; temp = temp->fNext) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000223 ++numBlocks;
224 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000225
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000226 return numBlocks;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227}
228
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000229SkDeque::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
235void SkDeque::freeBlock(Block* block) {
236 sk_free(block);
237}
238
239///////////////////////////////////////////////////////////////////////////////
240
halcanary96fcdcc2015-08-27 07:41:13 -0700241SkDeque::Iter::Iter() : fCurBlock(nullptr), fPos(nullptr), fElemSize(0) {}
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000242
243SkDeque::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.
249void* SkDeque::Iter::next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 char* pos = fPos;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 if (pos) { // if we were valid, try to move to the next setting
253 char* next = pos + fElemSize;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000254 SkASSERT(next <= fCurBlock->fEnd);
255 if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 do {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000257 fCurBlock = fCurBlock->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700258 } while (fCurBlock != nullptr && fCurBlock->fBegin == nullptr);
259 next = fCurBlock ? fCurBlock->fBegin : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 }
261 fPos = next;
262 }
263 return pos;
264}
265
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000266// Like next, prev actually returns the current element pointed to by fPos and
267// then makes fPos point to the previous element.
268void* 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;
halcanary96fcdcc2015-08-27 07:41:13 -0700277 } while (fCurBlock != nullptr && fCurBlock->fEnd == nullptr);
278 prev = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000279 }
280 fPos = prev;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000281 }
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000282 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
halcanary96fcdcc2015-08-27 07:41:13 -0700289// out of this routine nullptr.
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000290void 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
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000295 fCurBlock = d.fFrontBlock;
halcanary96fcdcc2015-08-27 07:41:13 -0700296 while (fCurBlock && nullptr == fCurBlock->fBegin) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000297 fCurBlock = fCurBlock->fNext;
298 }
halcanary96fcdcc2015-08-27 07:41:13 -0700299 fPos = fCurBlock ? fCurBlock->fBegin : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000300 } else {
301 // initialize the iterator to start at the back
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000302 fCurBlock = d.fBackBlock;
halcanary96fcdcc2015-08-27 07:41:13 -0700303 while (fCurBlock && nullptr == fCurBlock->fEnd) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000304 fCurBlock = fCurBlock->fPrev;
305 }
halcanary96fcdcc2015-08-27 07:41:13 -0700306 fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000307 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000308}