Data-Graph-Shared
view release on metacpan or search on metacpan
}
static inline void graph_bit_free(uint64_t *bm, uint32_t idx) {
__atomic_fetch_and(&bm[idx / 64], ~((uint64_t)1 << (idx % 64)), __ATOMIC_RELEASE);
}
/* ================================================================
* Graph operations (must hold mutex)
* ================================================================ */
static inline int32_t graph_add_node_locked(GraphHandle *h, int64_t data) {
int32_t idx = graph_bit_alloc(h->node_bitmap, h->node_bwords, h->hdr->max_nodes);
if (idx < 0) return -1;
h->node_data[idx] = data;
h->node_heads[idx] = GRAPH_NONE;
__atomic_fetch_add(&h->hdr->node_count, 1, __ATOMIC_RELAXED);
return idx;
}
static inline int graph_add_edge_locked(GraphHandle *h, uint32_t src, uint32_t dst, int64_t weight) {
if (src >= h->hdr->max_nodes || dst >= h->hdr->max_nodes) return 0;
if (!graph_bit_set(h->node_bitmap, src) || !graph_bit_set(h->node_bitmap, dst))
return 0;
int32_t eidx = graph_bit_alloc(h->edge_bitmap, h->edge_bwords, h->hdr->max_edges);
if (eidx < 0) return 0;
h->edges[eidx].dst = dst;
h->edges[eidx].weight = weight;
h->edges[eidx].next = h->node_heads[src];
h->node_heads[src] = (uint32_t)eidx;
__atomic_fetch_add(&h->hdr->edge_count, 1, __ATOMIC_RELAXED);
return 1;
}
static inline int graph_remove_node_locked(GraphHandle *h, uint32_t node) {
if (node >= h->hdr->max_nodes) return 0;
if (!graph_bit_set(h->node_bitmap, node)) return 0;
/* free all outgoing edges */
uint32_t eidx = h->node_heads[node];
while (eidx != GRAPH_NONE) {
uint32_t next = h->edges[eidx].next;
graph_bit_free(h->edge_bitmap, eidx);
__atomic_fetch_sub(&h->hdr->edge_count, 1, __ATOMIC_RELAXED);
eidx = next;
}
h->node_heads[node] = GRAPH_NONE;
graph_bit_free(h->node_bitmap, node);
__atomic_fetch_sub(&h->hdr->node_count, 1, __ATOMIC_RELAXED);
return 1;
}
/* Like remove_node_locked, but also splices every other node's adjacency
* list to drop edges pointing TO `node` (incoming edges). O(N+E). */
static inline int graph_remove_node_full_locked(GraphHandle *h, uint32_t node) {
if (node >= h->hdr->max_nodes) return 0;
if (!graph_bit_set(h->node_bitmap, node)) return 0;
uint32_t max_n = h->hdr->max_nodes;
for (uint32_t src = 0; src < max_n; src++) {
if (src == node) continue;
if (!graph_bit_set(h->node_bitmap, src)) continue;
uint32_t *slot = &h->node_heads[src];
uint32_t eidx = *slot;
while (eidx != GRAPH_NONE) {
uint32_t next = h->edges[eidx].next;
if (h->edges[eidx].dst == node) {
*slot = next;
graph_bit_free(h->edge_bitmap, eidx);
__atomic_fetch_sub(&h->hdr->edge_count, 1, __ATOMIC_RELAXED);
} else {
slot = &h->edges[eidx].next;
}
eidx = next;
}
}
return graph_remove_node_locked(h, node);
}
/* ================================================================
* Public API (lock + operation + unlock)
* ================================================================ */
static inline int32_t graph_add_node(GraphHandle *h, int64_t data) {
graph_mutex_lock(h->hdr);
int32_t r = graph_add_node_locked(h, data);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
graph_mutex_unlock(h->hdr);
return r;
}
static inline int graph_add_edge(GraphHandle *h, uint32_t src, uint32_t dst, int64_t weight) {
graph_mutex_lock(h->hdr);
int r = graph_add_edge_locked(h, src, dst, weight);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
graph_mutex_unlock(h->hdr);
return r;
}
static inline int graph_remove_node(GraphHandle *h, uint32_t node) {
graph_mutex_lock(h->hdr);
int r = graph_remove_node_locked(h, node);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
graph_mutex_unlock(h->hdr);
return r;
}
static inline int graph_remove_node_full(GraphHandle *h, uint32_t node) {
graph_mutex_lock(h->hdr);
int r = graph_remove_node_full_locked(h, node);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
graph_mutex_unlock(h->hdr);
return r;
}
static inline int graph_has_node(GraphHandle *h, uint32_t node) {
if (node >= h->hdr->max_nodes) return 0;
return graph_bit_set(h->node_bitmap, node);
}
t/02-review-fixes.t view on Meta::CPAN
my $a = $g->add_node(1);
my $b = $g->add_node(2);
# No outgoing, no incoming edges.
ok $g->remove_node_full($a), 'remove_node_full: isolated node';
ok !$g->has_node($a), 'isolated node removed';
# Non-existent index â false.
ok !$g->remove_node_full(42), 'remove_node_full: non-existent returns false';
# Self-loop: outgoing edge is freed by inner remove_node_locked
# (outer splice skips src == node).
my $c = $g->add_node(3);
ok $g->add_edge($c, $c, 7), 'self-loop';
is $g->edge_count, 1;
ok $g->remove_node_full($c), 'remove_node_full: self-loop';
is $g->edge_count, 0, 'self-loop edge freed';
}
done_testing;
( run in 1.065 second using v1.01-cache-2.11-cpan-e1769b4cff6 )