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