#include "ops/hll.h"
#include "ops/internal.h"
#include "ops/ops.h"
#include "core/pool.h"
#include "table/sym.h"
#include <math.h>
#include <string.h>
#include <stdatomic.h>
int ray_hll_init(ray_hll_t* h, uint8_t p) {
if (!h) return -1;
if (p < 4) p = 4;
if (p > 18) p = 18;
memset(h, 0, sizeof(*h));
uint32_t m = 1u << p;
h->p = p;
h->m = m;
h->regs = (uint8_t*)scratch_calloc(&h->_hdr, (size_t)m);
if (!h->regs) return -1;
return 0;
}
void ray_hll_init_sparse(ray_hll_t* h, uint8_t p,
uint32_t* sparse_buf, uint32_t sparse_cap,
uint8_t* dense_buf) {
if (!h) return;
if (p < 4) p = 4;
if (p > 18) p = 18;
memset(h, 0, sizeof(*h));
h->p = p;
h->m = 1u << p;
assert(((uintptr_t)dense_buf & 1u) == 0);
uintptr_t tagged = (uintptr_t)dense_buf | (uintptr_t)1;
h->_hdr = (ray_t*)tagged;
h->sparse_keys = sparse_buf;
h->sparse_count = 0;
h->sparse_cap = sparse_cap;
}
static inline uint8_t* hll_caller_dense_buf(const ray_hll_t* h) {
uintptr_t tagged = (uintptr_t)h->_hdr;
if (!(tagged & 1)) return NULL;
return (uint8_t*)(tagged & ~(uintptr_t)1);
}
void ray_hll_promote_to_dense(ray_hll_t* h) {
if (!h || h->regs) return;
uint8_t* dense = hll_caller_dense_buf(h);
if (!dense) {
ray_t* hdr = NULL;
dense = (uint8_t*)scratch_calloc(&hdr, (size_t)h->m);
if (!dense) {
return;
}
h->_hdr = hdr;
} else {
memset(dense, 0, (size_t)h->m);
h->_hdr = NULL;
}
h->regs = dense;
uint32_t* sk = h->sparse_keys;
uint32_t n = h->sparse_count;
for (uint32_t i = 0; i < n; i++) {
uint32_t v = sk[i];
uint32_t idx = v >> 8;
uint8_t rho = (uint8_t)(v & 0xFF);
if (rho > dense[idx]) dense[idx] = rho;
}
h->sparse_keys = NULL;
h->sparse_count = 0;
h->sparse_cap = 0;
}
void ray_hll_free(ray_hll_t* h) {
if (!h) return;
uintptr_t tagged = (uintptr_t)h->_hdr;
if (h->_hdr && !(tagged & 1)) scratch_free(h->_hdr);
h->regs = NULL;
h->_hdr = NULL;
h->sparse_keys = NULL;
h->sparse_count = 0;
h->sparse_cap = 0;
h->m = 0;
h->p = 0;
}
void ray_hll_reset(ray_hll_t* h) {
if (!h) return;
if (h->regs) {
memset(h->regs, 0, (size_t)h->m);
return;
}
if (h->sparse_keys) {
h->sparse_count = 0;
}
}
static inline void hll_merge_sparse_into_dense(uint8_t* d,
const uint32_t* sk,
uint32_t n) {
for (uint32_t i = 0; i < n; i++) {
uint32_t v = sk[i];
uint32_t idx = v >> 8;
uint8_t rho = (uint8_t)(v & 0xFF);
if (rho > d[idx]) d[idx] = rho;
}
}
void ray_hll_merge(ray_hll_t* dst, const ray_hll_t* src) {
if (!dst || !src) return;
if (dst->m != src->m) return;
if (!dst->regs) {
ray_hll_promote_to_dense(dst);
if (!dst->regs) return;
}
if (src->regs) {
const uint8_t* s = src->regs;
uint8_t* d = dst->regs;
uint32_t m = dst->m;
for (uint32_t i = 0; i < m; i++) {
uint8_t a = d[i], b = s[i];
d[i] = a > b ? a : b;
}
} else if (src->sparse_keys) {
hll_merge_sparse_into_dense(dst->regs, src->sparse_keys,
src->sparse_count);
}
}
int64_t ray_hll_estimate(const ray_hll_t* h) {
if (!h) return 0;
uint32_t m = h->m;
if (m == 0) return 0;
double alpha_m;
if (m == 16) alpha_m = 0.673;
else if (m == 32) alpha_m = 0.697;
else if (m == 64) alpha_m = 0.709;
else alpha_m = 0.7213 / (1.0 + 1.079 / (double)m);
double sum_inv = 0.0;
uint32_t n_zeros = 0;
if (h->regs) {
for (uint32_t i = 0; i < m; i++) {
uint8_t r = h->regs[i];
sum_inv += ldexp(1.0, -(int)r);
n_zeros += (r == 0);
}
} else if (h->sparse_keys) {
uint32_t n = h->sparse_count;
const uint32_t* sk = h->sparse_keys;
sum_inv = (double)(m - n);
n_zeros = m - n;
for (uint32_t i = 0; i < n; i++) {
uint8_t r = (uint8_t)(sk[i] & 0xFF);
sum_inv += ldexp(1.0, -(int)r);
}
} else {
sum_inv = (double)m;
n_zeros = m;
}
double raw = alpha_m * (double)m * (double)m / sum_inv;
if (raw <= 2.5 * (double)m && n_zeros != 0) {
raw = (double)m * log((double)m / (double)n_zeros);
}
if (raw < 0.0) raw = 0.0;
return (int64_t)(raw + 0.5);
}
typedef struct {
const ray_t* vec;
int8_t type;
uint8_t attrs;
bool has_nulls;
ray_hll_t* shards;
uint8_t p;
uint32_t n_workers;
_Atomic(int) oom;
} cda_scalar_ctx_t;
static void cda_scalar_fn(void* raw, uint32_t worker_id, int64_t start, int64_t end) {
cda_scalar_ctx_t* c = (cda_scalar_ctx_t*)raw;
if (atomic_load_explicit(&c->oom, memory_order_relaxed)) return;
ray_hll_t* sh = &c->shards[worker_id % c->n_workers];
if (!sh->regs) {
if (ray_hll_init(sh, c->p) != 0) {
atomic_store_explicit(&c->oom, 1, memory_order_relaxed);
return;
}
}
const ray_t* v = c->vec;
const void* base = ray_data((ray_t*)v);
int8_t t = c->type;
bool hn = c->has_nulls;
const int64_t CHK = 65535;
if (t == RAY_I64 || t == RAY_TIMESTAMP) {
const int64_t* d = (const int64_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t v_i = d[r];
if (hn && v_i == NULL_I64) continue;
ray_hll_add(sh, ray_hash_i64(v_i));
}
} else if (t == RAY_I32 || t == RAY_DATE || t == RAY_TIME) {
const int32_t* d = (const int32_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int32_t v_i = d[r];
if (hn && v_i == NULL_I32) continue;
ray_hll_add(sh, ray_hash_i64((int64_t)v_i));
}
} else if (t == RAY_I16) {
const int16_t* d = (const int16_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int16_t v_i = d[r];
if (hn && v_i == NULL_I16) continue;
ray_hll_add(sh, ray_hash_i64((int64_t)v_i));
}
} else if (t == RAY_BOOL || t == RAY_U8) {
const uint8_t* d = (const uint8_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
ray_hll_add(sh, ray_hash_i64((int64_t)d[r]));
}
} else if (t == RAY_F64) {
const double* d = (const double*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
double v_f = d[r];
if (v_f != v_f) continue;
ray_hll_add(sh, ray_hash_f64(v_f));
}
} else if (RAY_IS_SYM(t)) {
uint8_t w = c->attrs & RAY_SYM_W_MASK;
if (w == RAY_SYM_W64) {
const int64_t* d = (const int64_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t v_i = d[r];
if (v_i == 0) continue;
ray_hll_add(sh, ray_hash_i64(v_i));
}
} else if (w == RAY_SYM_W32) {
const uint32_t* d = (const uint32_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
uint32_t v_i = d[r];
if (v_i == 0) continue;
ray_hll_add(sh, ray_hash_i64((int64_t)v_i));
}
} else if (w == RAY_SYM_W16) {
const uint16_t* d = (const uint16_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
uint16_t v_i = d[r];
if (v_i == 0) continue;
ray_hll_add(sh, ray_hash_i64((int64_t)v_i));
}
} else {
const uint8_t* d = (const uint8_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
uint8_t v_i = d[r];
if (v_i == 0) continue;
ray_hll_add(sh, ray_hash_i64((int64_t)v_i));
}
}
} else if (t == RAY_STR) {
ray_t* vm = (ray_t*)v;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
size_t n = 0;
const char* s = ray_str_vec_get(vm, r, &n);
if (!s || n == 0) continue;
ray_hll_add(sh, ray_hash_bytes(s, n));
}
}
}
ray_t* ray_count_distinct_approx(ray_t* x) {
if (!x || RAY_IS_ERR(x)) return x;
if (!ray_is_vec(x)) {
if (ray_is_atom(x)) {
if (RAY_ATOM_IS_NULL(x)) return ray_i64(0);
return ray_i64(1);
}
return ray_error("type", "count_distinct_approx: vec expected");
}
int8_t t = x->type;
if (t != RAY_I64 && t != RAY_I32 && t != RAY_I16 && t != RAY_U8 &&
t != RAY_BOOL && t != RAY_F64 && t != RAY_DATE && t != RAY_TIME &&
t != RAY_TIMESTAMP && t != RAY_STR && !RAY_IS_SYM(t))
return ray_error("type", "count_distinct_approx: unsupported element type");
int64_t n = x->len;
if (n == 0) return ray_i64(0);
ray_pool_t* pool = ray_pool_get();
uint32_t nw = (pool && n >= RAY_PARALLEL_THRESHOLD)
? ray_pool_total_workers(pool) : 1;
ray_t* shards_hdr = NULL;
ray_hll_t* shards = (ray_hll_t*)scratch_calloc(
&shards_hdr, (size_t)nw * sizeof(ray_hll_t));
if (!shards) return ray_error("oom", NULL);
cda_scalar_ctx_t ctx = {
.vec = x,
.type = t,
.attrs = x->attrs,
.has_nulls = ray_vec_may_have_nulls(x),
.shards = shards,
.p = RAY_HLL_DEFAULT_P,
.n_workers = nw,
.oom = 0,
};
if (nw > 1) {
ray_pool_dispatch(pool, cda_scalar_fn, &ctx, n);
} else {
cda_scalar_fn(&ctx, 0, 0, n);
}
if (atomic_load_explicit(&ctx.oom, memory_order_relaxed)) {
for (uint32_t w = 0; w < nw; w++) ray_hll_free(&shards[w]);
scratch_free(shards_hdr);
return ray_error("oom", "count_distinct_approx: HLL alloc failed");
}
uint32_t base = 0;
while (base < nw && !shards[base].regs) base++;
int64_t est = 0;
if (base < nw) {
for (uint32_t w = base + 1; w < nw; w++) {
if (shards[w].regs)
ray_hll_merge(&shards[base], &shards[w]);
}
est = ray_hll_estimate(&shards[base]);
}
for (uint32_t w = 0; w < nw; w++) ray_hll_free(&shards[w]);
scratch_free(shards_hdr);
return ray_i64(est);
}
typedef struct {
const ray_t* vec;
int8_t type;
uint8_t attrs;
bool has_nulls;
const int64_t* idx_buf;
const int64_t* offsets;
const int64_t* counts;
uint8_t p;
uint32_t m;
int64_t* out;
_Atomic(int) oom;
} cda_pg_buf_ctx_t;
static void cda_pg_buf_task(void* raw, uint32_t worker_id, int64_t start, int64_t end) {
(void)worker_id;
cda_pg_buf_ctx_t* c = (cda_pg_buf_ctx_t*)raw;
if (atomic_load_explicit(&c->oom, memory_order_relaxed)) return;
const void* base = ray_data((ray_t*)c->vec);
int8_t t = c->type;
bool hn = c->has_nulls;
uint8_t regs[1u << 14];
uint32_t sparse_buf[RAY_HLL_SPARSE_CAP];
ray_hll_t sk;
for (int64_t g = start; g < end; g++) {
ray_hll_init_sparse(&sk, c->p, sparse_buf,
RAY_HLL_SPARSE_CAP, regs);
int64_t s = c->offsets[g];
int64_t e = s + c->counts[g];
if (t == RAY_I64 || t == RAY_TIMESTAMP) {
const int64_t* d = (const int64_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
int64_t v = d[r];
if (hn && v == NULL_I64) continue;
ray_hll_add(&sk, ray_hash_i64(v));
}
} else if (t == RAY_I32 || t == RAY_DATE || t == RAY_TIME) {
const int32_t* d = (const int32_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
int32_t v = d[r];
if (hn && v == NULL_I32) continue;
ray_hll_add(&sk, ray_hash_i64((int64_t)v));
}
} else if (t == RAY_I16) {
const int16_t* d = (const int16_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
int16_t v = d[r];
if (hn && v == NULL_I16) continue;
ray_hll_add(&sk, ray_hash_i64((int64_t)v));
}
} else if (t == RAY_BOOL || t == RAY_U8) {
const uint8_t* d = (const uint8_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
ray_hll_add(&sk, ray_hash_i64((int64_t)d[r]));
}
} else if (t == RAY_F64) {
const double* d = (const double*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
double v = d[r];
if (v != v) continue;
ray_hll_add(&sk, ray_hash_f64(v));
}
} else if (RAY_IS_SYM(t)) {
uint8_t w = c->attrs & RAY_SYM_W_MASK;
if (w == RAY_SYM_W64) {
const int64_t* d = (const int64_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
int64_t v = d[r]; if (v == 0) continue;
ray_hll_add(&sk, ray_hash_i64(v));
}
} else if (w == RAY_SYM_W32) {
const uint32_t* d = (const uint32_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
uint32_t v = d[r]; if (v == 0) continue;
ray_hll_add(&sk, ray_hash_i64((int64_t)v));
}
} else if (w == RAY_SYM_W16) {
const uint16_t* d = (const uint16_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
uint16_t v = d[r]; if (v == 0) continue;
ray_hll_add(&sk, ray_hash_i64((int64_t)v));
}
} else {
const uint8_t* d = (const uint8_t*)base;
for (int64_t k = s; k < e; k++) {
int64_t r = c->idx_buf[k];
uint8_t v = d[r]; if (v == 0) continue;
ray_hll_add(&sk, ray_hash_i64((int64_t)v));
}
}
}
c->out[g] = ray_hll_estimate(&sk);
}
}
int ray_count_distinct_approx_pg_buf(ray_t* src,
const int64_t* idx_buf,
const int64_t* offsets,
const int64_t* counts,
int64_t n_groups,
uint8_t p, int64_t* out)
{
if (!src || RAY_IS_ERR(src) || !idx_buf || !offsets || !counts || !out)
return -1;
int8_t t = src->type;
bool hashable = (t == RAY_I64 || t == RAY_I32 || t == RAY_I16 ||
t == RAY_U8 || t == RAY_BOOL || t == RAY_F64 ||
t == RAY_DATE || t == RAY_TIME || t == RAY_TIMESTAMP ||
RAY_IS_SYM(t));
if (!hashable) return -1;
if (n_groups <= 0) return 0;
if (p < 4) p = 4;
if (p > 14) p = 14;
uint32_t m = 1u << p;
cda_pg_buf_ctx_t ctx = {
.vec = src,
.type = t,
.attrs = src->attrs,
.has_nulls = ray_vec_may_have_nulls(src),
.idx_buf = idx_buf,
.offsets = offsets,
.counts = counts,
.p = p,
.m = m,
.out = out,
.oom = 0,
};
ray_pool_t* pool = ray_pool_get();
if (pool && ray_pool_total_workers(pool) >= 2 && n_groups >= 4) {
if (n_groups <= 65536) {
ray_pool_dispatch_n(pool, cda_pg_buf_task, &ctx, (uint32_t)n_groups);
} else {
ray_pool_dispatch(pool, cda_pg_buf_task, &ctx, n_groups);
}
} else {
cda_pg_buf_task(&ctx, 0, 0, n_groups);
}
if (atomic_load_explicit(&ctx.oom, memory_order_relaxed)) return -1;
return 0;
}
typedef struct {
ray_hll_t** banks;
const ray_t* vec;
const int64_t* row_gid;
int64_t n_rows;
int64_t n_groups;
int8_t type;
uint8_t attrs;
bool has_nulls;
uint8_t p;
uint32_t m;
} cda_pg_stream_ctx_t;
static void cda_pg_stream_task(void* raw, uint32_t worker_id,
int64_t start, int64_t end) {
cda_pg_stream_ctx_t* c = (cda_pg_stream_ctx_t*)raw;
ray_hll_t* bank = c->banks[worker_id];
if (!bank) return;
const void* base = ray_data((ray_t*)c->vec);
const int64_t* row_gid = c->row_gid;
int64_t ng = c->n_groups;
int8_t t = c->type;
bool hn = c->has_nulls;
const int64_t CHK = 65535;
if (t == RAY_I64 || t == RAY_TIMESTAMP) {
const int64_t* d = (const int64_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
int64_t v = d[r];
if (hn && v == NULL_I64) continue;
ray_hll_add(&bank[gid], ray_hash_i64(v));
}
} else if (t == RAY_I32 || t == RAY_DATE || t == RAY_TIME) {
const int32_t* d = (const int32_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
int32_t v = d[r];
if (hn && v == NULL_I32) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)v));
}
} else if (t == RAY_I16) {
const int16_t* d = (const int16_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
int16_t v = d[r];
if (hn && v == NULL_I16) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)v));
}
} else if (t == RAY_BOOL || t == RAY_U8) {
const uint8_t* d = (const uint8_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)d[r]));
}
} else if (t == RAY_F64) {
const double* d = (const double*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
double v = d[r];
if (v != v) continue;
ray_hll_add(&bank[gid], ray_hash_f64(v));
}
} else if (RAY_IS_SYM(t)) {
uint8_t w = c->attrs & RAY_SYM_W_MASK;
if (w == RAY_SYM_W64) {
const int64_t* d = (const int64_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
int64_t v = d[r]; if (v == 0) continue;
ray_hll_add(&bank[gid], ray_hash_i64(v));
}
} else if (w == RAY_SYM_W32) {
const uint32_t* d = (const uint32_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
uint32_t v = d[r]; if (v == 0) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)v));
}
} else if (w == RAY_SYM_W16) {
const uint16_t* d = (const uint16_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
uint16_t v = d[r]; if (v == 0) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)v));
}
} else {
const uint8_t* d = (const uint8_t*)base;
for (int64_t r = start; r < end; r++) {
if (((r - start) & CHK) == 0 && ray_interrupted()) return;
int64_t gid = row_gid[r];
if (gid < 0 || gid >= ng) continue;
uint8_t v = d[r]; if (v == 0) continue;
ray_hll_add(&bank[gid], ray_hash_i64((int64_t)v));
}
}
}
}
int ray_count_distinct_approx_pg_stream(ray_t* src,
const int64_t* row_gid,
int64_t n_rows,
int64_t n_groups,
uint8_t p, int64_t* out)
{
if (!src || RAY_IS_ERR(src) || !row_gid || !out) return -1;
if (n_rows <= 0 || n_groups <= 0) return -1;
int8_t t = src->type;
bool hashable = (t == RAY_I64 || t == RAY_I32 || t == RAY_I16 ||
t == RAY_U8 || t == RAY_BOOL || t == RAY_F64 ||
t == RAY_DATE || t == RAY_TIME || t == RAY_TIMESTAMP ||
RAY_IS_SYM(t));
if (!hashable) return -1;
if (p < 4) p = 4;
if (p > 14) p = 14;
uint32_t m = 1u << p;
ray_pool_t* pool = ray_pool_get();
uint32_t nw = (pool && n_rows >= RAY_PARALLEL_THRESHOLD)
? ray_pool_total_workers(pool) : 1;
ray_t* banks_hdr = NULL;
ray_hll_t** banks = (ray_hll_t**)scratch_calloc(
&banks_hdr, (size_t)nw * sizeof(ray_hll_t*));
if (!banks) return -1;
ray_t** slab_hdrs_array = NULL;
ray_t* slab_hdrs_hdr = NULL;
slab_hdrs_array = (ray_t**)scratch_calloc(
&slab_hdrs_hdr, (size_t)nw * sizeof(ray_t*));
if (!slab_hdrs_array) {
scratch_free(banks_hdr);
return -1;
}
size_t sketches_bytes = (size_t)n_groups * sizeof(ray_hll_t);
size_t sparse_bytes = (size_t)n_groups *
RAY_HLL_SPARSE_CAP * sizeof(uint32_t);
size_t dense_bytes = (size_t)n_groups * (size_t)m;
size_t per_worker = sketches_bytes + sparse_bytes + dense_bytes;
bool oom = false;
for (uint32_t w = 0; w < nw; w++) {
ray_t* slab_hdr = NULL;
uint8_t* slab = (uint8_t*)scratch_alloc(&slab_hdr, per_worker);
if (!slab) { oom = true; break; }
slab_hdrs_array[w] = slab_hdr;
ray_hll_t* sketches = (ray_hll_t*)slab;
uint32_t* sparse = (uint32_t*)(slab + sketches_bytes);
uint8_t* dense = slab + sketches_bytes + sparse_bytes;
for (int64_t g = 0; g < n_groups; g++) {
ray_hll_init_sparse(&sketches[g], p,
sparse + (size_t)g * RAY_HLL_SPARSE_CAP,
RAY_HLL_SPARSE_CAP,
dense + (size_t)g * m);
}
banks[w] = sketches;
}
if (oom) {
for (uint32_t w = 0; w < nw; w++) {
if (slab_hdrs_array[w]) scratch_free(slab_hdrs_array[w]);
}
scratch_free(slab_hdrs_hdr);
scratch_free(banks_hdr);
return -1;
}
cda_pg_stream_ctx_t ctx = {
.banks = banks,
.vec = src,
.row_gid = row_gid,
.n_rows = n_rows,
.n_groups = n_groups,
.type = t,
.attrs = src->attrs,
.has_nulls = ray_vec_may_have_nulls(src),
.p = p,
.m = m,
};
if (nw > 1) {
ray_pool_dispatch(pool, cda_pg_stream_task, &ctx, n_rows);
} else {
cda_pg_stream_task(&ctx, 0, 0, n_rows);
}
for (int64_t g = 0; g < n_groups; g++) {
ray_hll_t* dst = &banks[0][g];
for (uint32_t w = 1; w < nw; w++) {
ray_hll_merge(dst, &banks[w][g]);
}
out[g] = ray_hll_estimate(dst);
}
for (uint32_t w = 0; w < nw; w++) {
for (int64_t g = 0; g < n_groups; g++) ray_hll_free(&banks[w][g]);
scratch_free(slab_hdrs_array[w]);
}
scratch_free(slab_hdrs_hdr);
scratch_free(banks_hdr);
return 0;
}