1/*-------------------------------------------------------------------------
2 * drawElements Memory Pool Library
3 * --------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Memory pool array class.
22 *
23 * Features of the pooled arrays:
24 * - single indirection layer (grows exponentially)
25 * - constant # elements per page
26 * - recycles old indirection tables as element pages
27 * - about 10% overhead on large arrays
28 *//*--------------------------------------------------------------------*/
29
30#include "dePoolArray.h"
31#include "deInt32.h"
32
33#include <stdlib.h>
34#include <string.h>
35
36/*--------------------------------------------------------------------*//*!
37 * \internal
38 * \brief Create a new pool array.
39 * \param pool			Pool to allocate memory from.
40 * \param elementSize	Size of the element to be put in array.
41 * \param Pointer to the created array, or null on failure.
42 *//*--------------------------------------------------------------------*/
43dePoolArray* dePoolArray_create (deMemPool* pool, int elementSize)
44{
45	/* Alloc struct. */
46	dePoolArray* arr = DE_POOL_NEW(pool, dePoolArray);
47	if (!arr)
48		return DE_NULL;
49
50	/* Init array. */
51	memset(arr, 0, sizeof(dePoolArray));
52	arr->pool			= pool;
53	arr->elementSize	= elementSize;
54
55	return arr;
56}
57
58/*--------------------------------------------------------------------*//*!
59 * \internal
60 * \brief Ensure that the array can hold at least N elements.
61 * \param arr	Array pointer.
62 * \param size	Number of elements for which to reserve memory.
63 * \param True on success, false on failure.
64 *//*--------------------------------------------------------------------*/
65deBool			dePoolArray_reserve			(dePoolArray* arr, int size)
66{
67	if (size >= arr->capacity)
68	{
69		void*	oldPageTable			= DE_NULL;
70		int		oldPageTableSize		= 0;
71
72		int		newCapacity				= deAlign32(size, 1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2);
73		int		reqPageTableCapacity	= newCapacity >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2;
74
75		if (arr->pageTableCapacity < reqPageTableCapacity)
76		{
77			int		newPageTableCapacity	= deMax32(2*arr->pageTableCapacity, reqPageTableCapacity);
78			void**	newPageTable			= (void**)deMemPool_alloc(arr->pool, (size_t)newPageTableCapacity * sizeof(void*));
79			int		i;
80
81			if (!newPageTable)
82				return DE_FALSE;
83
84			for (i = 0; i < arr->pageTableCapacity; i++)
85				newPageTable[i] = arr->pageTable[i];
86
87			for (; i < newPageTableCapacity; i++)
88				newPageTable[i] = DE_NULL;
89
90			/* Grab information about old page table for recycling purposes. */
91			oldPageTable		= arr->pageTable;
92			oldPageTableSize	= arr->pageTableCapacity * (int)sizeof(void*);
93
94			arr->pageTable			= newPageTable;
95			arr->pageTableCapacity	= newPageTableCapacity;
96		}
97
98		/* Allocate new pages. */
99		{
100			int pageAllocSize = arr->elementSize << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2;
101			int pageTableNdx = arr->capacity >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2;
102
103			/* Allocate new pages from recycled old page table. */
104			while (oldPageTableSize >= pageAllocSize)
105			{
106				void* newPage = oldPageTable;
107				DE_ASSERT(arr->pageTableCapacity > pageTableNdx); /* \todo [petri] is this always true? */
108				DE_ASSERT(!arr->pageTable[pageTableNdx]);
109				arr->pageTable[pageTableNdx++] = newPage;
110
111				oldPageTable = (void*)((deUint8*)oldPageTable + pageAllocSize);
112				oldPageTableSize -= pageAllocSize;
113			}
114
115			/* Allocate the rest of the needed pages from the pool. */
116			for (; pageTableNdx < reqPageTableCapacity; pageTableNdx++)
117			{
118				void* newPage = deMemPool_alloc(arr->pool, (size_t)pageAllocSize);
119				if (!newPage)
120					return DE_FALSE;
121
122				DE_ASSERT(!arr->pageTable[pageTableNdx]);
123				arr->pageTable[pageTableNdx] = newPage;
124			}
125
126			arr->capacity = pageTableNdx << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2;
127			DE_ASSERT(arr->capacity >= newCapacity);
128		}
129	}
130
131	return DE_TRUE;
132}
133
134/*--------------------------------------------------------------------*//*!
135 * \internal
136 * \brief Set the size of the array (also reserves capacity).
137 * \param arr	Array pointer.
138 * \param size	New size of the array (in elements).
139 * \param True on success, false on failure.
140 *//*--------------------------------------------------------------------*/
141deBool			dePoolArray_setSize			(dePoolArray* arr, int size)
142{
143	if (!dePoolArray_reserve(arr, size))
144		return DE_FALSE;
145
146	arr->numElements = size;
147	return DE_TRUE;
148}
149
150DE_DECLARE_POOL_ARRAY(dePoolIntArray, int);
151DE_DECLARE_POOL_ARRAY(dePoolInt16Array, deInt16);
152
153/*--------------------------------------------------------------------*//*!
154 * \internal
155 * \brief Test array functionality.
156 *//*--------------------------------------------------------------------*/
157void dePoolArray_selfTest (void)
158{
159	deMemPool*			pool	= deMemPool_createRoot(DE_NULL, 0);
160	dePoolIntArray*		arr		= dePoolIntArray_create(pool);
161	dePoolInt16Array*	arr16	= dePoolInt16Array_create(pool);
162	int					i;
163
164	/* Test pushBack(). */
165	for (i = 0; i < 5000; i++)
166	{
167		/* Dummy alloc to try to break alignments. */
168		deMemPool_alloc(pool, 1);
169
170		dePoolIntArray_pushBack(arr, i);
171		dePoolInt16Array_pushBack(arr16, (deInt16)i);
172	}
173
174	DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000);
175	DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 5000);
176	for (i = 0; i < 5000; i++)
177	{
178		DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i);
179		DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i);
180	}
181
182	/* Test popBack(). */
183	for (i = 0; i < 1000; i++)
184	{
185		DE_TEST_ASSERT(dePoolIntArray_popBack(arr) == (4999 - i));
186		DE_TEST_ASSERT(dePoolInt16Array_popBack(arr16) == (4999 - i));
187	}
188
189	DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 4000);
190	DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 4000);
191	for (i = 0; i < 4000; i++)
192	{
193		DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i);
194		DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i);
195	}
196
197	/* Test setSize(). */
198	dePoolIntArray_setSize(arr, 1000);
199	dePoolInt16Array_setSize(arr16, 1000);
200	for (i = 1000; i < 5000; i++)
201	{
202		dePoolIntArray_pushBack(arr, i);
203		dePoolInt16Array_pushBack(arr16, (deInt16)i);
204	}
205
206	DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000);
207	DE_TEST_ASSERT(dePoolInt16Array_getNumElements(arr16) == 5000);
208	for (i = 0; i < 5000; i++)
209	{
210		DE_TEST_ASSERT(dePoolIntArray_get(arr, i) == i);
211		DE_TEST_ASSERT(dePoolInt16Array_get(arr16, i) == i);
212	}
213
214	/* Test set() and pushBack() with reserve(). */
215	arr = dePoolIntArray_create(pool);
216	dePoolIntArray_setSize(arr, 1500);
217	dePoolIntArray_reserve(arr, 2000);
218	for (i = 0; i < 1500; i++)
219		dePoolIntArray_set(arr, i, i);
220	for (; i < 5000; i++)
221		dePoolIntArray_pushBack(arr, i);
222
223	DE_TEST_ASSERT(dePoolIntArray_getNumElements(arr) == 5000);
224	for (i = 0; i < 5000; i++)
225	{
226		int val = dePoolIntArray_get(arr, i);
227		DE_TEST_ASSERT(val == i);
228	}
229
230	deMemPool_destroy(pool);
231}
232