#ifndef _DEPOOLARRAY_H #define _DEPOOLARRAY_H /*------------------------------------------------------------------------- * drawElements Memory Pool Library * -------------------------------- * * Copyright 2014 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. * *//*! * \file * \brief Memory pool array class. *//*--------------------------------------------------------------------*/ #include "deDefs.h" #include "deMemPool.h" enum { DE_ARRAY_ELEMENTS_PER_PAGE_LOG2 = 4 /*!< \internal 16 */ }; /*--------------------------------------------------------------------*//*! * \internal * \brief Type-independent version of the array template class. *//*--------------------------------------------------------------------*/ typedef struct dePoolArray_s { deMemPool* pool; /*!< Pool from which all memory is allocated from. */ int elementSize; /*!< Size of the element (in bytes). */ int numElements; /*!< Number of elements in the array. */ int capacity; /*!< Number of allocated elements in the array. */ int pageTableCapacity; /*!< Size of the page table. */ void** pageTable; /*!< Pointer to the page table. */ } dePoolArray; DE_BEGIN_EXTERN_C dePoolArray* dePoolArray_create (deMemPool* pool, int elementSize); deBool dePoolArray_reserve (dePoolArray* arr, int capacity); deBool dePoolArray_setSize (dePoolArray* arr, int size); void dePoolArray_selfTest (void); DE_END_EXTERN_C /*--------------------------------------------------------------------*//*! * \brief Declare a template pool array class. * \param TYPENAME Type name of the declared array. * \param VALUETYPE Type of the value contained in the array. * * This macro declares a pool array with all the necessary functions for * operating with it. All allocated memory is taken from the memory pool * given to the constructor. * * The array is implemented by having a set of pages (which store the * elements) and a page table with pointers to each of them. The pages * are allocated individually whenever they are needed, but the page * table grows exponentially. This keeps the memory overhead for large * arrays very small. On the other hand, the relative overhead for very * small arrays is prohibitive (the minimum allocation is 16 elements). * * The functions for operating the array are: * \todo [petri] Figure out how to comment these in Doxygen-style. * * \code * Array* Array_create (deMemPool* pool); * int Array_getNumElements (const Array* array); * deBool Array_reserve (Array* array, int size); * deBool Array_setSize (Array* array, int size); * void Array_reset (Array* array); * Element Array_get (Array* array, int ndx); * deBool Array_set (Array* array, int ndx, Element elem); * deBool Array_pushBack (Array* array, Element elem); * Element Array_popBack (Array* array); * void Array_swap (Array* array, int aNdx, int bNdx); * \endcode *//*--------------------------------------------------------------------*/ #define DE_DECLARE_POOL_ARRAY(TYPENAME, VALUETYPE) \ \ typedef struct TYPENAME##_s \ { \ deMemPool* pool; \ \ int elementSize; \ int numElements; \ int capacity; \ \ int pageTableCapacity; \ DE_PTR_TYPE(VALUETYPE)* pageTable; \ } TYPENAME; /* NOLINT(TYPENAME) */ \ \ DE_INLINE TYPENAME* TYPENAME##_create (deMemPool* pool); \ DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* arr) DE_UNUSED_FUNCTION; \ DE_INLINE deBool TYPENAME##_reserve (DE_PTR_TYPE(TYPENAME) arr, int capacity) DE_UNUSED_FUNCTION; \ DE_INLINE deBool TYPENAME##_setSize (DE_PTR_TYPE(TYPENAME) arr, int size) DE_UNUSED_FUNCTION; \ DE_INLINE void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) arr) DE_UNUSED_FUNCTION; \ DE_INLINE VALUETYPE TYPENAME##_get (const TYPENAME* arr, int ndx) DE_UNUSED_FUNCTION; \ DE_INLINE void TYPENAME##_set (DE_PTR_TYPE(TYPENAME) arr, int ndx, VALUETYPE elem) DE_UNUSED_FUNCTION; \ DE_INLINE deBool TYPENAME##_pushBack (DE_PTR_TYPE(TYPENAME) arr, VALUETYPE elem) DE_UNUSED_FUNCTION; \ DE_INLINE VALUETYPE TYPENAME##_popBack (DE_PTR_TYPE(TYPENAME) arr) DE_UNUSED_FUNCTION; \ DE_INLINE deBool TYPENAME##_copy (DE_PTR_TYPE(TYPENAME) dst, const TYPENAME* src) DE_UNUSED_FUNCTION; \ DE_INLINE void TYPENAME##_swap (DE_PTR_TYPE(TYPENAME) arr, int aNdx, int bNdx) DE_UNUSED_FUNCTION; \ \ DE_INLINE TYPENAME* TYPENAME##_create (deMemPool* pool) \ { \ return (TYPENAME*)dePoolArray_create(pool, sizeof(VALUETYPE)); \ } \ \ DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* arr) \ { \ return arr->numElements; \ } \ \ DE_INLINE deBool TYPENAME##_reserve (DE_PTR_TYPE(TYPENAME) arr, int capacity) \ { \ if (capacity > arr->capacity) \ return dePoolArray_reserve((dePoolArray*)arr, capacity); \ return DE_TRUE; \ } \ \ DE_INLINE deBool TYPENAME##_setSize (DE_PTR_TYPE(TYPENAME) arr, int size) \ { \ if (size > arr->capacity) \ return dePoolArray_setSize((dePoolArray*)arr, size); \ \ arr->numElements = size; \ return DE_TRUE; \ } \ \ DE_INLINE void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) arr) \ { \ arr->numElements = 0; \ } \ \ DE_INLINE VALUETYPE TYPENAME##_get (const TYPENAME* arr, int ndx) \ { \ DE_ASSERT(ndx >= 0 && ndx < arr->numElements); \ { \ int pageNdx = (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2); \ int subNdx = ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1); \ return ((VALUETYPE*)arr->pageTable[pageNdx])[subNdx]; \ } \ } \ \ DE_INLINE void TYPENAME##_set (DE_PTR_TYPE(TYPENAME) arr, int ndx, VALUETYPE elem) \ { \ DE_ASSERT(ndx >= 0 && ndx < arr->numElements); \ { \ int pageNdx = (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2); \ int subNdx = ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1); \ ((VALUETYPE*)arr->pageTable[pageNdx])[subNdx] = elem; \ } \ } \ \ DE_INLINE deBool TYPENAME##_pushBack (DE_PTR_TYPE(TYPENAME) arr, VALUETYPE elem) \ { \ if ((arr->numElements + 1 >= arr->capacity) && !TYPENAME##_reserve(arr, arr->numElements + 1)) \ return DE_FALSE; \ arr->numElements++; \ TYPENAME##_set(arr, arr->numElements - 1, elem); \ return DE_TRUE; \ } \ \ DE_INLINE VALUETYPE TYPENAME##_popBack (DE_PTR_TYPE(TYPENAME) arr) \ { \ int ndx = arr->numElements - 1; \ int pageNdx = (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2); \ int subNdx = ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1); \ DE_ASSERT(arr->numElements > 0); \ arr->numElements--; \ /* \note We access a value which is out-of-bounds, but we know it to be safe. */ \ return ((VALUETYPE*)arr->pageTable[pageNdx])[subNdx]; \ } \ \ DE_INLINE deBool TYPENAME##_copy (DE_PTR_TYPE(TYPENAME) dst, const TYPENAME* src) \ { \ DE_ASSERT(dst && src); \ { \ int numElements = src->numElements; \ int ndx; \ if (!TYPENAME##_setSize(dst, numElements)) \ return DE_FALSE; \ for (ndx = 0; ndx < numElements; ndx++) \ TYPENAME##_set(dst, ndx, TYPENAME##_get(src, ndx)); \ } \ return DE_TRUE; \ } \ \ DE_INLINE void TYPENAME##_swap (DE_PTR_TYPE(TYPENAME) arr, int aNdx, int bNdx) \ { \ VALUETYPE tmp = TYPENAME##_get(arr, aNdx); \ TYPENAME##_set(arr, aNdx, TYPENAME##_get(arr, bNdx)); \ TYPENAME##_set(arr, bNdx, tmp); \ } \ \ struct TYPENAME##Dummy_s { int dummy; } /*--------------------------------------------------------------------*//*! * \brief Declare a sort function for an array. * \param TYPENAME Type name of the declared array. * \param VALUETYPE Type of the value contained in the array. * \param SORTNAME Name for this specific sort. * \param CMPFUNC Comparison function for sorting. * * This macro declares a sort function for an array declared using * DE_DECLARE_POOL_ARRAY macro. * * Sorting algorithm is heap sort since it requires constant amount of * auxiliary space and is in-place sort. Worst-case run-time is O(n log n) * and sort is NOT stable. * * CMPFUNC is used to compare elements in array. It must accept two * parameters and return negative integer if first is smaller than, 0 if * both are equal and positive integer if first is larger than second. * * The functions for sorting array are: * \todo [petri] Figure out how to comment these in Doxygen-style. * * \code * void Array_sortName (Array* array); * void Array_sortNameHeapify (Array* array); * void Array_sortNameShiftDown (Array* array, int start, int end); * \endcode *//*--------------------------------------------------------------------*/ #define DE_DECLARE_POOL_ARRAY_SORT(TYPENAME, VALUETYPE, SORTNAME, CMPFUNC) \ \ DE_INLINE void TYPENAME##_##SORTNAME##ShiftDown (DE_PTR_TYPE(TYPENAME) arr, int startNdx, int endNdx) \ { \ int rootNdx = startNdx; \ \ while (rootNdx * 2 + 1 <= endNdx) \ { \ int childNdx = rootNdx * 2 + 1; \ \ if ((childNdx + 1 <= endNdx) && (CMPFUNC(TYPENAME##_get(arr, childNdx), TYPENAME##_get(arr, childNdx + 1)) < 0)) \ childNdx += 1; \ \ if (CMPFUNC(TYPENAME##_get(arr, rootNdx), TYPENAME##_get(arr, childNdx)) < 0) \ { \ TYPENAME##_swap(arr, rootNdx, childNdx); \ rootNdx = childNdx; \ } \ else \ break; \ } \ } \ \ DE_INLINE void TYPENAME##_##SORTNAME##Heapify (DE_PTR_TYPE(TYPENAME) arr) \ { \ int startNdx = (TYPENAME##_getNumElements(arr) - 2) / 2; \ \ while (startNdx >= 0) \ { \ TYPENAME##_##SORTNAME##ShiftDown(arr, startNdx, TYPENAME##_getNumElements(arr) - 1); \ startNdx -= 1; \ } \ } \ \ DE_INLINE void TYPENAME##_##SORTNAME (DE_PTR_TYPE(TYPENAME) arr) \ { \ int endNdx = TYPENAME##_getNumElements(arr) - 1; \ \ TYPENAME##_##SORTNAME##Heapify(arr); \ \ while (endNdx > 0) \ { \ TYPENAME##_swap(arr, endNdx, 0); \ endNdx -= 1; \ TYPENAME##_##SORTNAME##ShiftDown(arr, 0, endNdx); \ } \ } \ \ struct TYPENAME##SORTNAME##Dummy_s { int dummy; } /* Basic array types. */ DE_DECLARE_POOL_ARRAY(deIntArray, int); DE_DECLARE_POOL_ARRAY(deInt8Array, deInt8); DE_DECLARE_POOL_ARRAY(deUint8Array, deUint8); DE_DECLARE_POOL_ARRAY(deInt16Array, deInt16); DE_DECLARE_POOL_ARRAY(deUint16Array, deUint16); DE_DECLARE_POOL_ARRAY(deInt32Array, deInt32); DE_DECLARE_POOL_ARRAY(deUint32Array, deUint32); #endif /* _DEPOOLARRAY_H */