/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include struct SimpleQueueEntry { uint32_t nextIdx : 31; uint32_t discardable : 1; uint8_t data[]; }; struct SimpleQueue { SimpleQueueForciblyDiscardCbkF discardCbk; uint32_t head, tail, num, freeHead, entrySz; uint8_t data[]; }; #define SIMPLE_QUEUE_IDX_NONE (SIMPLE_QUEUE_MAX_ELEMENTS + 1) //no bounds checking static inline struct SimpleQueueEntry *simpleQueueGetNth(struct SimpleQueue* sq, uint32_t n) { return (struct SimpleQueueEntry*)(sq->data + n * sq->entrySz); } static inline uint32_t simpleQueueGetIdx(struct SimpleQueue* sq, const struct SimpleQueueEntry *e) { return (((const uint8_t*)e) - sq->data) / sq->entrySz; } struct SimpleQueue* simpleQueueAlloc(uint32_t numEntries, uint32_t entrySz, SimpleQueueForciblyDiscardCbkF forceDiscardCbk) { uint32_t i, sz = sizeof(struct SimpleQueue) + (sizeof(struct SimpleQueueEntry) + entrySz) * numEntries; struct SimpleQueue *sq; if (numEntries > SIMPLE_QUEUE_MAX_ELEMENTS) return NULL; sq = heapAlloc(sz); if (!sq) return NULL; memset(sq, 0, sz); sq->discardCbk = forceDiscardCbk; sq->head = SIMPLE_QUEUE_IDX_NONE; sq->tail = SIMPLE_QUEUE_IDX_NONE; sq->entrySz = entrySz + sizeof(struct SimpleQueueEntry); sq->freeHead = 0; sq->num = numEntries; //init the freelist for (i = 0; i < numEntries - 1; i++) simpleQueueGetNth(sq, i)->nextIdx = i + 1; simpleQueueGetNth(sq, numEntries - 1)->nextIdx = SIMPLE_QUEUE_IDX_NONE; return sq; } void simpleQueueDestroy(struct SimpleQueue* sq) { SimpleQueueForciblyDiscardCbkF discard = sq->discardCbk; struct SimpleQueueEntry *cur; uint32_t i; for (i = sq->head; i != SIMPLE_QUEUE_IDX_NONE; i = cur->nextIdx) { cur = simpleQueueGetNth(sq, i); discard(cur->data, true); } heapFree(sq); } bool simpleQueueDequeue(struct SimpleQueue* sq, void *data) { struct SimpleQueueEntry *e; uint32_t head; if (!sq || sq->head == SIMPLE_QUEUE_IDX_NONE) return false; head = sq->head; e = simpleQueueGetNth(sq, head); sq->head = e->nextIdx; if (sq->tail == head) sq->tail = SIMPLE_QUEUE_IDX_NONE; memcpy(data, e->data, sq->entrySz - sizeof(struct SimpleQueueEntry)); e->nextIdx = sq->freeHead; sq->freeHead = simpleQueueGetIdx(sq, e); return true; } //if this is called, we need to discard at least one entry. we prefer to discard the oldest item static struct SimpleQueueEntry* simpleQueueAllocWithDiscard(struct SimpleQueue* sq) { struct SimpleQueueEntry* cur = NULL; uint32_t idx, prev = SIMPLE_QUEUE_IDX_NONE; for (idx = sq->head; idx != SIMPLE_QUEUE_IDX_NONE; prev=idx, idx = cur->nextIdx) { cur = simpleQueueGetNth(sq, idx); if (!cur->discardable) continue; //try to discard it if (sq->discardCbk(cur->data, false)) { //unlink if (prev == SIMPLE_QUEUE_IDX_NONE) sq->head = cur->nextIdx; else simpleQueueGetNth(sq, prev)->nextIdx = cur->nextIdx; if (sq->tail == idx) sq->tail = prev; return cur; } } return NULL; } bool simpleQueueEnqueue(struct SimpleQueue* sq, const void *data, int length, bool possiblyDiscardable) { struct SimpleQueueEntry *e = NULL; if (length > sq->entrySz - sizeof(struct SimpleQueueEntry)) return false; //first try a simple alloc if (sq->freeHead != SIMPLE_QUEUE_IDX_NONE) { e = simpleQueueGetNth(sq, sq->freeHead); sq->freeHead = e->nextIdx; } //if no luck, it gets complicated if (!e) e = simpleQueueAllocWithDiscard(sq); //and we may have to give up if (!e) return false; //link it in e->nextIdx = SIMPLE_QUEUE_IDX_NONE; if (sq->head == SIMPLE_QUEUE_IDX_NONE) // head = none implies tail = none sq->head = simpleQueueGetIdx(sq, e); else simpleQueueGetNth(sq, sq->tail)->nextIdx = simpleQueueGetIdx(sq, e); sq->tail = simpleQueueGetIdx(sq, e); //fill in the data memcpy(e->data, data, length); e->discardable = possiblyDiscardable ? 1 : 0; return true; }