| /*------------------------------------------------------------------------- |
| * 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. |
| * |
| * Features of the pooled arrays: |
| * - single indirection layer (grows exponentially) |
| * - constant # elements per page |
| * - recycles old indirection tables as element pages |
| * - about 10% overhead on large arrays |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "dePoolArray.h" |
| #include "deInt32.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /*--------------------------------------------------------------------*//*! |
| * \internal |
| * \brief Create a new pool array. |
| * \param pool Pool to allocate memory from. |
| * \param elementSize Size of the element to be put in array. |
| * \param Pointer to the created array, or null on failure. |
| *//*--------------------------------------------------------------------*/ |
| dePoolArray* dePoolArray_create (deMemPool* pool, int elementSize) |
| { |
| /* Alloc struct. */ |
| dePoolArray* arr = DE_POOL_NEW(pool, dePoolArray); |
| if (!arr) |
| return DE_NULL; |
| |
| /* Init array. */ |
| memset(arr, 0, sizeof(dePoolArray)); |
| arr->pool = pool; |
| arr->elementSize = elementSize; |
| |
| return arr; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \internal |
| * \brief Ensure that the array can hold at least N elements. |
| * \param arr Array pointer. |
| * \param size Number of elements for which to reserve memory. |
| * \param True on success, false on failure. |
| *//*--------------------------------------------------------------------*/ |
| deBool dePoolArray_reserve (dePoolArray* arr, int size) |
| { |
| if (size >= arr->capacity) |
| { |
| void* oldPageTable = DE_NULL; |
| int oldPageTableSize = 0; |
| |
| int newCapacity = deAlign32(size, 1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2); |
| int reqPageTableCapacity = newCapacity >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2; |
| |
| if (arr->pageTableCapacity < reqPageTableCapacity) |
| { |
| int newPageTableCapacity = deMax32(2*arr->pageTableCapacity, reqPageTableCapacity); |
| void** newPageTable = (void**)deMemPool_alloc(arr->pool, (size_t)newPageTableCapacity * sizeof(void*)); |
| int i; |
| |
| if (!newPageTable) |
| return DE_FALSE; |
| |
| for (i = 0; i < arr->pageTableCapacity; i++) |
| newPageTable[i] = arr->pageTable[i]; |
| |
| for (; i < newPageTableCapacity; i++) |
| newPageTable[i] = DE_NULL; |
| |
| /* Grab information about old page table for recycling purposes. */ |
| oldPageTable = arr->pageTable; |
| oldPageTableSize = arr->pageTableCapacity * (int)sizeof(void*); |
| |
| arr->pageTable = newPageTable; |
| arr->pageTableCapacity = newPageTableCapacity; |
| } |
| |
| /* Allocate new pages. */ |
| { |
| int pageAllocSize = arr->elementSize << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2; |
| int pageTableNdx = arr->capacity >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2; |
| |
| /* Allocate new pages from recycled old page table. */ |
| while (oldPageTableSize >= pageAllocSize) |
| { |
| void* newPage = oldPageTable; |
| DE_ASSERT(arr->pageTableCapacity > pageTableNdx); /* \todo [petri] is this always true? */ |
| DE_ASSERT(!arr->pageTable[pageTableNdx]); |
| arr->pageTable[pageTableNdx++] = newPage; |
| |
| oldPageTable = (void*)((deUint8*)oldPageTable + pageAllocSize); |
| oldPageTableSize -= pageAllocSize; |
| } |
| |
| /* Allocate the rest of the needed pages from the pool. */ |
| for (; pageTableNdx < reqPageTableCapacity; pageTableNdx++) |
| { |
| void* newPage = deMemPool_alloc(arr->pool, (size_t)pageAllocSize); |
| if (!newPage) |
| return DE_FALSE; |
| |
| DE_ASSERT(!arr->pageTable[pageTableNdx]); |
| arr->pageTable[pageTableNdx] = newPage; |
| } |
| |
| arr->capacity = pageTableNdx << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2; |
| DE_ASSERT(arr->capacity >= newCapacity); |
| } |
| } |
| |
| return DE_TRUE; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \internal |
| * \brief Set the size of the array (also reserves capacity). |
| * \param arr Array pointer. |
| * \param size New size of the array (in elements). |
| * \param True on success, false on failure. |
| *//*--------------------------------------------------------------------*/ |
| deBool dePoolArray_setSize (dePoolArray* arr, int size) |
| { |
| if (!dePoolArray_reserve(arr, size)) |
| return DE_FALSE; |
| |
| arr->numElements = size; |
| return DE_TRUE; |
| } |
| |
| DE_DECLARE_POOL_ARRAY(dePoolIntArray, int); |
| DE_DECLARE_POOL_ARRAY(dePoolInt16Array, deInt16); |
| |
| /*--------------------------------------------------------------------*//*! |
| * \internal |
| * \brief Test array functionality. |
| *//*--------------------------------------------------------------------*/ |
| void dePoolArray_selfTest (void) |
| { |
| deMemPool* pool = deMemPool_createRoot(DE_NULL, 0); |
| dePoolIntArray* arr = dePoolIntArray_create(pool); |
| dePoolInt16Array* arr16 = dePoolInt16Array_create(pool); |
| int i; |
| |
| /* Test pushBack(). */ |
| for (i = 0; i < 5000; i++) |
| { |
| /* Dummy alloc to try to break alignments. */ |
| deMemPool_alloc(pool, 1); |
| |
| dePoolIntArray_pushBack(arr, i); |
| dePoolInt16Array_pushBack(arr16, (deInt16)i); |
| } |
| |
| DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000); |
| DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 5000); |
| for (i = 0; i < 5000; i++) |
| { |
| DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i); |
| DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i); |
| } |
| |
| /* Test popBack(). */ |
| for (i = 0; i < 1000; i++) |
| { |
| DE_TEST_ASSERT(dePoolIntArray_popBack(arr) == (4999 - i)); |
| DE_TEST_ASSERT(dePoolInt16Array_popBack(arr16) == (4999 - i)); |
| } |
| |
| DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 4000); |
| DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 4000); |
| for (i = 0; i < 4000; i++) |
| { |
| DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i); |
| DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i); |
| } |
| |
| /* Test setSize(). */ |
| dePoolIntArray_setSize(arr, 1000); |
| dePoolInt16Array_setSize(arr16, 1000); |
| for (i = 1000; i < 5000; i++) |
| { |
| dePoolIntArray_pushBack(arr, i); |
| dePoolInt16Array_pushBack(arr16, (deInt16)i); |
| } |
| |
| DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000); |
| DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 5000); |
| for (i = 0; i < 5000; i++) |
| { |
| DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i); |
| DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i); |
| } |
| |
| /* Test set() and pushBack() with reserve(). */ |
| arr = dePoolIntArray_create(pool); |
| dePoolIntArray_setSize(arr, 1500); |
| dePoolIntArray_reserve(arr, 2000); |
| for (i = 0; i < 1500; i++) |
| dePoolIntArray_set(arr, i, i); |
| for (; i < 5000; i++) |
| dePoolIntArray_pushBack(arr, i); |
| |
| DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000); |
| for (i = 0; i < 5000; i++) |
| { |
| int val = dePoolIntArray_get(arr, i); |
| DE_TEST_ASSERT(val == i); |
| } |
| |
| deMemPool_destroy(pool); |
| } |