181 lines
4.8 KiB
C
181 lines
4.8 KiB
C
/*
|
|
* 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;
|
|
}
|