blob: 84ca134db2fc5c5d456b963ac1e18f86eb37d848 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
* @ingroup Pool
* @brief Pool primitives (creation, iteration, deletion, etc.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hypool.h"
#define ROUND_TO(granularity, number) ( (((number) % (granularity)) ? ((number) + (granularity) - ((number) % (granularity))) : (number)))
#define MIN_GRANULARITY sizeof(UDATA)
#define MALLOC_ALIGNMENT sizeof(UDATA)
/* read this if a port library call becomes available that gives out the OS page size */
#if 0
#define OS_PAGE_SIZE (EsGetAddressSpacePageSize())
#else
#define OS_PAGE_SIZE 4096
#endif
/**
* @section pool_new
* @fn pool_new(U_32 structSize, U_32 minNumberElements, U_32 elementAlignment, UDATA poolFlags, void *(VMCALL * memAlloc) (void *, U_32), void (VMCALL * memFree) (void *, void *), void *userData)
* Returns a handle to a variable sized pool of structures.
* This handle should be passed into all other pool functions.
*
* @param[in] structSize Size of the pool-elements
* @param[in] minNumberElements If zero, will default to 1
* @param[in] elementAlignment If zero will default to MIN_GRANULARITY
* @param[in] poolFlags
* @param[in] memAlloc Allocate function pointer
* @param[in] memFree Free function pointer
* @param[in] userData
*
* @return pointer to a new pool
*/
HyPool *VMCALL
pool_new (U_32 structSize, U_32 minNumberElements, U_32 elementAlignment,
UDATA poolFlags, void *(VMCALL * memAlloc) (void *, U_32),
void (VMCALL * memFree) (void *, void *), void *userData)
{
U_64 roundedStructSize, tempAllocSize, finalAllocSize;
U_32 finalNumberOfElements, tempNumElems;
UDATA freeLocation, *oldFreeLocation = NULL;
HyPool *newHandle;
if (minNumberElements == 0)
{
minNumberElements = 1;
}
if (elementAlignment == 0)
{
elementAlignment = MIN_GRANULARITY;
}
roundedStructSize = ROUND_TO (elementAlignment, structSize);
tempAllocSize = roundedStructSize * minNumberElements
+ ROUND_TO (elementAlignment, sizeof (HyPool))
+ (elementAlignment - MALLOC_ALIGNMENT);
finalAllocSize = ROUND_TO (OS_PAGE_SIZE, tempAllocSize);
finalNumberOfElements = minNumberElements;
finalNumberOfElements +=
(U_32)((finalAllocSize - tempAllocSize) / roundedStructSize);
/*
* finalAllocSize is a U_64 so that we can detect pool sizes which overflow 32-bits.
*/
if (finalAllocSize > (U_64) 0xFFFFFFFF)
{
return NULL;
}
newHandle = memAlloc (userData, (UDATA) finalAllocSize);
if (newHandle)
{
memset ((void *) newHandle, 0, (size_t) finalAllocSize);
newHandle->elementSize = (UDATA)roundedStructSize;
newHandle->alignment = (U_16) elementAlignment; /* we assume no alignment is > 64k */
newHandle->flags = poolFlags | POOL_SORTED; /* list starts sorted */
newHandle->numberOfElements = finalNumberOfElements;
newHandle->usedElements = 0;
newHandle->firstElementAddress = (void *) ROUND_TO (elementAlignment,
((UDATA) newHandle)
+ sizeof (HyPool));
newHandle->firstFreeSlot = (UDATA *) newHandle->firstElementAddress;
newHandle->activePuddle = newHandle;
newHandle->nextPool = NULL;
newHandle->memAlloc = memAlloc;
newHandle->memFree = memFree;
newHandle->userData = userData;
/* now stuff the free list pointers in */
freeLocation = (UDATA) newHandle->firstElementAddress;
tempNumElems = newHandle->numberOfElements;
while (tempNumElems--)
{
oldFreeLocation = (UDATA *) freeLocation;
freeLocation += (UDATA)roundedStructSize;
*oldFreeLocation = freeLocation;
}
*oldFreeLocation = 0; /* end of list */
}
return newHandle;
}
/**
* @fn pool_kill(HyPool * aPool)
* Deallocates all memory associated with a pool.
*
* @param[in] aPool Pool to be deallocated
*
* @return none
*
*/
void VMCALL
pool_kill (HyPool * aPool)
{
HyPool *tmpPool;
while (aPool)
{
tmpPool = aPool;
aPool = aPool->nextPool;
tmpPool->memFree (tmpPool->userData, tmpPool);
}
}
/**
* @fn pool_newElement (HyPool * aPool)
* Asks for the address of a new pool element.
* If it succeeds, the address returned will have space for
* one element of the correct structure size.
* The contents of the element are undefined.
* If the current pool is full, a new one will be grafted onto the
* end of the pool chain and memory from there will be used.
*
* @param[in] aPool
*
* @return NULL on error
* @return pointer to a new element otherwise
*
*/
void *VMCALL
pool_newElement (HyPool * aPool)
{
void *newElement = NULL;
BOOLEAN searchedActive = FALSE;
HyPool *head = aPool;
aPool = head->activePuddle; /* peek at current active puddle */
while (aPool)
{
if (aPool->firstFreeSlot != NULL)
{
newElement = aPool->firstFreeSlot;
aPool->firstFreeSlot = (UDATA *) * ((UDATA *) newElement);
aPool->usedElements += 1;
/* if we were sorted before getting the new element, the free list is still in order afterwards. If not,
it's unlikely to have become sorted. Thus, the POOL_SORTED bit should only be flipped in
pool_removeElement. */
head->activePuddle = aPool; /* sets to most recently active puddle */
break;
}
else if (!searchedActive)
{ /* allocate a new pool to flow into */
searchedActive = TRUE;
aPool = head; /* no space in active puddle - walk the chain from the head */
}
else
{
if (aPool->nextPool)
{
aPool = aPool->nextPool;
}
else
{
aPool->nextPool =
pool_new (aPool->elementSize, aPool->numberOfElements,
aPool->alignment, aPool->flags, aPool->memAlloc,
aPool->memFree, aPool->userData);
if (!(aPool->nextPool))
return NULL;
aPool = aPool->nextPool; /* let the next iteration do the address calc. */
}
}
}
return newElement;
}
/**
* @fn pool_removeElement (HyPool * aPool, void *anElement)
* Deallocates an element from a pool
*
* It is safe to call pool_removeElement() while looping over
* the pool with @ref pool_startDo / @ref pool_nextDo on the element
* returned by those calls. This is because the free element
* is always inserted at either the head of the free list or
* before the nextFree element in the pool_state.
*
* @param[in] aPool
* @param[in] anElement Pointer to the element to be removed
*
* @return none
*/
void VMCALL
pool_removeElement (HyPool * aPool, void *anElement)
{
UDATA tempHead, tmp2;
BOOLEAN foundPool = FALSE;
BOOLEAN searchedActive = FALSE;
HyPool *oldPool = aPool;
HyPool *head = aPool;
if (!aPool)
return;
/* Peek at active puddle first. If not suitable, walk whole chain. */
aPool = head->activePuddle;
do
{
tmp2 =
(UDATA) aPool->firstElementAddress +
aPool->elementSize * aPool->numberOfElements;
if (((UDATA) anElement < tmp2) && ((UDATA) anElement > (UDATA) aPool))
{
/* If we've already walked the chain, FOUND. If we're peeking at the active puddle
and there's only one element left, walk the chain from the start. */
if (searchedActive
|| (!searchedActive
&& (aPool == head || aPool->usedElements > 1)))
{
foundPool = TRUE;
break; /* this is the right pool */
}
}
if (!searchedActive)
{
searchedActive = TRUE;
aPool = head; /* start from the beginning */
}
else
{
oldPool = aPool;
aPool = aPool->nextPool;
}
}
while (aPool);
if (!foundPool)
return; /* this is an error... we were passed an invalid data pointer. */
head->activePuddle = aPool; /* set this pool to the active pool */
/* we delete from the list differently, depending on if we want to preserve sort order */
if (aPool->flags & POOL_ALWAYS_KEEP_SORTED)
{
/* in this case, we walk the free list, looking for where to insert the newly freed item. */
UDATA *lastElem = (UDATA *) & (aPool->firstFreeSlot);
UDATA *current = (UDATA *) aPool->firstFreeSlot;
while (current && (UDATA) current < (UDATA) anElement)
{
lastElem = current;
current = (UDATA *) * current;
}
*((UDATA *) anElement) = (UDATA) current;
*lastElem = (UDATA) anElement;
aPool->usedElements -= 1;
}
else
{
tempHead = (UDATA) aPool->firstFreeSlot;
aPool->firstFreeSlot = (UDATA *) anElement;
*((UDATA *) anElement) = tempHead;
aPool->usedElements -= 1;
aPool->flags &= ~POOL_SORTED; /* reset sorted flag */
}
if ((oldPool != aPool) && (aPool->usedElements == 0)
&& !(aPool->flags & POOL_NEVER_FREE_PUDDLES))
{
oldPool->nextPool = aPool->nextPool;
head->activePuddle = oldPool;
aPool->memFree (aPool->userData, aPool);
}
}
/**
* @fn pool_do (HyPool * aPool, void (*aFunction) (void *anElement, void *userData), void *userData)
* Calls a user provided function for each element in the list.
*
* @param[in] aPool The pool to "do" things to
* @param[in] aFunction Pointer to function which will "do" things to the elements of aPool
* @param[in] userData Pointer to data to be passed to "do" function, along with each pool-element
*
* @return none
*
* @see pool_startDo, pool_nextDo
*
*/
void VMCALL
pool_do (HyPool * aPool, void (*aFunction) (void *anElement, void *userData),
void *userData)
{
void *anElement;
pool_state aState;
anElement = pool_startDo (aPool, &aState);
while (anElement)
{
(aFunction) (anElement, userData);
anElement = pool_nextDo (&aState);
}
}
/**
* @fn pool_numElements (HyPool * aPool)
* Returns the number of elements in a given pool.
*
* @param[in] aPool
*
* @return 0 on error
* @return the number of elements in the pool otherwise
*
*/
UDATA VMCALL
pool_numElements (HyPool * aPool)
{
UDATA numElements = 0;
while (aPool)
{
numElements += aPool->usedElements;
aPool = aPool->nextPool;
}
return numElements;
}
/**
* @section pool_startDo
* @fn pool_startDo (HyPool * aPool, pool_state * lastHandle)
* Start of an iteration set that will return when code is to be executed.
* This is based strongly on pool_sortFreeAndIterateUsed.
* Pass in a pointer to an empty pool_state and it will be filled in.
*
* @param[in] aPool The pool to "do" things to
* @param[in] lastHandle
*
* @return NULL
* @return pointer to element otherwise
*
* @see pool_do, pool_nextDo
*/
void *VMCALL
pool_startDo (HyPool * aPool, pool_state * lastHandle)
{
UDATA tmpInc;
UDATA *currAddr, **nextFreeAddr;
if (!aPool)
return NULL;
if (!(aPool->flags & POOL_SORTED))
{ /* sort if not pure */
pool_sortFree (aPool);
}
if (0 == aPool->usedElements)
{ /* this pool is empty */
if (aPool->nextPool)
{
return pool_startDo (aPool->nextPool, lastHandle);
}
else
{
return NULL; /* totally empty */
}
}
/* walk, iterating. */
tmpInc = aPool->elementSize;
currAddr = (UDATA *) aPool->firstElementAddress;
nextFreeAddr = (UDATA **) aPool->firstFreeSlot;
while (currAddr == (UDATA *) nextFreeAddr)
{
nextFreeAddr = (UDATA **) * nextFreeAddr;
currAddr = (UDATA *) ((U_8 *) currAddr + tmpInc);
}
lastHandle->currPool = aPool;
lastHandle->nextFree = nextFreeAddr;
lastHandle->lastAddr = (UDATA *) ((U_8 *) currAddr + tmpInc); /* set up for next time in. */
lastHandle->leftToDo = aPool->usedElements - 1;
return (void *) currAddr;
}
/**
* @section pool_nextDo
* @fn pool_nextDo (pool_state * lastHandle)
* Continue an iteration based on state passed in by lastHandle.
* It is safe to stop an iteration midway through.
*
* @param[in] lastHandle pointer for current iteration state
*
* @return NULL nothing more to be done
* @return pointer to next element to be processed otherwise
*
* @see pool_do, pool_startDo
*
*/
void *VMCALL
pool_nextDo (pool_state * lastHandle)
{
UDATA tmpInc;
UDATA *currAddr, **nextFreeAddr;
if (lastHandle->leftToDo == 0)
{ /* no more used elements, stop this pool. */
if (lastHandle->currPool->nextPool) /* check the next one. */
return pool_startDo (lastHandle->currPool->nextPool, lastHandle);
else
return NULL;
}
/* there's at least one more used element */
currAddr = lastHandle->lastAddr;
nextFreeAddr = lastHandle->nextFree;
tmpInc = lastHandle->currPool->elementSize;
/* walk, iterating. */
while (currAddr == (UDATA *) nextFreeAddr)
{
nextFreeAddr = (UDATA **) * nextFreeAddr;
currAddr = (UDATA *) ((U_8 *) currAddr + tmpInc);
}
lastHandle->nextFree = nextFreeAddr;
lastHandle->lastAddr = (UDATA *) ((U_8 *) currAddr + tmpInc); /* set up for next time in. */
lastHandle->leftToDo -= 1;
return (void *) currAddr;
}
/**
* @fn pool_sortFree (HyPool * aPool)
* Sorts the free list of the current pool.
* (ie: does not follow nextPool pointers...)
* This is a O(n) most of the time.
*
* @param[in] aPool The pool to be sorted
*
* @return none
*/
void VMCALL
pool_sortFree (HyPool * aPool)
{
BOOLEAN done = FALSE, conflict;
UDATA *currAddr, *tmpAddr, **nextFreeAddr;
UDATA tmpInc, x, numElements;
U_32 flagVal = 0xDEADBEEE;
tmpInc = aPool->elementSize;
numElements = aPool->numberOfElements;
currAddr = (UDATA *) aPool->firstElementAddress;
while (!done)
{
conflict = FALSE;
for (x = 0; x < numElements; x++)
{
if (*currAddr == flagVal)
{
conflict = TRUE;
flagVal -= 1;
break;
}
currAddr = (UDATA *) ((U_8 *) currAddr + tmpInc);
}
if (!conflict)
{
done = TRUE;
}
}
/* now walk, slamming pointers as I go */
currAddr = aPool->firstFreeSlot;
while (currAddr)
{
tmpAddr = (UDATA *) * currAddr;
*currAddr = flagVal;
currAddr = tmpAddr;
}
/* walk, fixing pointers and iterating. */
currAddr = (UDATA *) aPool->firstElementAddress;
nextFreeAddr = (UDATA **) & (aPool->firstFreeSlot);
for (x = 0; x < numElements; x++)
{
if (*currAddr == flagVal)
{ /* add to free list being built */
*nextFreeAddr = currAddr;
nextFreeAddr = (UDATA **) currAddr;
}
currAddr = (UDATA *) ((U_8 *) currAddr + tmpInc);
}
*nextFreeAddr = NULL; /* end the free list cleanly */
aPool->flags |= POOL_SORTED; /* set sorted flag */
}
/**
* @fn pool_forPortLib (U_32 structSize, HyPortLibrary * portLibrary)
* Shortcut for @ref pool_new, using the default malloc/free from the portLibrary
*
* @param[in] structSize size of pool-element
* @param[in] portLibrary
*
* @return pointer to a Pool
*
* @see pool_new
*/
HyPool *VMCALL
pool_forPortLib (U_32 structSize, HyPortLibrary * portLibrary)
{
return pool_new (structSize, 0, 0, 0, POOL_FOR_PORT (portLibrary));
}
/**
* @fn pool_clear (HyPool * aPool)
* Clear the contents of a pool but not delete it
*
* @note Make no assumptions about the contents of the pool after invoking
* this method (it currently does not zero the memory)
*
* @param[in] aPool The pool to clear
*
* @return none
*/
void VMCALL
pool_clear (HyPool * aPool)
{
while (aPool)
{
UDATA freeLocation, *oldFreeLocation = NULL;
U_32 tempNumElems;
aPool->usedElements = 0;
aPool->firstElementAddress =
(void *) ROUND_TO (aPool->alignment,
((UDATA) aPool) + sizeof (HyPool));
aPool->firstFreeSlot = (UDATA *) aPool->firstElementAddress;
freeLocation = (UDATA) aPool->firstElementAddress;
tempNumElems = aPool->numberOfElements;
while (tempNumElems--)
{
oldFreeLocation = (UDATA *) freeLocation;
freeLocation += aPool->elementSize;
*oldFreeLocation = freeLocation;
}
*oldFreeLocation = 0; /* end of list */
aPool->flags |= POOL_SORTED; /* an empty list is sorted */
aPool = aPool->nextPool;
}
}