m3g/m3gcore11/src/m3g_array.c
author jakl.martin@cell-telecom.com
Mon, 06 Dec 2010 18:07:30 +0100
branchNewGraphicsArchitecture
changeset 218 99b3451c560e
parent 0 5d03bc08d59c
permissions -rw-r--r--
Fix for Bug 3890

/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Dynamic pointer array implementation
*
*/


/*!
 * \internal
 * \file
 * \brief Dynamic pointer array implementation
 *
 */

#ifndef M3G_CORE_INCLUDE
#   error included by m3g_core.c; do not compile separately.
#endif

#include "m3g_array.h"


/* Define a minimum (default) capacity and a maximum amount of growth
 * for the array, to try and keep memory usage reasonable */

#define MIN_CAPACITY 8  /* 32 bytes */
#define MAX_GROWTH 1024 /* 4 KB */

M3G_CT_ASSERT(MIN_CAPACITY < MAX_GROWTH);

/*----------------------------------------------------------------------
 * Private functions
 *--------------------------------------------------------------------*/

/*!
 * \internal
 * \brief Resizes an array to the given size
 */
static M3Gbool m3gReallocateArray(PointerArray *array,
                                  M3Gsizei newCapacity,
                                  Interface *m3g)
{
    void **newItems;
    M3G_VALIDATE_INTERFACE(m3g);
    
    /* Allocate the new data block */
    
    newItems = m3gAlloc(m3g, (M3Gsize) newCapacity * sizeof(void*));
    if (newItems == NULL) {
        return M3G_FALSE; /* automatic out of memory raised by m3gAlloc */
    }

    /* Copy array contents */
    
    if (array->items != NULL) {
        int i;
        M3G_ASSERT(array->size <= newCapacity);
        
        for (i = 0; i < array->size; ++i) {
            newItems[i] = array->items[i];
        }
        m3gFree(m3g, array->items);
    }

    array->capacity = newCapacity;
    array->items = newItems;
    return M3G_TRUE;
}

/*!
 * \internal
 * \brief Increases the capacity of the array
 *
 * Array growth is limited by the \c MAX_GROWTH constant, to avoid
 * blowing memory management for very large arrays. Small arrays are
 * always grown to the next power of two, to allow for easier
 * recycling of memory blocks.
 */
static M3Gbool m3gGrowArray(PointerArray *array, Interface *m3g)
{
    M3Gsizei capacity = array->capacity;
    M3Gsizei newCapacity;

    /* Calculate the new capacity for the array */

    if (capacity >= MIN_CAPACITY) {
        if (capacity < MAX_GROWTH) {
            newCapacity = MIN_CAPACITY << 1;
            while (newCapacity <= capacity) {
                newCapacity <<= 1;
            }
        }
        else {
            newCapacity = capacity + MAX_GROWTH;
        }
    }
    else {
        newCapacity = MIN_CAPACITY;
    }

    /* Reallocate the array to the new capacity */

    return m3gReallocateArray(array, newCapacity, m3g);
}

/*----------------------------------------------------------------------
 * M3G internal functions
 *--------------------------------------------------------------------*/

/*!
 * \internal
 * \brief Appends a new item to the end of the array
 *
 * Grows the array if necessary, by allocating new data from the
 * interface \c m3g.
 */
static M3Gint m3gArrayAppend(PointerArray *array, void *item, Interface *m3g)
{
    M3G_VALIDATE_PTR(array);
    M3G_ASSERT(array->size <= array->capacity);
    if (array->size == array->capacity) {
        if (!m3gGrowArray(array, m3g)) {
            return -1;
        }
    }
    array->items[array->size] = item;
    return (array->size++);
}

/*!
 * \internal
 * \brief Deletes an element in the array
 *
 * All subsequent elements will move back to fill the gap, and the
 * size of the array decremented by one.
 */
static void m3gArrayDelete(PointerArray *array, M3Gint idx)
{
    int i, n;
    M3G_VALIDATE_PTR(array);
    M3G_ASSERT(idx >= 0 && idx < array->size);

    n = --array->size;
    for (i = idx; i < n; ++i) {
        array->items[i] = array->items[i+1];
    }

    M3G_ASSERT(array->size >= 0);
}

/*!
 * \internal
 * \brief Finds the first occurrence of an item in the array
 *
 * \return index of \c item, or -1 if not found
 */
static M3Gint m3gArrayFind(const PointerArray *array, void *item)
{
    int i;
    M3G_VALIDATE_PTR(array);

    for (i = 0; i < array->size; ++i) {
        if (array->items[i] == item) {
            return i;
        }
    }
    return -1;
}

/*!
 * \internal
 * \brief Inserts an element into the array
 *
 * All subsequent elements move forward by one index.
 */
static M3Gint m3gArrayInsert(PointerArray *array,
                             M3Gint idx,
                             void *item,
                             Interface *m3g)
{
    int i;
    M3G_VALIDATE_PTR(array);
    M3G_ASSERT(idx >= 0 && idx <= array->size);
    
    if (array->size == array->capacity) {
        if (!m3gGrowArray(array, m3g)) {
            return -1;
        }
    }

    for (i = array->size++; i > idx; --i) {
        array->items[i] = array->items[i-1];
    }
    array->items[idx] = item;
    return idx;
}

#if 0 /* currently unused, but available here */
/*!
 * \internal
 * \brief Removes the first instance of an item from the array
 *
 * \return true if \c item found, false otherwise
 */
static M3Gbool m3gArrayRemove(PointerArray *array, void *item)
{
    M3Gint idx = m3gArrayFind(array, item);
    if (idx >= 0) {
        m3gArrayDelete(array, idx);
        return M3G_TRUE;
    }
    return M3G_FALSE;
}
#endif

/*!
 * \internal
 * \brief Clears all array elements
 *
 * Does not affect the capacity of the array
 */
static void m3gClearArray(PointerArray *array)
{
    M3G_VALIDATE_PTR(array);
    array->size = 0;
}

/*!
 * \internal
 * \brief Destroys the array, freeing any resources allocated
 */
static void m3gDestroyArray(PointerArray *array, Interface *m3g)
{
    M3G_VALIDATE_PTR(array);
    m3gFree(m3g, array->items);
    array->items = NULL;
}

/*!
 * \internal
 * \brief Initializes an array prior to its first use
 *
 * \note This is also accomplished by clearing the array structure to
 * zero
 */
static void m3gInitArray(PointerArray *array)
{
    M3G_VALIDATE_PTR(array);
    m3gZero(array, sizeof(PointerArray));
}

/*!
 * \internal
 * \brief Ensures that the array has a specified capacity
 */
static M3Gbool m3gEnsureArrayCapacity(PointerArray *array,
                                      M3Gsizei capacity,
                                      Interface *m3g)
{
    M3G_VALIDATE_PTR(array);    
    if (array->capacity < capacity) {
        return m3gReallocateArray(array, capacity, m3g);
    }
    return M3G_TRUE;
}

#if 0 /* currently unused, but available here */
/*!
 * \internal
 * \brief Minimizes the memory usage of the array
 */
static M3Gbool m3gTrimArray(PointerArray *array, Interface *m3g)
{
    M3G_VALIDATE_PTR(array);
    M3G_ASSERT(array->size <= array->capacity);
    return m3gReallocateArray(array, array->size, m3g);
}
#endif


#undef MIN_CAPACITY
#undef MAX_GROWTH