#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "Ecore_Data.h" #define HEAP_INCREMENT 4096 #define PARENT(i) (i / 2) #define LEFT(i) (2 * i) #define RIGHT(i) (2 * i + 1) static void _ecore_sheap_heapify(Ecore_Sheap *heap, int i); static void _ecore_sheap_update_data(Ecore_Sheap *heap); /** * Allocate and initialize a new binary heap * @param compare The function for comparing keys, NULL for direct comparison * @param size The number of elements to allow in the heap * @return A pointer to the newly allocated binary heap on success, NULL on * failure. */ EAPI Ecore_Sheap * ecore_sheap_new(Ecore_Compare_Cb compare, int size) { Ecore_Sheap *heap = NULL; heap = (Ecore_Sheap *)malloc(sizeof(Ecore_Sheap)); if (!heap) return NULL; memset(heap, 0, sizeof(Ecore_Sheap)); if (!ecore_sheap_init(heap, compare, size)) { FREE(heap); return NULL; } return heap; } /** * Initialize a binary heap to default values * @param heap The heap to initialize * @param compare The function for comparing keys, NULL for direct comparison * @param size The number of elements to allow in the heap * @return TRUE on success, FALSE on failure */ EAPI int ecore_sheap_init(Ecore_Sheap *heap, Ecore_Compare_Cb compare, int size) { CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); heap->space = size; if (!compare) heap->compare = ecore_direct_compare; else heap->compare = compare; heap->order = ECORE_SORT_MIN; heap->data = (void **)malloc(heap->space * sizeof(void *)); if (!heap->data) return FALSE; memset(heap->data, 0, heap->space * sizeof(void *)); return TRUE; } /** * Free up the memory used by the heap * * Frees the memory used by @a heap, calls the destroy function on each data * item if necessary. * * @param heap The heap to be freed */ EAPI void ecore_sheap_destroy(Ecore_Sheap *heap) { int i; CHECK_PARAM_POINTER("heap", heap); /* * Free data in heap */ if (heap->free_func) for (i = 0; i < heap->size; i++) heap->free_func(heap->data[i]); FREE(heap->data); FREE(heap); } /** * Set the function for freeing data. * @param heap The heap that will use this function when nodes are * destroyed. * @param free_func The function that will free the key data. * @return @c TRUE on successful set, @c FALSE otherwise. */ EAPI int ecore_sheap_free_cb_set(Ecore_Sheap *heap, Ecore_Free_Cb free_func) { CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); heap->free_func = free_func; return TRUE; } /** * Insert new data into the heap. * @param heap The heap to insert @a data. * @param data The data to add to @a heap. * @return TRUE on success, NULL on failure. Increases the size of the heap if * it becomes larger than available space. */ EAPI int ecore_sheap_insert(Ecore_Sheap *heap, void *data) { int i; void *temp; int parent; int position; CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); /* * Increase the size of the allocated data area if there isn't enough * space available to add this data */ if (heap->size >= heap->space) return FALSE; heap->sorted = FALSE; /* * Place the data at the end of the heap initially. Then determine the * parent and position in the array of it's parent. */ heap->data[heap->size] = data; position = heap->size; heap->size++; i = heap->size; parent = PARENT(i) - 1; /* * Check the order of the heap to decide where to place the inserted * data. The loop is placed inside the if statement to reduce the * number of branching decisions that must be predicted. */ if (heap->order == ECORE_SORT_MIN) while ((position > 0) && heap->compare(heap->data[parent], heap->data[position]) > 0) { /* * Swap the data with it's parents to move it up in * the heap. */ temp = heap->data[position]; heap->data[position] = heap->data[parent]; heap->data[parent] = temp; /* * Now determine the new position for the next * iteration of the loop, as well as it's parents * position. */ i = PARENT(i); position = i - 1; parent = PARENT(i) - 1; } else while ((position > 0) && heap->compare(heap->data[parent], heap->data[position]) < 0) { /* * Swap the data with it's parents to move it up in * the heap. */ temp = heap->data[position]; heap->data[position] = heap->data[PARENT(i) - 1]; heap->data[PARENT(i) - 1] = temp; /* * Now determine the new position for the next * iteration of the loop, as well as it's parents * position. */ i = PARENT(i); position = i - 1; parent = PARENT(i) - 1; } return TRUE; } /** * Extract the item at the top of the heap * @param heap The heap to remove the top item * @return The top item of the heap on success, NULL on failure. * @note The extract function maintains the heap properties after the * extract. */ EAPI void * ecore_sheap_extract(Ecore_Sheap *heap) { void *extreme; if (heap->size < 1) return NULL; heap->sorted = FALSE; extreme = heap->data[0]; heap->size--; heap->data[0] = heap->data[heap->size]; _ecore_sheap_heapify(heap, 1); return extreme; } /** * Examine the item at the top of the heap * @param heap The heap to examine the top item * @return The top item of the heap on success, NULL on failure. * @note The function does not alter the heap. */ EAPI void * ecore_sheap_extreme(Ecore_Sheap *heap) { if (heap->size < 1) return NULL; return heap->data[0]; } /** * Change the value of the specified item in the heap * @param heap The heap to search for the item to change * @param item The item in the heap to change * @param newval The new value assigned to the item in the heap * @return TRUE on success, FALSE on failure. * @note The heap does not free the old data since it must be passed * in, so the caller can perform the free if desired. */ EAPI int ecore_sheap_change(Ecore_Sheap *heap, void *item, void *newval) { int i; CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); for (i = 0; i < heap->size && heap->compare(heap->data[i], item); i++) ; if (i < heap->size) heap->data[i] = newval; else return FALSE; /* * FIXME: This is not the correct procedure when a change occurs. */ _ecore_sheap_heapify(heap, 1); return TRUE; } /** * Change the comparison function for the heap * @param heap The heap to change comparison function * @param compare The new function for comparing nodes * @return TRUE on success, FALSE on failure. * * The comparison function is changed to @compare and the heap is heapified * by the new comparison. */ EAPI int ecore_sheap_compare_set(Ecore_Sheap *heap, Ecore_Compare_Cb compare) { CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); if (!compare) heap->compare = ecore_direct_compare; else heap->compare = compare; _ecore_sheap_update_data(heap); return TRUE; } /** * Change the order of the heap * @param heap The heap to change the order * @param order The new order of the heap * * Changes the heap order of @heap and re-heapifies the data to this new * order. The default order is a min heap. */ EAPI void ecore_sheap_order_set(Ecore_Sheap *heap, char order) { CHECK_PARAM_POINTER("heap", heap); heap->order = order; _ecore_sheap_update_data(heap); } /** * Sort the data in the heap * @param heap The heap to be sorted * * Sorts the data in the heap into the order that is used for the heap's * data. */ EAPI void ecore_sheap_sort(Ecore_Sheap *heap) { int i = 0; void **new_data; CHECK_PARAM_POINTER("heap", heap); new_data = (void **)malloc(heap->size * sizeof(void *)); /* * Extract the heap and insert into the new data array in order. */ while (heap->size > 0) new_data[i++] = ecore_sheap_extract(heap); /* * Free the old data array and update the heap with the new data, also * mark as sorted. */ FREE(heap->data); heap->data = new_data; heap->size = i; heap->sorted = TRUE; } /* * Access the item at the ith position in the heap * @param heap The heap to access the internal data * @param i The index of the data within the heap * @return The data located at the ith position within @heap on success, * NULL on failure. * @note The data is guaranteed to be in sorted order. */ EAPI inline void * ecore_sheap_item(Ecore_Sheap *heap, int i) { if (i >= heap->size) return NULL; /* * Make sure the data is sorted so we return the correct value. */ if (!heap->sorted) ecore_sheap_sort(heap); return heap->data[i]; } /* * Regain the heap properties starting at position i * @param heap The heap to regain heap properties * @param i The position to start heapifying */ static void _ecore_sheap_heapify(Ecore_Sheap *heap, int i) { int extreme; int left = LEFT(i); int right = RIGHT(i); if (heap->order == ECORE_SORT_MIN) { if (left <= heap->size && heap->compare(heap->data[left - 1], heap->data[i - 1]) < 0) extreme = left; else extreme = i; if (right <= heap->size && heap->compare(heap->data[right - 1], heap->data[extreme - 1]) < 0) extreme = right; } else { if (left <= heap->size && heap->compare(heap->data[left - 1], heap->data[i - 1]) > 0) extreme = left; else extreme = i; if (right <= heap->size && heap->compare(heap->data[right - 1], heap->data[extreme - 1]) > 0) extreme = right; } /* * If the data needs to be swapped down the heap, recurse on * heapifying it's new placement. */ if (extreme != i) { void *temp; temp = heap->data[extreme - 1]; heap->data[extreme - 1] = heap->data[i - 1]; heap->data[i - 1] = temp; _ecore_sheap_heapify(heap, extreme); } } static void _ecore_sheap_update_data(Ecore_Sheap *heap) { int i, old_size; void **data; /* * Track the old values from the heap */ old_size = heap->size; data = heap->data; heap->size = 0; heap->data = malloc(heap->space * sizeof(void *)); for (i = 0; i < old_size; i++) ecore_sheap_insert(heap, data[i]); FREE(data); } int ecore_direct_compare(const void *key1, const void *key2) { uintptr_t k1, k2; k1 = (uintptr_t)key1; k2 = (uintptr_t)key2; if (k1 > k2) return 1; if (k1 < k2) return -1; return 0; }