| /* |
| * 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 <simpleQ.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <heap.h> |
| |
| |
| 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; |
| } |