From b425ce77ad70463186f1a9494547ec5b8b16e3dc Mon Sep 17 00:00:00 2001 From: Andrew Galante Date: Thu, 21 Sep 2006 02:40:20 +0000 Subject: [PATCH] A helper datastructure for network buffers. Defines a fixed-size region of same-size data blocks, and maintains a reference count for each git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18896 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/util/datastore.h | 27 ++++ src/system/kernel/util/datastore.cpp | 184 ++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100755 headers/private/kernel/util/datastore.h create mode 100755 src/system/kernel/util/datastore.cpp diff --git a/headers/private/kernel/util/datastore.h b/headers/private/kernel/util/datastore.h new file mode 100755 index 0000000000..222b585dee --- /dev/null +++ b/headers/private/kernel/util/datastore.h @@ -0,0 +1,27 @@ +/* General data region of fixed size data blocks + * + * Copyright 2006, Haiku, Inc. All Rights Reserved + * Distributed under the terms of the MIT liscence. + * + * Authors: + * Andrew Galante, haiku.galante@gmail.com + */ + +struct datastore { + void *store; // ptr to data region + size_t blocksize; // size of the blocks in the datastore + size_t blockcount; // total number of blocks in the datastore + int32 *refcounts; // array containing refcounts for each block. a refcount of 0 is a free block + int lastfreed; // index of last freed block + int nextfree; // index of next free block +}; + +status_t init_datastore(struct datastore *store, size_t blocksize, int blockcount); // initializes the datastore +status_t uninit_datastore(struct datastore *store); // frees the memory used by the datastore +void *get_datablock(struct datastore *store); // returns a ptr to the first found free block, and increments its refcount +void *get_datablock(struct datastore *store, void *block); // increments the refcount of the specified block +status_t put_datablock(struct datastore *store, void *block); // decrements the refcount of the specified block + +// not thread safe - for amusement purposes only: +int is_empty_datastore(struct datastore *store); // returns nonzero if all refcounts are zero +int is_full_datastore(struct datastore *store); // returns nonzero if all refcounts are greater than zero diff --git a/src/system/kernel/util/datastore.cpp b/src/system/kernel/util/datastore.cpp new file mode 100755 index 0000000000..24dbad3f9e --- /dev/null +++ b/src/system/kernel/util/datastore.cpp @@ -0,0 +1,184 @@ +/* General data region of fixed size data blocks + * + * Copyright 2006, Haiku, Inc. All Rights Reserved + * Distributed under the terms of the MIT liscence. + * + * Authors: + * Andrew Galante : haiku.galante@gmail.com + */ + +#include +#include +#include +#include +#include +#include + +/*! + * Creates a new datastore with \a blockcount datablocks of \a blocksize size + * \return B_NO_MEMORY if there is not enough memory to allocate to the datastore + * \return B_BAD_VALUE if blockcount is negative + */ +status_t +init_datastore(struct datastore *store, size_t blocksize, int blockcount) +{ + if (blockcount < 1) + return B_BAD_VALUE; + + store->blocksize = blocksize; + store->blockcount = blockcount; + + store->store = malloc(blocksize * blockcount); + if (store->store == NULL) + return B_NO_MEMORY; + + store->refcounts = (int32 *)calloc(blockcount, sizeof(uint32)); + if (store->refcounts == NULL) + return B_NO_MEMORY; + + store->lastfreed = -1; + store->nextfree = 0; + + return B_OK; +} + + +/*! + * Frees all memory occupied by the datastore, except for the datastore structure itself + */ +status_t +uninit_datastore(struct datastore *store) +{ + store->blocksize = 0; + store->blockcount = 0; + + free(store->store); + store->store = NULL; + free(store->refcounts); + store->refcounts = NULL; + + store->lastfreed = -1; + store->nextfree = 0; + + return B_OK; +} + + +/*! + * Searches the datastore for a free datablock. If none are free, + * continues search until one is freed. + * \return a pointer to the datablock + * \return NULL if the datastore has not been properly initialized + */ +void * +get_datablock(struct datastore *store) +{ + void *block = NULL; + if (store->store == NULL) + return NULL; + + int index = store->lastfreed; + // first check lastfreed index, as that's most likely to be free + if (index >= 0) { + if (atomic_add(&store->refcounts[index], 1) == 0) { + block = (void *)(index * store->blocksize + (uint8 *)store->store); + store->lastfreed = -1; + return block; + } else + atomic_add(&store->refcounts[index], -1); + } + // otherwise start at the "next free" block and search + index = store->nextfree; + while (1) { + if(atomic_add(&store->refcounts[index], 1) == 0) { + // found a free block + block = (void *)(index * store->blocksize + (uint8 *)store->store); + store->nextfree = (index + 1) % store->blockcount; + break; + } else + atomic_add(&store->refcounts[index], -1); + index = (index + 1) % store->blockcount; + } + + return block; +} + + +/*! + * Increments the refcount of the specified block + * \return a reference to the block on success + * \return NULL if there was an error + */ +void * +get_datablock(struct datastore *store, void *block) +{ + if (block == NULL + || block < store->store + || block > (uint8 *)store->store + (store->blocksize * store->blockcount)) + return NULL; + size_t i = ((uint8 *)block - (uint8 *)store->store) / store->blocksize; + + if (atomic_add(&store->refcounts[i], 1) < 0) { + atomic_add(&store->refcounts[i], -1); + return NULL; + } + return block; +} + + +/*! + * Decrements the refcount of the specified block + * \return B_OK on success + * \return B_BAD_VALUE if \a block is not in the datastore \a store + */ +status_t +put_datablock(struct datastore *store, void *block) +{ + if (block == NULL + || block < store->store + || block > (uint8 *)store->store + (store->blocksize * store->blockcount)) + return B_BAD_VALUE; + size_t i = ((uint8 *)block - (uint8 *)store->store) / store->blocksize; + + if (store->refcounts[i] == 0) + return B_OK; + if (atomic_add(&store->refcounts[i], -1) == 1) + if ((store->nextfree - 1) % store->blockcount == i) + store->nextfree = i; + else + store->lastfreed = i; + return B_OK; +} + + +/*! + * Checks if the datastore is empty. + * Not guaranteed to be thread safe! + * \return nonzero if all blocks are free + * \return zero otherwise + */ +int +is_empty_datastore(struct datastore *store) +{ + size_t i; + for (i = 0; i < store->blockcount; i++) + if (store->refcounts[i] != 0) + return 0; + return 1; +} + + +/*! + * Checks if the datastore is occupied + * Not guaranteed to be thread safe! + * \return nonzero if no blocks are free + * \return zero otherwise + */ +int is_full_datastore(struct datastore *store) +{ + size_t i; + for (i = 0; i < store->blockcount; i++) + if (store->refcounts[i] == 0) + return 0; + return 1; +}