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 12struct 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 19 char* start() { return (char*)(this + 1); } 20 const char* start() const { return (const char*)(this + 1); } 21 22 void init(size_t size) { 23 fNext = fPrev = nullptr; 24 fBegin = fEnd = nullptr; 25 fStop = (char*)this + size; 26 } 27}; 28 29SkDeque::SkDeque(size_t elemSize, int allocCount) 30 : fElemSize(elemSize) 31 , fInitialStorage(nullptr) 32 , fCount(0) 33 , fAllocCount(allocCount) { 34 SkASSERT(allocCount >= 1); 35 fFrontBlock = fBackBlock = nullptr; 36 fFront = fBack = nullptr; 37} 38 39SkDeque::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 != nullptr); 45 SkASSERT(allocCount >= 1); 46 47 if (storageSize >= sizeof(Block) + elemSize) { 48 fFrontBlock = (Block*)storage; 49 fFrontBlock->init(storageSize); 50 } else { 51 fFrontBlock = nullptr; 52 } 53 fBackBlock = fFrontBlock; 54 fFront = fBack = nullptr; 55} 56 57SkDeque::~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 70void* SkDeque::push_front() { 71 fCount += 1; 72 73 if (nullptr == fFrontBlock) { 74 fFrontBlock = this->allocateBlock(fAllocCount); 75 fBackBlock = fFrontBlock; // update our linklist 76 } 77 78 Block* first = fFrontBlock; 79 char* begin; 80 81 if (nullptr == 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 (nullptr == fFront) { 100 SkASSERT(nullptr == fBack); 101 fFront = fBack = begin; 102 } else { 103 SkASSERT(fBack); 104 fFront = begin; 105 } 106 107 return begin; 108} 109 110void* SkDeque::push_back() { 111 fCount += 1; 112 113 if (nullptr == fBackBlock) { 114 fBackBlock = this->allocateBlock(fAllocCount); 115 fFrontBlock = fBackBlock; // update our linklist 116 } 117 118 Block* last = fBackBlock; 119 char* end; 120 121 if (nullptr == 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 (nullptr == fBack) { 141 SkASSERT(nullptr == fFront); 142 fFront = fBack = end; 143 } else { 144 SkASSERT(fFront); 145 fBack = end; 146 } 147 148 return end; 149} 150 151void SkDeque::pop_front() { 152 SkASSERT(fCount > 0); 153 fCount -= 1; 154 155 Block* first = fFrontBlock; 156 157 SkASSERT(first != nullptr); 158 159 if (first->fBegin == nullptr) { // we were marked empty from before 160 first = first->fNext; 161 first->fPrev = nullptr; 162 this->freeBlock(fFrontBlock); 163 fFrontBlock = first; 164 SkASSERT(first != nullptr); // 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 = nullptr; // mark as empty 176 if (nullptr == first->fNext) { 177 fFront = fBack = nullptr; 178 } else { 179 SkASSERT(first->fNext->fBegin); 180 fFront = first->fNext->fBegin; 181 } 182 } 183} 184 185void SkDeque::pop_back() { 186 SkASSERT(fCount > 0); 187 fCount -= 1; 188 189 Block* last = fBackBlock; 190 191 SkASSERT(last != nullptr); 192 193 if (last->fEnd == nullptr) { // we were marked empty from before 194 last = last->fPrev; 195 last->fNext = nullptr; 196 this->freeBlock(fBackBlock); 197 fBackBlock = last; 198 SkASSERT(last != nullptr); // 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 = nullptr; // mark as empty 210 if (nullptr == last->fPrev) { 211 fFront = fBack = nullptr; 212 } else { 213 SkASSERT(last->fPrev->fEnd); 214 fBack = last->fPrev->fEnd - fElemSize; 215 } 216 } 217} 218 219int 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 229SkDeque::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 241SkDeque::Iter::Iter() : fCurBlock(nullptr), fPos(nullptr), fElemSize(0) {} 242 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() { 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 != nullptr && fCurBlock->fBegin == nullptr); 259 next = fCurBlock ? fCurBlock->fBegin : nullptr; 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. 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; 277 } while (fCurBlock != nullptr && fCurBlock->fEnd == nullptr); 278 prev = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr; 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 nullptr. 290void 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 && nullptr == fCurBlock->fBegin) { 297 fCurBlock = fCurBlock->fNext; 298 } 299 fPos = fCurBlock ? fCurBlock->fBegin : nullptr; 300 } else { 301 // initialize the iterator to start at the back 302 fCurBlock = d.fBackBlock; 303 while (fCurBlock && nullptr == fCurBlock->fEnd) { 304 fCurBlock = fCurBlock->fPrev; 305 } 306 fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : nullptr; 307 } 308} 309