C++程序  |  181行  |  4.83 KB

/*
 * 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;
}