| |
| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| |
| #include "SkDeque.h" |
| |
| #define INIT_ELEM_COUNT 1 // should we let the caller set this? |
| |
| struct SkDeque::Head { |
| Head* fNext; |
| Head* fPrev; |
| char* fBegin; // start of used section in this chunk |
| char* fEnd; // end of used section in this chunk |
| char* fStop; // end of the allocated chunk |
| |
| char* start() { return (char*)(this + 1); } |
| const char* start() const { return (const char*)(this + 1); } |
| |
| void init(size_t size) { |
| fNext = fPrev = NULL; |
| fBegin = fEnd = NULL; |
| fStop = (char*)this + size; |
| } |
| }; |
| |
| SkDeque::SkDeque(size_t elemSize) |
| : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) { |
| fFront = fBack = NULL; |
| } |
| |
| SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize) |
| : fElemSize(elemSize), fInitialStorage(storage), fCount(0) { |
| SkASSERT(storageSize == 0 || storage != NULL); |
| |
| if (storageSize >= sizeof(Head) + elemSize) { |
| fFront = (Head*)storage; |
| fFront->init(storageSize); |
| } else { |
| fFront = NULL; |
| } |
| fBack = fFront; |
| } |
| |
| SkDeque::~SkDeque() { |
| Head* head = fFront; |
| Head* initialHead = (Head*)fInitialStorage; |
| |
| while (head) { |
| Head* next = head->fNext; |
| if (head != initialHead) { |
| sk_free(head); |
| } |
| head = next; |
| } |
| } |
| |
| const void* SkDeque::front() const { |
| Head* front = fFront; |
| |
| if (NULL == front) { |
| return NULL; |
| } |
| if (NULL == front->fBegin) { |
| front = front->fNext; |
| if (NULL == front) { |
| return NULL; |
| } |
| } |
| SkASSERT(front->fBegin); |
| return front->fBegin; |
| } |
| |
| const void* SkDeque::back() const { |
| Head* back = fBack; |
| |
| if (NULL == back) { |
| return NULL; |
| } |
| if (NULL == back->fEnd) { // marked as deleted |
| back = back->fPrev; |
| if (NULL == back) { |
| return NULL; |
| } |
| } |
| SkASSERT(back->fEnd); |
| return back->fEnd - fElemSize; |
| } |
| |
| void* SkDeque::push_front() { |
| fCount += 1; |
| |
| if (NULL == fFront) { |
| fFront = (Head*)sk_malloc_throw(sizeof(Head) + |
| INIT_ELEM_COUNT * fElemSize); |
| fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); |
| fBack = fFront; // update our linklist |
| } |
| |
| Head* first = fFront; |
| char* begin; |
| |
| if (NULL == first->fBegin) { |
| INIT_CHUNK: |
| first->fEnd = first->fStop; |
| begin = first->fStop - fElemSize; |
| } else { |
| begin = first->fBegin - fElemSize; |
| if (begin < first->start()) { // no more room in this chunk |
| // should we alloc more as we accumulate more elements? |
| size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; |
| |
| first = (Head*)sk_malloc_throw(size); |
| first->init(size); |
| first->fNext = fFront; |
| fFront->fPrev = first; |
| fFront = first; |
| goto INIT_CHUNK; |
| } |
| } |
| |
| first->fBegin = begin; |
| return begin; |
| } |
| |
| void* SkDeque::push_back() { |
| fCount += 1; |
| |
| if (NULL == fBack) { |
| fBack = (Head*)sk_malloc_throw(sizeof(Head) + |
| INIT_ELEM_COUNT * fElemSize); |
| fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); |
| fFront = fBack; // update our linklist |
| } |
| |
| Head* last = fBack; |
| char* end; |
| |
| if (NULL == last->fBegin) { |
| INIT_CHUNK: |
| last->fBegin = last->start(); |
| end = last->fBegin + fElemSize; |
| } else { |
| end = last->fEnd + fElemSize; |
| if (end > last->fStop) { // no more room in this chunk |
| // should we alloc more as we accumulate more elements? |
| size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; |
| |
| last = (Head*)sk_malloc_throw(size); |
| last->init(size); |
| last->fPrev = fBack; |
| fBack->fNext = last; |
| fBack = last; |
| goto INIT_CHUNK; |
| } |
| } |
| |
| last->fEnd = end; |
| return end - fElemSize; |
| } |
| |
| void SkDeque::pop_front() { |
| SkASSERT(fCount > 0); |
| fCount -= 1; |
| |
| Head* first = fFront; |
| |
| SkASSERT(first != NULL); |
| |
| if (first->fBegin == NULL) { // we were marked empty from before |
| first = first->fNext; |
| first->fPrev = NULL; |
| sk_free(fFront); |
| fFront = first; |
| SkASSERT(first != NULL); // else we popped too far |
| } |
| |
| char* begin = first->fBegin + fElemSize; |
| SkASSERT(begin <= first->fEnd); |
| |
| if (begin < fFront->fEnd) { |
| first->fBegin = begin; |
| } else { |
| first->fBegin = first->fEnd = NULL; // mark as empty |
| } |
| } |
| |
| void SkDeque::pop_back() { |
| SkASSERT(fCount > 0); |
| fCount -= 1; |
| |
| Head* last = fBack; |
| |
| SkASSERT(last != NULL); |
| |
| if (last->fEnd == NULL) { // we were marked empty from before |
| last = last->fPrev; |
| last->fNext = NULL; |
| sk_free(fBack); |
| fBack = last; |
| SkASSERT(last != NULL); // else we popped too far |
| } |
| |
| char* end = last->fEnd - fElemSize; |
| SkASSERT(end >= last->fBegin); |
| |
| if (end > last->fBegin) { |
| last->fEnd = end; |
| } else { |
| last->fBegin = last->fEnd = NULL; // mark as empty |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| SkDeque::F2BIter::F2BIter() : fHead(NULL), fPos(NULL), fElemSize(0) {} |
| |
| SkDeque::F2BIter::F2BIter(const SkDeque& d) { |
| this->reset(d); |
| } |
| |
| void* SkDeque::F2BIter::next() { |
| char* pos = fPos; |
| |
| if (pos) { // if we were valid, try to move to the next setting |
| char* next = pos + fElemSize; |
| SkASSERT(next <= fHead->fEnd); |
| if (next == fHead->fEnd) { // exhausted this chunk, move to next |
| do { |
| fHead = fHead->fNext; |
| } while (fHead != NULL && fHead->fBegin == NULL); |
| next = fHead ? fHead->fBegin : NULL; |
| } |
| fPos = next; |
| } |
| return pos; |
| } |
| |
| void SkDeque::F2BIter::reset(const SkDeque& d) { |
| fElemSize = d.fElemSize; |
| fHead = d.fFront; |
| while (fHead != NULL && fHead->fBegin == NULL) { |
| fHead = fHead->fNext; |
| } |
| fPos = fHead ? fHead->fBegin : NULL; |
| } |