156 lines
4.6 KiB
C
156 lines
4.6 KiB
C
/*
|
|
* 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
|