Data-Graph-Shared

 view release on metacpan or  search on metacpan

graph.h  view on Meta::CPAN

}

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 0.744 second using v1.01-cache-2.11-cpan-13bb782fe5a )