blob: 167ce46ccc4b8eac8b109eb994b423cecfc2c064 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkDeque.h"
Herb Derbyb549cc32017-03-27 13:35:15 -04009#include "SkMalloc.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000011struct SkDeque::Block {
12 Block* fNext;
13 Block* fPrev;
reed@android.com8a1c16f2008-12-17 15:59:43 +000014 char* fBegin; // start of used section in this chunk
15 char* fEnd; // end of used section in this chunk
16 char* fStop; // end of the allocated chunk
reed@google.com4c09d5c2011-02-22 13:16:38 +000017
reed@android.com8a1c16f2008-12-17 15:59:43 +000018 char* start() { return (char*)(this + 1); }
19 const char* start() const { return (const char*)(this + 1); }
reed@google.com4c09d5c2011-02-22 13:16:38 +000020
reed@android.com8a1c16f2008-12-17 15:59:43 +000021 void init(size_t size) {
halcanary96fcdcc2015-08-27 07:41:13 -070022 fNext = fPrev = nullptr;
23 fBegin = fEnd = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000024 fStop = (char*)this + size;
25 }
26};
27
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000028SkDeque::SkDeque(size_t elemSize, int allocCount)
29 : fElemSize(elemSize)
halcanary96fcdcc2015-08-27 07:41:13 -070030 , fInitialStorage(nullptr)
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000031 , fCount(0)
32 , fAllocCount(allocCount) {
33 SkASSERT(allocCount >= 1);
halcanary96fcdcc2015-08-27 07:41:13 -070034 fFrontBlock = fBackBlock = nullptr;
35 fFront = fBack = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000036}
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) {
halcanary96fcdcc2015-08-27 07:41:13 -070043 SkASSERT(storageSize == 0 || storage != nullptr);
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) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000047 fFrontBlock = (Block*)storage;
48 fFrontBlock->init(storageSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 } else {
halcanary96fcdcc2015-08-27 07:41:13 -070050 fFrontBlock = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 }
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000052 fBackBlock = fFrontBlock;
halcanary96fcdcc2015-08-27 07:41:13 -070053 fFront = fBack = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000054}
55
56SkDeque::~SkDeque() {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000057 Block* head = fFrontBlock;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000058 Block* initialHead = (Block*)fInitialStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +000059
60 while (head) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000061 Block* next = head->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 if (head != initialHead) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000063 this->freeBlock(head);
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 }
65 head = next;
66 }
67}
68
reed@android.com8a1c16f2008-12-17 15:59:43 +000069void* SkDeque::push_front() {
70 fCount += 1;
71
halcanary96fcdcc2015-08-27 07:41:13 -070072 if (nullptr == fFrontBlock) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000073 fFrontBlock = this->allocateBlock(fAllocCount);
74 fBackBlock = fFrontBlock; // update our linklist
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 }
reed@google.com4c09d5c2011-02-22 13:16:38 +000076
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000077 Block* first = fFrontBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 char* begin;
79
halcanary96fcdcc2015-08-27 07:41:13 -070080 if (nullptr == first->fBegin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 INIT_CHUNK:
82 first->fEnd = first->fStop;
83 begin = first->fStop - fElemSize;
84 } else {
85 begin = first->fBegin - fElemSize;
86 if (begin < first->start()) { // no more room in this chunk
87 // should we alloc more as we accumulate more elements?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +000088 first = this->allocateBlock(fAllocCount);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000089 first->fNext = fFrontBlock;
90 fFrontBlock->fPrev = first;
91 fFrontBlock = first;
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 goto INIT_CHUNK;
93 }
94 }
95
96 first->fBegin = begin;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +000097
halcanary96fcdcc2015-08-27 07:41:13 -070098 if (nullptr == fFront) {
99 SkASSERT(nullptr == fBack);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000100 fFront = fBack = begin;
101 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700102 SkASSERT(fBack);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000103 fFront = begin;
104 }
105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 return begin;
107}
108
109void* SkDeque::push_back() {
110 fCount += 1;
111
halcanary96fcdcc2015-08-27 07:41:13 -0700112 if (nullptr == fBackBlock) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000113 fBackBlock = this->allocateBlock(fAllocCount);
114 fFrontBlock = fBackBlock; // update our linklist
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000116
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000117 Block* last = fBackBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 char* end;
119
halcanary96fcdcc2015-08-27 07:41:13 -0700120 if (nullptr == last->fBegin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 INIT_CHUNK:
122 last->fBegin = last->start();
123 end = last->fBegin + fElemSize;
124 } else {
125 end = last->fEnd + fElemSize;
126 if (end > last->fStop) { // no more room in this chunk
127 // should we alloc more as we accumulate more elements?
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000128 last = this->allocateBlock(fAllocCount);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000129 last->fPrev = fBackBlock;
130 fBackBlock->fNext = last;
131 fBackBlock = last;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 goto INIT_CHUNK;
133 }
134 }
135
136 last->fEnd = end;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000137 end -= fElemSize;
138
halcanary96fcdcc2015-08-27 07:41:13 -0700139 if (nullptr == fBack) {
140 SkASSERT(nullptr == fFront);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000141 fFront = fBack = end;
142 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700143 SkASSERT(fFront);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000144 fBack = end;
145 }
146
147 return end;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148}
149
150void SkDeque::pop_front() {
151 SkASSERT(fCount > 0);
152 fCount -= 1;
153
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000154 Block* first = fFrontBlock;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
halcanary96fcdcc2015-08-27 07:41:13 -0700156 SkASSERT(first != nullptr);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000157
halcanary96fcdcc2015-08-27 07:41:13 -0700158 if (first->fBegin == nullptr) { // we were marked empty from before
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 first = first->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700160 first->fPrev = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000161 this->freeBlock(fFrontBlock);
162 fFrontBlock = first;
halcanary96fcdcc2015-08-27 07:41:13 -0700163 SkASSERT(first != nullptr); // else we popped too far
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 }
165
166 char* begin = first->fBegin + fElemSize;
167 SkASSERT(begin <= first->fEnd);
168
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000169 if (begin < fFrontBlock->fEnd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 first->fBegin = begin;
bsalomon49f085d2014-09-05 13:34:00 -0700171 SkASSERT(first->fBegin);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000172 fFront = first->fBegin;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700174 first->fBegin = first->fEnd = nullptr; // mark as empty
175 if (nullptr == first->fNext) {
176 fFront = fBack = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000177 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700178 SkASSERT(first->fNext->fBegin);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000179 fFront = first->fNext->fBegin;
180 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 }
182}
183
184void SkDeque::pop_back() {
185 SkASSERT(fCount > 0);
186 fCount -= 1;
187
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000188 Block* last = fBackBlock;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000189
halcanary96fcdcc2015-08-27 07:41:13 -0700190 SkASSERT(last != nullptr);
reed@google.com4c09d5c2011-02-22 13:16:38 +0000191
halcanary96fcdcc2015-08-27 07:41:13 -0700192 if (last->fEnd == nullptr) { // we were marked empty from before
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 last = last->fPrev;
halcanary96fcdcc2015-08-27 07:41:13 -0700194 last->fNext = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000195 this->freeBlock(fBackBlock);
196 fBackBlock = last;
halcanary96fcdcc2015-08-27 07:41:13 -0700197 SkASSERT(last != nullptr); // else we popped too far
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 }
reed@google.com4c09d5c2011-02-22 13:16:38 +0000199
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 char* end = last->fEnd - fElemSize;
201 SkASSERT(end >= last->fBegin);
202
203 if (end > last->fBegin) {
204 last->fEnd = end;
bsalomon49f085d2014-09-05 13:34:00 -0700205 SkASSERT(last->fEnd);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000206 fBack = last->fEnd - fElemSize;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700208 last->fBegin = last->fEnd = nullptr; // mark as empty
209 if (nullptr == last->fPrev) {
210 fFront = fBack = nullptr;
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000211 } else {
bsalomon49f085d2014-09-05 13:34:00 -0700212 SkASSERT(last->fPrev->fEnd);
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000213 fBack = last->fPrev->fEnd - fElemSize;
214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 }
216}
217
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000218int SkDeque::numBlocksAllocated() const {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000219 int numBlocks = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000221 for (const Block* temp = fFrontBlock; temp; temp = temp->fNext) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000222 ++numBlocks;
223 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000224
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000225 return numBlocks;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226}
227
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000228SkDeque::Block* SkDeque::allocateBlock(int allocCount) {
229 Block* newBlock = (Block*)sk_malloc_throw(sizeof(Block) + allocCount * fElemSize);
230 newBlock->init(sizeof(Block) + allocCount * fElemSize);
231 return newBlock;
232}
233
234void SkDeque::freeBlock(Block* block) {
235 sk_free(block);
236}
237
238///////////////////////////////////////////////////////////////////////////////
239
halcanary96fcdcc2015-08-27 07:41:13 -0700240SkDeque::Iter::Iter() : fCurBlock(nullptr), fPos(nullptr), fElemSize(0) {}
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000241
242SkDeque::Iter::Iter(const SkDeque& d, IterStart startLoc) {
243 this->reset(d, startLoc);
244}
245
246// Due to how reset and next work, next actually returns the current element
247// pointed to by fPos and then updates fPos to point to the next one.
248void* SkDeque::Iter::next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 char* pos = fPos;
reed@google.com4c09d5c2011-02-22 13:16:38 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 if (pos) { // if we were valid, try to move to the next setting
252 char* next = pos + fElemSize;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000253 SkASSERT(next <= fCurBlock->fEnd);
254 if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 do {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000256 fCurBlock = fCurBlock->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700257 } while (fCurBlock != nullptr && fCurBlock->fBegin == nullptr);
258 next = fCurBlock ? fCurBlock->fBegin : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 }
260 fPos = next;
261 }
262 return pos;
263}
264
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000265// Like next, prev actually returns the current element pointed to by fPos and
266// then makes fPos point to the previous element.
267void* SkDeque::Iter::prev() {
268 char* pos = fPos;
269
270 if (pos) { // if we were valid, try to move to the prior setting
271 char* prev = pos - fElemSize;
272 SkASSERT(prev >= fCurBlock->fBegin - fElemSize);
273 if (prev < fCurBlock->fBegin) { // exhausted this chunk, move to prior
274 do {
275 fCurBlock = fCurBlock->fPrev;
halcanary96fcdcc2015-08-27 07:41:13 -0700276 } while (fCurBlock != nullptr && fCurBlock->fEnd == nullptr);
277 prev = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000278 }
279 fPos = prev;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000280 }
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000281 return pos;
282}
283
284// reset works by skipping through the spare blocks at the start (or end)
285// of the doubly linked list until a non-empty one is found. The fPos
286// member is then set to the first (or last) element in the block. If
287// there are no elements in the deque both fCurBlock and fPos will come
halcanary96fcdcc2015-08-27 07:41:13 -0700288// out of this routine nullptr.
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000289void SkDeque::Iter::reset(const SkDeque& d, IterStart startLoc) {
290 fElemSize = d.fElemSize;
291
292 if (kFront_IterStart == startLoc) {
293 // initialize the iterator to start at the front
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000294 fCurBlock = d.fFrontBlock;
halcanary96fcdcc2015-08-27 07:41:13 -0700295 while (fCurBlock && nullptr == fCurBlock->fBegin) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000296 fCurBlock = fCurBlock->fNext;
297 }
halcanary96fcdcc2015-08-27 07:41:13 -0700298 fPos = fCurBlock ? fCurBlock->fBegin : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000299 } else {
300 // initialize the iterator to start at the back
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000301 fCurBlock = d.fBackBlock;
halcanary96fcdcc2015-08-27 07:41:13 -0700302 while (fCurBlock && nullptr == fCurBlock->fEnd) {
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000303 fCurBlock = fCurBlock->fPrev;
304 }
halcanary96fcdcc2015-08-27 07:41:13 -0700305 fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr;
robertphillips@google.com0a78b0f2012-07-16 16:58:49 +0000306 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000307}