// 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. #include #include #include #include #include #include "hqueue.h" struct hqueue { int version; uint32_t idx; uint32_t max_elems; uint32_t heap_size; hqnode_t* heap; // one based index }; struct hqnode { double priority; void* value; }; static inline void hqueue_exchange(hqueue_t* hqueue, int i, int j) { hqnode_t tmp; tmp = hqueue->heap[i]; hqueue->heap[i] = hqueue->heap[j]; hqueue->heap[j] = tmp; return; } static inline int hqueue_less(hqueue_t* hqueue, int i, int j) { return hqueue->heap[i].priority < hqueue->heap[j].priority; } static void hqueue_fix_up(hqueue_t* hqueue, int k) { while(k > 1 && hqueue_less(hqueue, k/2, k)) { hqueue_exchange(hqueue, k/2, k); k = k/2; } return; } static void hqueue_fix_down(hqueue_t* hqueue, int k) { int j; int n = hqueue->idx; while(2*k <= n) { j = 2*k; if(j < n && hqueue_less(hqueue, j, j+1)) { j++; } if(!hqueue_less(hqueue, k, j)) { break; } hqueue_exchange(hqueue, k, j); k = j; } return; } hqueue_t* hqueue_new(uint32_t max_elems, uint32_t heap_size) { hqueue_t* hqueue = NULL; size_t total_heap_size; if(max_elems == 0 || heap_size == 0) { return NULL; } if(max_elems < heap_size) { heap_size = max_elems; } hqueue = HQUEUE_ALLOC(sizeof(hqueue_t)); if(hqueue == NULL) { return NULL; } memset(hqueue, '\0', sizeof(hqueue_t)); hqueue->version = HQ_VERSION; hqueue->max_elems = max_elems; hqueue->heap_size = heap_size; hqueue->idx = 0; total_heap_size = sizeof(hqnode_t) * (hqueue->heap_size+1); hqueue->heap = (hqnode_t*) HQUEUE_ALLOC(total_heap_size); if(hqueue->heap == NULL ) { HQUEUE_FREE(hqueue); return NULL; } memset(hqueue->heap, '\0', total_heap_size); return hqueue; } void hqueue_free(hqueue_t* hqueue) { HQUEUE_FREE(hqueue->heap); HQUEUE_FREE(hqueue); return; } void hqueue_free2(hqueue_t* hqueue, void (*free_node)(void* node)) { uint32_t i; for(i = 1; i < hqueue->heap_size + 1; i++) { if(i <= hqueue->idx) { free_node(hqueue->heap[i].value); } else { assert(hqueue->heap[i].value == NULL && "inactive elements must be NULL"); } } hqueue_free(hqueue); return; } // Extraction order is undefined for entries with duplicate priorities int hqueue_extract_max(hqueue_t* hqueue, double* priority, void** value) { if(hqueue->idx <= 0) { return 0; } hqueue_exchange(hqueue, 1, hqueue->idx); *priority = hqueue->heap[hqueue->idx].priority; *value = hqueue->heap[hqueue->idx].value; hqueue->heap[hqueue->idx].value = NULL; hqueue->idx--; // heap uses one based index, so we decrement after hqueue_fix_down(hqueue, 1); return 1; } void hqueue_get_elem(hqueue_t* hqueue, uint32_t idx, double *priority, void** value) { *priority = hqueue->heap[idx].priority; *value = hqueue->heap[idx].value; return; } static int hqueue_maybe_resize(hqueue_t* hqueue) { uint32_t min_resize; if(hqueue->idx + 1 > hqueue->heap_size) { if(hqueue->idx * HQ_SCALE_FACTOR > hqueue->max_elems) { min_resize = hqueue->max_elems; } else { min_resize = hqueue->idx * HQ_SCALE_FACTOR; } return hqueue_resize_heap(hqueue, min_resize); } return 1; } int hqueue_insert(hqueue_t* hqueue, double priority, void* value) { if(hqueue->idx >= hqueue->max_elems) { return 0; } if(!hqueue_maybe_resize(hqueue)) { return 0; } hqueue->idx++; // heap uses one based index, so we increment first hqueue->heap[hqueue->idx].priority = priority; hqueue->heap[hqueue->idx].value = value; hqueue_fix_up(hqueue, hqueue->idx); return 1; } uint32_t hqueue_size(hqueue_t* hqueue) { return hqueue->idx; } uint32_t hqueue_heap_size(hqueue_t* hqueue) { return hqueue->heap_size; } uint32_t hqueue_max_elems(hqueue_t* hqueue) { return hqueue->max_elems; } void hqueue_scale_by(hqueue_t* hqueue, double factor) { uint32_t i; for(i = 1; i <= hqueue->idx && i <= hqueue->heap_size; i++) { hqueue->heap[i].priority *= factor; } return; } uint32_t hqueue_resize_heap(hqueue_t* hqueue, uint32_t new_heap_size) { uint32_t old_heap_size; size_t total_heap_size; hqnode_t* tmp_heap; uint32_t i; if(hqueue->idx > new_heap_size) { return 0; } total_heap_size = sizeof(hqnode_t) * (new_heap_size+1); old_heap_size = hqueue->heap_size; if((tmp_heap = (hqnode_t*) HQUEUE_ALLOC(total_heap_size)) == NULL) { return 0; } memset(tmp_heap, '\0', total_heap_size); for(i = 1; i <= hqueue->idx && i <= old_heap_size; i++) { if(i <= hqueue->idx) { tmp_heap[i] = hqueue->heap[i]; hqueue->heap[i].value = NULL; } else { assert(hqueue->heap[i].value == NULL && "unexpected NULL element during heap resize"); } } HQUEUE_FREE(hqueue->heap); hqueue->heap = tmp_heap; hqueue->heap_size = new_heap_size; return old_heap_size; } int hqueue_set_max_elems(hqueue_t* hqueue, uint32_t new_max_elems) { uint32_t old_max_elems; if(hqueue->heap_size > new_max_elems) { if(!hqueue_resize_heap(hqueue, new_max_elems)) { return 0; } } old_max_elems = hqueue->max_elems; hqueue->max_elems = new_max_elems; return old_max_elems; }