/** * Sparse Array * * Author: David Chisnall * * License: See COPYING.MIT * */ #ifndef _SARRAY_H_INCLUDED_ #define _SARRAY_H_INCLUDED_ #include #include #include "visibility.h" /** * Sparse arrays, used to implement dispatch tables. Current implementation is * quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers. * * Note that deletion from the array is not supported. This allows accesses to * be done without locking; the worst that can happen is that the caller gets * an old value (and if this is important to you then you should be doing your * own locking). For this reason, you should be very careful when deleting a * sparse array that there are no references to it held by other threads. */ typedef struct { /** * Mask value applied to the index when generating an index in this * sub-array. */ uint32_t mask; /** * Number of bits that the masked value should be right shifted by to get * the index in the subarray. If this value is greater than zero, then the * value in the array is another SparseArray*. */ uint32_t shift; /** * The reference count for this. Used for copy-on-write. When making a * copy of a sparse array, we only copy the root node, and increment the * reference count of the remaining nodes. When modifying any leaf node, * we copy if its reference count is greater than one. */ uint32_t refCount; /** * The data stored in this sparse array node. */ void ** data; } SparseArray; /** * Turn an index in the array into an index in the current depth. */ #define MASK_INDEX(index) \ ((index & sarray->mask) >> sarray->shift) #define SARRAY_EMPTY ((void*)0) /** * Look up the specified value in the sparse array. This is used in message * dispatch and so has been put in the header to allow compilers to inline it, * even though this breaks the abstraction. */ static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index) { // This unrolled version of the commented-out segment below only works with // sarrays that use one-byte leaves. It's really ugly, but seems to be faster. // With this version, we get the same performance as the old GNU code, but // with about half the memory usage. uint32_t i = index; switch (sarray->shift) { default: UNREACHABLE("broken sarray"); case 0: return sarray->data[i & 0xff]; case 8: return ((SparseArray*)sarray->data[(i & 0xff00)>>8])->data[(i & 0xff)]; case 16: return ((SparseArray*)((SparseArray*) sarray->data[(i & 0xff0000)>>16])-> data[(i & 0xff00)>>8])->data[(i & 0xff)]; case 24: return ((SparseArray*)((SparseArray*)((SparseArray*) sarray->data[(i & 0xff000000)>>24])-> data[(i & 0xff0000)>>16])-> data[(i & 0xff00)>>8])->data[(i & 0xff)]; } /* while(sarray->shift > 0) { uint32_t i = MASK_INDEX(index); sarray = (SparseArray*) sarray->data[i]; } uint32_t i = index & sarray->mask; return sarray->data[i]; */ } /** * Create a new sparse array. */ SparseArray *SparseArrayNew(); /** * Creates a new sparse array with the specified capacity. The depth indicates * the number of bits to use for the key. Must be a value between 8 and 32 and * should ideally be a multiple of base_shift. */ SparseArray *SparseArrayNewWithDepth(uint32_t depth); /** * Returns a new sparse array created by adding this one as the first child * node in an expanded one. */ SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth); /** * Insert a value at the specified index. */ void SparseArrayInsert(SparseArray * sarray, uint32_t index, void * value); /** * Destroy the sparse array. Note that calling this while other threads are * performing lookups is guaranteed to break. */ void SparseArrayDestroy(SparseArray * sarray); /** * Iterate through the array. Returns the next non-NULL value after index and * sets index to the following value. For example, an array containing values * at 0 and 10 will, if called with index set to 0 first return the value at 0 * and set index to 1. A subsequent call with index set to 1 will return the * value at 10 and set index to 11. */ void * SparseArrayNext(SparseArray * sarray, uint32_t * index); /** * Creates a copy of the sparse array. */ SparseArray *SparseArrayCopy(SparseArray * sarray); /** * Returns the total memory usage of a sparse array. */ int SparseArraySize(SparseArray *sarray); #endif //_SARRAY_H_INCLUDED_