| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #ifndef _PICO_UTIL_PHEAP_H |
| #define _PICO_UTIL_PHEAP_H |
| |
| #include "pico.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PHEAP, Enable/disable assertions in the pheap module, type=bool, default=0, group=pico_util |
| #ifndef PARAM_ASSERTIONS_ENABLED_PHEAP |
| #define PARAM_ASSERTIONS_ENABLED_PHEAP 0 |
| #endif |
| |
| /** |
| * \file pheap.h |
| * \defgroup util_pheap pheap |
| * Pairing Heap Implementation |
| * \ingroup pico_util |
| * |
| * pheap defines a simple pairing heap. the implementation simply tracks array indexes, it is up to |
| * the user to provide storage for heap entries and a comparison function. |
| * |
| * NOTE: this class is not safe for concurrent usage. It should be externally protected. Furthermore |
| * if used concurrently, the caller needs to protect around their use of the returned id. |
| * for example, ph_remove_head returns the id of an element that is no longer in the heap. |
| * |
| * The user can still use this to look at the data in their companion array, however obviously further operations |
| * on the heap may cause them to overwrite that data as the id may be reused on subsequent operations |
| * |
| */ |
| // PICO_CONFIG: PICO_PHEAP_MAX_ENTRIES, Maximum number of entries in the pheap, min=1, max=65534, default=255, group=pico_util |
| #ifndef PICO_PHEAP_MAX_ENTRIES |
| #define PICO_PHEAP_MAX_ENTRIES 255 |
| #endif |
| |
| // public heap_node ids are numbered from 1 (0 means none) |
| #if PICO_PHEAP_MAX_ENTRIES < 256 |
| typedef uint8_t pheap_node_id_t; |
| #elif PICO_PHEAP_MAX_ENTRIES < 65535 |
| typedef uint16_t pheap_node_id_t; |
| #else |
| #error invalid PICO_PHEAP_MAX_ENTRIES |
| #endif |
| |
| typedef struct pheap_node { |
| pheap_node_id_t child, sibling, parent; |
| } pheap_node_t; |
| |
| // return true if a < b in natural order |
| typedef bool (*pheap_comparator)(void *user_data, pheap_node_id_t a, pheap_node_id_t b); |
| |
| typedef struct pheap { |
| pheap_node_t *nodes; |
| pheap_comparator comparator; |
| void *user_data; |
| pheap_node_id_t max_nodes; |
| pheap_node_id_t root_id; |
| // we remove from head and add to tail to stop reusing the same ids |
| pheap_node_id_t free_head_id; |
| pheap_node_id_t free_tail_id; |
| } pheap_t; |
| |
| pheap_t *ph_create(uint max_nodes, pheap_comparator comparator, void *user_data); |
| |
| void ph_clear(pheap_t *heap); |
| |
| void ph_destroy(pheap_t *heap); |
| |
| static inline pheap_node_t *ph_get_node(pheap_t *heap, pheap_node_id_t id) { |
| assert(id && id <= heap->max_nodes); |
| return heap->nodes + id - 1; |
| } |
| |
| static void ph_add_child_node(pheap_t *heap, pheap_node_id_t parent_id, pheap_node_id_t child_id) { |
| pheap_node_t *n = ph_get_node(heap, parent_id); |
| assert(parent_id); |
| assert(child_id); |
| assert(parent_id != child_id); |
| pheap_node_t *c = ph_get_node(heap, child_id); |
| c->parent = parent_id; |
| if (!n->child) { |
| n->child = child_id; |
| } else { |
| c->sibling = n->child; |
| n->child = child_id; |
| } |
| } |
| |
| static pheap_node_id_t ph_merge_nodes(pheap_t *heap, pheap_node_id_t a, pheap_node_id_t b) { |
| if (!a) return b; |
| if (!b) return a; |
| if (heap->comparator(heap->user_data, a, b)) { |
| ph_add_child_node(heap, a, b); |
| return a; |
| } else { |
| ph_add_child_node(heap, b, a); |
| return b; |
| } |
| } |
| |
| static inline pheap_node_id_t ph_new_node(pheap_t *heap) { |
| if (!heap->free_head_id) return 0; |
| pheap_node_id_t id = heap->free_head_id; |
| heap->free_head_id = ph_get_node(heap, id)->sibling; |
| if (!heap->free_head_id) heap->free_tail_id = 0; |
| return id; |
| } |
| |
| // note this will callback the comparator for the node |
| // returns the (new) root of the heap |
| static inline pheap_node_id_t ph_insert(pheap_t *heap, pheap_node_id_t id) { |
| assert(id); |
| pheap_node_t *hn = ph_get_node(heap, id); |
| hn->child = hn->sibling = hn->parent = 0; |
| heap->root_id = ph_merge_nodes(heap, heap->root_id, id); |
| return heap->root_id; |
| } |
| |
| static inline pheap_node_id_t ph_peek_head(pheap_t *heap) { |
| return heap->root_id; |
| } |
| |
| pheap_node_id_t ph_remove_head_reserve(pheap_t *heap, bool reserve); |
| |
| static inline pheap_node_id_t ph_remove_head(pheap_t *heap) { |
| return ph_remove_head_reserve(heap, false); |
| } |
| |
| static inline bool ph_contains(pheap_t *heap, pheap_node_id_t id) { |
| return id == heap->root_id || ph_get_node(heap, id)->parent; |
| } |
| |
| bool ph_delete(pheap_t *heap, pheap_node_id_t id); |
| |
| static inline void ph_add_to_free_list(pheap_t *heap, pheap_node_id_t id) { |
| assert(id && !ph_contains(heap, id)); |
| if (heap->free_tail_id) { |
| ph_get_node(heap, heap->free_tail_id)->sibling = id; |
| } |
| heap->free_tail_id = id; |
| } |
| |
| void ph_dump(pheap_t *heap, void (*dump_key)(pheap_node_id_t, void *), void *user_data); |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |