#include "ops/colbind.h"
#include "table/table.h"
#include <stdatomic.h>
#include <string.h>
static _Atomic int64_t g_bind_fail_at;
void ray_graph_bind_test_fail_alloc(int64_t nth) { atomic_store(&g_bind_fail_at, nth); }
static bool bind_alloc_fails(void) {
int64_t v = atomic_load(&g_bind_fail_at);
while (v > 0)
if (atomic_compare_exchange_weak(&g_bind_fail_at, &v, v - 1)) return v == 1;
return false;
}
typedef struct { ray_t* tbl; int64_t sym; ray_col_src_t src; } colbind_ent_t;
typedef struct ray_col_binds_s {
colbind_ent_t** e;
uint32_t n, cap;
} ray_col_binds_t;
ray_col_src_t* ray_graph_bind_col(ray_graph_t* g, ray_t* tbl, int64_t sym, ray_t** err) {
*err = NULL;
if (!g || !tbl) return NULL;
ray_col_binds_t* b = g->binds;
ray_t* cur = NULL;
bool cur_set = false;
for (uint32_t i = 0; b && i < b->n; i++) {
if (b->e[i]->tbl != tbl || b->e[i]->sym != sym) continue;
if (!cur_set) {
cur = ray_table_slot(tbl, sym);
cur_set = true;
}
if (b->e[i]->src.obj == cur) return &b->e[i]->src;
}
ray_col_meta_t m;
if (!ray_table_col_meta(tbl, sym, &m)) return NULL;
if (bind_alloc_fails()) { *err = ray_error("oom", "column bind"); return NULL; }
if (!b) {
b = ray_calloc_raw(sizeof *b);
if (!b) { *err = ray_error("oom", "column bind"); return NULL; }
g->binds = b;
}
if (b->n == b->cap) {
uint32_t nc = b->cap ? b->cap * 2 : 8;
colbind_ent_t** ne = ray_realloc_raw(b->e, nc * sizeof *ne);
if (!ne) { *err = ray_error("oom", "column bind"); return NULL; }
b->e = ne; b->cap = nc;
}
colbind_ent_t* x = ray_calloc_raw(sizeof *x);
if (!x) { *err = ray_error("oom", "column bind"); return NULL; }
ray_t* e = ray_table_col_src(tbl, sym, &x->src);
if (e || !x->src.obj) { ray_free_raw(x); *err = e; return NULL; }
x->tbl = tbl; x->sym = sym;
b->e[b->n++] = x;
return &x->src;
}
bool ray_col_bind_declines(ray_t* tbl, int64_t sym) {
ray_col_meta_t m;
if (!ray_table_col_meta(tbl, sym, &m)) return false;
if (m.type == RAY_MAPCOMMON) return true;
if (m.enc) return (m.enc->attrs & RAY_ATTR_SLICE) != 0;
return RAY_IS_PARTED(m.type) && (m.attrs & RAY_ATTR_SLICE) != 0;
}
void ray_graph_binds_free(ray_graph_t* g) {
ray_col_binds_t* b = g ? g->binds : NULL;
if (!b) return;
for (uint32_t i = 0; i < b->n; i++) { ray_col_src_release(&b->e[i]->src); ray_free_raw(b->e[i]); }
ray_free_raw(b->e);
ray_free_raw(b);
g->binds = NULL;
}
static _Atomic uint64_t g_unbound;
uint64_t ray_graph_binds_dropped(void) { return atomic_load(&g_unbound); }
void ray_graph_unbind_table(ray_graph_t* g, ray_t* tbl) {
ray_col_binds_t* b = g ? g->binds : NULL;
if (!b || !tbl) return;
uint32_t w = 0;
for (uint32_t i = 0; i < b->n; i++) {
if (b->e[i]->tbl == tbl) {
ray_col_src_release(&b->e[i]->src);
ray_free_raw(b->e[i]);
atomic_fetch_add(&g_unbound, 1);
} else {
b->e[w++] = b->e[i];
}
}
b->n = w;
}
uint32_t ray_graph_binds_count(const ray_graph_t* g) {
return (g && g->binds) ? g->binds->n : 0;
}