#if defined(__APPLE__)
# define _DARWIN_C_SOURCE
#elif !defined(_WIN32)
# define _GNU_SOURCE
#endif
#include "domain.h"
#include "core/platform.h"
#include "mem/heap.h"
#include "mem/sys.h"
#include "mem/arena.h"
#include "store/fileio.h"
#include "ops/hash.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <libgen.h>
#include <sys/stat.h>
#include <limits.h>
#ifndef PATH_MAX
# define PATH_MAX 4096
#endif
#define DOMAIN_STRL_MAGIC 0x4C525453U
typedef enum {
DOM_RUNTIME = 0,
DOM_FILE = 1,
} dom_kind_t;
typedef struct dom_retired_s {
void* ptr;
struct dom_retired_s* next;
} dom_retired_t;
struct ray_sym_domain_s {
uint8_t kind;
bool immortal;
uint32_t rc;
char* path;
void* map;
size_t map_size;
int64_t base_count;
int64_t disk_count;
size_t disk_size;
_Atomic(int64_t) count;
_Atomic(ray_t**) atoms;
int64_t atoms_cap;
ray_arena_t* arena;
size_t* offsets;
_Atomic(int64_t*) runtime_lut;
uint64_t* buckets;
uint64_t bucket_mask;
dom_retired_t* retired;
struct ray_sym_domain_s* next;
};
static struct ray_sym_domain_s g_runtime_domain = {
.kind = DOM_RUNTIME,
.immortal = true,
};
ray_sym_domain_t* ray_sym_runtime_domain(void) {
return &g_runtime_domain;
}
static ray_sym_domain_t* g_domains = NULL;
static _Atomic(int) g_dom_lock = 0;
static inline void dom_lock(void) {
while (atomic_exchange_explicit(&g_dom_lock, 1, memory_order_acquire)) {
RAY_CPU_RELAX();
}
}
static inline void dom_unlock(void) {
atomic_store_explicit(&g_dom_lock, 0, memory_order_release);
}
static char* dom_resolve_path(const char* path) {
char resolved[PATH_MAX];
if (realpath(path, resolved)) {
size_t n = strlen(resolved);
char* out = (char*)ray_sys_alloc(n + 1);
if (out) memcpy(out, resolved, n + 1);
return out;
}
char tmp[1024];
size_t plen = strlen(path);
if (plen == 0 || plen >= sizeof(tmp)) return NULL;
memcpy(tmp, path, plen + 1);
char* base = basename(tmp);
char bbuf[1024];
size_t blen = strlen(base);
if (blen >= sizeof(bbuf)) return NULL;
memcpy(bbuf, base, blen + 1);
memcpy(tmp, path, plen + 1);
char* dir = dirname(tmp);
char rdir[PATH_MAX];
if (!realpath(dir, rdir)) return NULL;
size_t dlen = strlen(rdir);
char* out = (char*)ray_sys_alloc(dlen + 1 + blen + 1);
if (!out) return NULL;
memcpy(out, rdir, dlen);
out[dlen] = '/';
memcpy(out + dlen + 1, bbuf, blen + 1);
return out;
}
static bool dom_retire(ray_sym_domain_t* d, void* p) {
if (!p) return true;
dom_retired_t* r = (dom_retired_t*)ray_sys_alloc(sizeof(*r));
if (!r) return false;
r->ptr = p;
r->next = d->retired;
d->retired = r;
return true;
}
static void dom_destroy(ray_sym_domain_t* d) {
ray_t** atoms = atomic_load_explicit(&d->atoms, memory_order_relaxed);
ray_sys_free(atoms);
if (d->arena) ray_arena_destroy(d->arena);
ray_sys_free(d->offsets);
ray_sys_free(atomic_load_explicit(&d->runtime_lut, memory_order_relaxed));
for (dom_retired_t* r = d->retired; r;) {
dom_retired_t* nxt = r->next;
ray_sys_free(r->ptr);
ray_sys_free(r);
r = nxt;
}
ray_sys_free(d->buckets);
if (d->map) ray_vm_unmap_file(d->map, d->map_size);
ray_sys_free(d->path);
ray_sys_free(d);
}
static bool dom_parse_strl_atoms(const void* map, size_t map_size,
ray_t*** out_atoms, size_t** out_offsets,
int64_t* out_count) {
const uint8_t* base = (const uint8_t*)map;
if (map_size < 12) return false;
uint32_t magic;
memcpy(&magic, base, 4);
if (magic != DOMAIN_STRL_MAGIC) return false;
int64_t count;
memcpy(&count, base + 4, 8);
if (count < 0 || count > UINT32_MAX) return false;
ray_t** atoms = NULL;
size_t* offsets = NULL;
if (count > 0) {
atoms = (ray_t**)ray_sys_alloc((size_t)((size_t)count) * (sizeof(ray_t*)));
if (!atoms) return false;
offsets = (size_t*)ray_sys_alloc((size_t)count * sizeof(size_t));
if (!offsets) { ray_sys_free(atoms); return false; }
}
size_t off = 12;
size_t remaining = map_size - 12;
for (int64_t i = 0; i < count; i++) {
offsets[i] = off;
uint32_t slen;
if (remaining < 4) goto fail;
memcpy(&slen, base + off, 4);
off += 4; remaining -= 4;
if ((size_t)slen > remaining) goto fail;
off += slen; remaining -= slen;
}
if (remaining != 0) goto fail;
*out_atoms = atoms;
*out_offsets = offsets;
*out_count = count;
return true;
fail:
ray_sys_free(atoms);
ray_sys_free(offsets);
return false;
}
static ray_t* dom_atom_at_locked(ray_sym_domain_t* d, int64_t pos) {
ray_t** atoms = atomic_load_explicit(&d->atoms, memory_order_relaxed);
ray_t* a = atomic_load_explicit((_Atomic(ray_t*)*)&atoms[pos],
memory_order_relaxed);
if (a) return a;
const uint8_t* base = (const uint8_t*)d->map;
size_t off = d->offsets[pos];
uint32_t slen;
memcpy(&slen, base + off, 4);
a = ray_arena_str(d->arena, (const char*)base + off + 4, slen);
if (!a) return NULL;
atomic_store_explicit((_Atomic(ray_t*)*)&atoms[pos], a, memory_order_release);
return a;
}
static bool dom_extend_from_file_locked(ray_sym_domain_t* d, size_t st_size) {
int64_t count = atomic_load_explicit(&d->count, memory_order_relaxed);
if (st_size < d->disk_size) return false;
if (count != d->disk_count) return false;
size_t map_size = 0;
void* map = ray_vm_map_file(d->path, &map_size);
if (!map) return false;
ray_t** fresh = NULL;
size_t* fresh_offsets = NULL;
int64_t fresh_count = 0;
if (!dom_parse_strl_atoms(map, map_size, &fresh, &fresh_offsets,
&fresh_count)) {
ray_vm_unmap_file(map, map_size);
return false;
}
bool ok = fresh_count >= count;
const uint8_t* nbase = (const uint8_t*)map;
const uint8_t* obase = (const uint8_t*)d->map;
#define DOM_REC(b, off, lp, pp) do { \
uint32_t _l; memcpy(&_l, (b) + (off), 4); \
(lp) = _l; (pp) = (const char*)((b) + (off) + 4); \
} while (0)
if (ok && count == 0 && fresh_count > 0) {
uint32_t l0; const char* p0; (void)p0;
DOM_REC(nbase, fresh_offsets[0], l0, p0);
if (l0 != 0) ok = false;
}
for (int64_t i = 0; ok && i < count; i++) {
uint32_t ol, nl; const char *op, *np;
DOM_REC(obase, d->offsets[i], ol, op);
DOM_REC(nbase, fresh_offsets[i], nl, np);
if (ol != nl || (ol > 0 && memcmp(op, np, ol) != 0)) ok = false;
}
#undef DOM_REC
if (ok && fresh_count > count) {
ray_t** atoms = atomic_load_explicit(&d->atoms, memory_order_relaxed);
ray_t** narr = (ray_t**)ray_sys_alloc((size_t)fresh_count * sizeof(ray_t*));
if (!narr) {
ok = false;
} else if (!dom_retire(d, atoms)) {
ray_sys_free(narr);
ok = false;
} else {
if (count > 0) memcpy(narr, atoms, (size_t)count * sizeof(ray_t*));
for (int64_t i = count; i < fresh_count; i++) narr[i] = NULL;
ray_sys_free(fresh);
ray_sys_free(d->offsets);
d->offsets = fresh_offsets;
void* old_map = d->map;
size_t old_map_size = d->map_size;
d->map = map;
d->map_size = map_size;
d->base_count = fresh_count;
atomic_store_explicit(&d->atoms, narr, memory_order_release);
d->atoms_cap = fresh_count;
atomic_store_explicit(&d->count, fresh_count, memory_order_release);
d->disk_count = fresh_count;
d->disk_size = st_size;
if (old_map) ray_vm_unmap_file(old_map, old_map_size);
if (d->buckets) { ray_sys_free(d->buckets); d->buckets = NULL; d->bucket_mask = 0; }
int64_t* lut = atomic_load_explicit(&d->runtime_lut, memory_order_relaxed);
if (lut) {
if (!dom_retire(d, lut)) {
fprintf(stderr, "rayforce: sym domain '%s': OOM retiring "
"runtime LUT after external extend — cannot "
"keep id translation consistent\n",
d->path ? d->path : "?");
abort();
}
atomic_store_explicit(&d->runtime_lut, NULL, memory_order_release);
}
return true;
}
}
if (ok && fresh_count == count) d->disk_size = st_size;
ray_sys_free(fresh);
ray_sys_free(fresh_offsets);
ray_vm_unmap_file(map, map_size);
return ok;
}
static ray_sym_domain_t* dom_open_impl(const char* path, bool create) {
if (!path) return NULL;
char* rpath = dom_resolve_path(path);
if (!rpath) return NULL;
struct stat st;
bool exists = (stat(rpath, &st) == 0);
if (!exists && !create) { ray_sys_free(rpath); return NULL; }
dom_lock();
for (ray_sym_domain_t* d = g_domains; d; d = d->next) {
if (strcmp(d->path, rpath) == 0) {
size_t cur_size = exists ? (size_t)st.st_size : 0;
if (cur_size != d->disk_size &&
!dom_extend_from_file_locked(d, cur_size)) {
dom_unlock();
ray_sys_free(rpath);
return NULL;
}
d->rc++;
dom_unlock();
ray_sys_free(rpath);
return d;
}
}
dom_unlock();
ray_sym_domain_t* d = (ray_sym_domain_t*)ray_sys_alloc((size_t)(1) * (sizeof(*d)));
if (!d) { ray_sys_free(rpath); return NULL; }
d->kind = DOM_FILE;
d->rc = 1;
d->path = rpath;
d->arena = ray_arena_new(64 * 1024);
if (!d->arena) { ray_sys_free(d); ray_sys_free(rpath); return NULL; }
if (exists) {
d->map = ray_vm_map_file(rpath, &d->map_size);
ray_t** atoms = NULL;
size_t* offsets = NULL;
int64_t count = 0;
if (!d->map ||
!dom_parse_strl_atoms(d->map, d->map_size, &atoms, &offsets, &count)) {
dom_destroy(d);
return NULL;
}
atomic_store_explicit(&d->atoms, atoms, memory_order_relaxed);
atomic_store_explicit(&d->count, count, memory_order_relaxed);
d->offsets = offsets;
d->atoms_cap = count;
d->base_count = count;
d->disk_count = count;
d->disk_size = d->map_size;
if (count > 0) {
uint32_t l0;
memcpy(&l0, (const uint8_t*)d->map + offsets[0], 4);
if (l0 != 0) {
dom_destroy(d);
return NULL;
}
}
}
dom_lock();
for (ray_sym_domain_t* e = g_domains; e; e = e->next) {
if (strcmp(e->path, d->path) == 0) {
e->rc++;
dom_unlock();
dom_destroy(d);
return e;
}
}
d->next = g_domains;
g_domains = d;
dom_unlock();
return d;
}
ray_sym_domain_t* ray_sym_domain_open(const char* path) {
return dom_open_impl(path, false);
}
ray_sym_domain_t* ray_sym_domain_open_or_create(const char* path) {
return dom_open_impl(path, true);
}
void ray_sym_domain_retain(ray_sym_domain_t* dom) {
if (!dom || dom->immortal) return;
dom_lock();
dom->rc++;
dom_unlock();
}
void ray_sym_domain_release(ray_sym_domain_t* dom) {
if (!dom || dom->immortal) return;
dom_lock();
if (--dom->rc > 0) {
dom_unlock();
return;
}
ray_sym_domain_t** pp = &g_domains;
while (*pp && *pp != dom) pp = &(*pp)->next;
if (*pp) *pp = dom->next;
dom_unlock();
dom_destroy(dom);
}
ray_t* ray_sym_domain_str(ray_sym_domain_t* dom, int64_t pos) {
if (!dom) return NULL;
if (dom->kind == DOM_RUNTIME) return ray_sym_str(pos);
int64_t count = atomic_load_explicit(&dom->count, memory_order_acquire);
if (pos < 0 || pos >= count) return NULL;
ray_t** atoms = atomic_load_explicit(&dom->atoms, memory_order_acquire);
ray_t* a = atomic_load_explicit((_Atomic(ray_t*)*)&atoms[pos],
memory_order_acquire);
if (a) return a;
dom_lock();
a = dom_atom_at_locked(dom, pos);
dom_unlock();
return a;
}
static const int64_t g_empty_lut[1] = { -1 };
const int64_t* ray_sym_domain_runtime_lut(ray_sym_domain_t* dom) {
if (!dom || dom->kind == DOM_RUNTIME) return NULL;
int64_t* lut = atomic_load_explicit(&dom->runtime_lut, memory_order_acquire);
if (lut) return lut;
if (atomic_load_explicit(&dom->count, memory_order_acquire) == 0)
return g_empty_lut;
dom_lock();
lut = atomic_load_explicit(&dom->runtime_lut, memory_order_relaxed);
if (!lut) {
int64_t count = atomic_load_explicit(&dom->count, memory_order_relaxed);
lut = (int64_t*)ray_sys_alloc((size_t)count * sizeof(int64_t));
if (!lut) { dom_unlock(); return NULL; }
for (int64_t i = 0; i < count; i++) {
ray_t* a = dom_atom_at_locked(dom, i);
if (!a) { ray_sys_free(lut); dom_unlock(); return NULL; }
lut[i] = ray_sym_intern(ray_str_ptr(a), ray_str_len(a));
if (lut[i] < 0) {
ray_sys_free(lut);
dom_unlock();
return NULL;
}
}
atomic_store_explicit(&dom->runtime_lut, lut, memory_order_release);
}
dom_unlock();
return lut;
}
static bool dom_build_index_locked(ray_sym_domain_t* d, int64_t extra) {
int64_t count = atomic_load_explicit(&d->count, memory_order_relaxed);
uint64_t need = (uint64_t)count + (uint64_t)extra;
uint64_t cap = 16;
while ((double)need > 0.7 * (double)cap) {
if (cap > (UINT64_MAX >> 1)) return false;
cap <<= 1;
}
uint64_t* buckets = (uint64_t*)ray_sys_alloc((size_t)((size_t)cap) * (sizeof(uint64_t)));
if (!buckets) return false;
uint64_t mask = cap - 1;
for (int64_t i = 0; i < count; i++) {
ray_t* a = dom_atom_at_locked(d, i);
if (!a) { ray_sys_free(buckets); return false; }
uint32_t h = (uint32_t)ray_hash_bytes(ray_str_ptr(a), ray_str_len(a));
uint64_t slot = h & mask;
while (buckets[slot] != 0) slot = (slot + 1) & mask;
buckets[slot] = ((uint64_t)h << 32) | ((uint64_t)(uint32_t)i + 1);
}
ray_sys_free(d->buckets);
d->buckets = buckets;
d->bucket_mask = mask;
return true;
}
static int64_t dom_probe_locked(ray_sym_domain_t* d, uint32_t h,
const char* str, size_t len) {
uint64_t mask = d->bucket_mask;
uint64_t slot = h & mask;
while (d->buckets[slot] != 0) {
uint64_t e = d->buckets[slot];
if ((uint32_t)(e >> 32) == h) {
int64_t pos = (int64_t)(uint32_t)e - 1;
ray_t* a = dom_atom_at_locked(d, pos);
if (a && ray_str_len(a) == len &&
(len == 0 || memcmp(ray_str_ptr(a), str, len) == 0))
return pos;
}
slot = (slot + 1) & mask;
}
return -1;
}
int64_t ray_sym_domain_find(ray_sym_domain_t* dom, const char* str, size_t len) {
if (!dom || !str) return -1;
if (dom->kind == DOM_RUNTIME) return ray_sym_find(str, len);
if (atomic_load_explicit(&dom->count, memory_order_acquire) == 0)
return -1;
dom_lock();
if (len == 0) {
ray_t* a0 = dom_atom_at_locked(dom, 0);
if (a0 && ray_str_len(a0) == 0) { dom_unlock(); return 0; }
}
if (!dom->buckets && !dom_build_index_locked(dom, 0)) {
dom_unlock();
return -1;
}
uint32_t h = (uint32_t)ray_hash_bytes(str, len);
int64_t found = dom_probe_locked(dom, h, str, len);
dom_unlock();
return found;
}
static int64_t dom_append_locked(ray_sym_domain_t* d, uint32_t h,
const char* str, size_t len) {
int64_t count = atomic_load_explicit(&d->count, memory_order_relaxed);
if (count >= (int64_t)UINT32_MAX) return -1;
if (count >= d->atoms_cap) {
int64_t ncap = d->atoms_cap < 8 ? 8 : d->atoms_cap * 2;
ray_t** narr = (ray_t**)ray_sys_alloc((size_t)ncap * sizeof(ray_t*));
if (!narr) return -1;
ray_t** old = atomic_load_explicit(&d->atoms, memory_order_relaxed);
if (count > 0) memcpy(narr, old, (size_t)count * sizeof(ray_t*));
if (!dom_retire(d, old)) { ray_sys_free(narr); return -1; }
atomic_store_explicit(&d->atoms, narr, memory_order_release);
d->atoms_cap = ncap;
}
uint64_t cap = d->bucket_mask + 1;
if ((double)(count + 1) > 0.7 * (double)cap &&
!dom_build_index_locked(d, 1))
return -1;
ray_t* s = ray_arena_str(d->arena, str, len);
if (!s) return -1;
ray_t** atoms = atomic_load_explicit(&d->atoms, memory_order_relaxed);
atoms[count] = s;
atomic_store_explicit(&d->count, count + 1, memory_order_release);
uint64_t mask = d->bucket_mask;
uint64_t slot = h & mask;
while (d->buckets[slot] != 0) slot = (slot + 1) & mask;
d->buckets[slot] = ((uint64_t)h << 32) | ((uint64_t)(uint32_t)count + 1);
int64_t* lut = atomic_load_explicit(&d->runtime_lut, memory_order_relaxed);
if (lut) {
if (!dom_retire(d, lut)) {
fprintf(stderr, "rayforce: sym domain '%s': OOM retiring runtime "
"LUT after append — cannot keep id translation "
"consistent\n", d->path ? d->path : "?");
abort();
}
atomic_store_explicit(&d->runtime_lut, NULL, memory_order_release);
}
return count;
}
int64_t ray_sym_domain_intern(ray_sym_domain_t* dom, const char* str, size_t len) {
if (!dom || !str) return -1;
if (dom->kind == DOM_RUNTIME) return ray_sym_intern(str, len);
dom_lock();
if (!dom->buckets && !dom_build_index_locked(dom, 1)) {
dom_unlock();
return -1;
}
if (atomic_load_explicit(&dom->count, memory_order_relaxed) == 0 &&
len != 0) {
uint32_t h0 = (uint32_t)ray_hash_bytes("", 0);
if (dom_append_locked(dom, h0, "", 0) != 0) {
dom_unlock();
return -1;
}
}
uint32_t h = (uint32_t)ray_hash_bytes(str, len);
int64_t pos = dom_probe_locked(dom, h, str, len);
if (pos < 0) pos = dom_append_locked(dom, h, str, len);
dom_unlock();
return pos;
}
int64_t ray_sym_domain_count(ray_sym_domain_t* dom) {
if (!dom) return 0;
if (dom->kind == DOM_RUNTIME) return (int64_t)ray_sym_count();
return atomic_load_explicit(&dom->count, memory_order_acquire);
}
const char* ray_sym_domain_path(ray_sym_domain_t* dom) {
if (!dom || dom->kind == DOM_RUNTIME) return NULL;
return dom->path;
}
ray_err_t ray_sym_domain_flush(ray_sym_domain_t* dom, bool durable) {
if (!dom) return RAY_ERR_TYPE;
if (dom->kind == DOM_RUNTIME) return RAY_OK;
dom_lock();
int64_t count = atomic_load_explicit(&dom->count, memory_order_relaxed);
int64_t disk_count = dom->disk_count;
size_t disk_size = dom->disk_size;
if (count != disk_count) {
for (int64_t i = 0; i < count; i++) {
if (!dom_atom_at_locked(dom, i)) { dom_unlock(); return RAY_ERR_OOM; }
}
}
ray_t** atoms = atomic_load_explicit(&dom->atoms, memory_order_relaxed);
dom_unlock();
if (count == disk_count) return RAY_OK;
char lock_path[1024];
char tmp_path[1024];
if (snprintf(lock_path, sizeof(lock_path), "%s.lk", dom->path) >= (int)sizeof(lock_path))
return RAY_ERR_IO;
if (snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", dom->path) >= (int)sizeof(tmp_path))
return RAY_ERR_IO;
ray_fd_t lock_fd = ray_file_open(lock_path,
RAY_OPEN_READ | RAY_OPEN_WRITE | RAY_OPEN_CREATE);
if (lock_fd == RAY_FD_INVALID) return RAY_ERR_IO;
ray_err_t err = ray_file_lock_ex(lock_fd);
if (err != RAY_OK) { ray_file_close(lock_fd); return err; }
{
struct stat st;
bool exists = (stat(dom->path, &st) == 0);
if (disk_count == 0) {
if (exists && st.st_size > 0) {
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return RAY_ERR_CORRUPT;
}
} else {
bool ok = exists && (size_t)st.st_size == disk_size;
if (ok) {
FILE* vf = fopen(dom->path, "rb");
uint32_t magic = 0;
int64_t fcount = -1;
if (!vf || fread(&magic, 4, 1, vf) != 1 ||
fread(&fcount, 8, 1, vf) != 1 ||
magic != DOMAIN_STRL_MAGIC || fcount != disk_count)
ok = false;
if (vf) fclose(vf);
}
if (!ok) {
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return RAY_ERR_CORRUPT;
}
}
}
size_t written_size = 0;
{
FILE* f = fopen(tmp_path, "wb");
if (!f) {
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return RAY_ERR_IO;
}
uint32_t magic = DOMAIN_STRL_MAGIC;
err = RAY_OK;
if (fwrite(&magic, 4, 1, f) != 1 || fwrite(&count, 8, 1, f) != 1)
err = RAY_ERR_IO;
written_size = 12;
for (int64_t i = 0; err == RAY_OK && i < count; i++) {
ray_t* s = atoms[i];
size_t slen = ray_str_len(s);
if (slen > UINT32_MAX) { err = RAY_ERR_RANGE; break; }
uint32_t len32 = (uint32_t)slen;
if (fwrite(&len32, 4, 1, f) != 1 ||
(slen > 0 && fwrite(ray_str_ptr(s), 1, slen, f) != slen)) {
err = RAY_ERR_IO;
break;
}
written_size += 4 + slen;
}
if (fclose(f) != 0 && err == RAY_OK) err = RAY_ERR_IO;
}
if (err != RAY_OK) goto fail_tmp;
if (durable) {
ray_fd_t tmp_fd = ray_file_open(tmp_path, RAY_OPEN_READ | RAY_OPEN_WRITE);
if (tmp_fd == RAY_FD_INVALID) { err = RAY_ERR_IO; goto fail_tmp; }
err = ray_file_sync(tmp_fd);
ray_file_close(tmp_fd);
if (err != RAY_OK) goto fail_tmp;
}
err = ray_file_rename(tmp_path, dom->path);
if (err != RAY_OK) goto fail_tmp;
if (durable) {
err = ray_file_sync_dir(dom->path);
if (err != RAY_OK) {
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return err;
}
}
dom_lock();
dom->disk_count = count;
dom->disk_size = written_size;
dom_unlock();
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return RAY_OK;
fail_tmp:
remove(tmp_path);
ray_file_unlock(lock_fd);
ray_file_close(lock_fd);
return err;
}
uint8_t ray_g_sym_audit = 0;
void ray_sym_audit_cell(ray_t* vec, int64_t row, int64_t pos, ray_t* resolved) {
ray_sym_domain_t* dom = ray_sym_vec_domain(vec);
int64_t count = ray_sym_domain_count(dom);
if (resolved != NULL && pos >= 0 && pos < count) return;
fprintf(stderr,
"rayforce: RAY_SYM_AUDIT violation: vec=%p type=%d attrs=0x%02x "
"len=%lld row=%lld pos=%lld domain=%p (%s) count=%lld resolved=%p\n",
(void*)vec, (int)vec->type, (unsigned)vec->attrs,
(long long)vec->len, (long long)row, (long long)pos,
(void*)dom,
dom == ray_sym_runtime_domain() ? "runtime"
: (ray_sym_domain_path(dom) ? ray_sym_domain_path(dom) : "file"),
(long long)count, (void*)resolved);
abort();
}