#include "col.h"
#include "core/platform.h"
#include "mem/heap.h"
#include "mem/sys.h"
#include "store/serde.h"
#include "store/fileio.h"
#include "table/sym.h"
#include "table/domain.h"
#include "table/dict.h"
#include "ops/idxop.h"
#include "vec/str.h"
#include "lang/format.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdatomic.h>
static ray_err_t validate_sym_bounds(const void* data, int64_t len,
uint8_t attrs, uint32_t sym_count) {
if (sym_count == 0 || len == 0) return RAY_OK;
uint64_t max_id = 0;
switch (attrs & RAY_SYM_W_MASK) {
case RAY_SYM_W8: {
const uint8_t* p = (const uint8_t*)data;
for (int64_t i = 0; i < len; i++)
if (p[i] > max_id) max_id = p[i];
break;
}
case RAY_SYM_W16: {
const uint16_t* p = (const uint16_t*)data;
for (int64_t i = 0; i < len; i++)
if (p[i] > max_id) max_id = p[i];
break;
}
case RAY_SYM_W32: {
const uint32_t* p = (const uint32_t*)data;
for (int64_t i = 0; i < len; i++)
if (p[i] > max_id) max_id = p[i];
break;
}
case RAY_SYM_W64: {
const int64_t* p = (const int64_t*)data;
for (int64_t i = 0; i < len; i++) {
if (p[i] < 0) return RAY_ERR_CORRUPT;
if ((uint64_t)p[i] > max_id) max_id = (uint64_t)p[i];
}
break;
}
default:
return RAY_ERR_CORRUPT;
}
if (max_id >= sym_count) return RAY_ERR_CORRUPT;
return RAY_OK;
}
#define STR_LIST_MAGIC 0x4C525453U
#define STR_VEC_MAGIC 0x56525453U
#define LIST_MAGIC 0x4754534CU
#define TABLE_MAGIC 0x4C425454U
static size_t col_str_pool_payload_len(const ray_t* vec);
#define COL_DISK_ATTRS_MASK \
((uint8_t)(RAY_SYM_W_MASK | RAY_ATTR_SORTED | RAY_ATTR_HAS_NULLS))
#define COL_IDX_AUX_MAGIC ((uint32_t)0x58444958)
static bool is_serializable_type(int8_t t) {
switch (t) {
case RAY_BOOL: case RAY_U8: case RAY_I16:
case RAY_I32: case RAY_I64: case RAY_F32: case RAY_F64:
case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP: case RAY_GUID:
case RAY_SYM: case RAY_STR:
return true;
default:
return false;
}
}
static ray_err_t col_preflight_recursive(ray_t* obj) {
if (!obj || RAY_IS_ERR(obj)) return RAY_ERR_TYPE;
int8_t type = obj->type;
if (type < 0 || is_serializable_type(type)) return RAY_OK;
if (type == RAY_LIST) {
ray_t** slots = (ray_t**)ray_data(obj);
for (int64_t i = 0; i < obj->len; i++) {
ray_err_t err = col_preflight_recursive(slots[i]);
if (err != RAY_OK) return err;
}
return RAY_OK;
}
if (type == RAY_TABLE) {
int64_t ncols = ray_table_ncols(obj);
for (int64_t c = 0; c < ncols; c++) {
ray_err_t err = col_preflight_recursive(
ray_table_get_col_idx(obj, c));
if (err != RAY_OK) return err;
}
return RAY_OK;
}
if (type == RAY_DICT) {
ray_t* keys = ray_dict_keys(obj);
ray_t* vals = ray_dict_vals(obj);
ray_err_t err = col_preflight_recursive(keys);
return err == RAY_OK ? col_preflight_recursive(vals) : err;
}
return RAY_ERR_NYI;
}
ray_err_t ray_col_save_preflight(ray_t* vec) {
if (!vec || RAY_IS_ERR(vec)) return RAY_ERR_TYPE;
if (is_serializable_type(vec->type)) return RAY_OK;
if (vec->type == RAY_LIST || vec->type == RAY_TABLE)
return col_preflight_recursive(vec);
return RAY_ERR_NYI;
}
static bool is_str_list(ray_t* v) {
if (!v || RAY_IS_ERR(v)) return false;
if (v->type != RAY_LIST) return false;
ray_t** slots = (ray_t**)ray_data(v);
for (int64_t i = 0; i < v->len; i++) {
ray_t* elem = slots[i];
if (!elem || RAY_IS_ERR(elem)) return false;
if (elem->type != -RAY_STR) return false;
}
return true;
}
static ray_err_t col_save_str_list(ray_t* list, FILE* f) {
uint32_t magic = STR_LIST_MAGIC;
if (fwrite(&magic, 4, 1, f) != 1) return RAY_ERR_IO;
int64_t count = list->len;
if (fwrite(&count, 8, 1, f) != 1) return RAY_ERR_IO;
ray_t** slots = (ray_t**)ray_data(list);
for (int64_t i = 0; i < count; i++) {
ray_t* s = slots[i];
const char* sp = ray_str_ptr(s);
size_t slen = ray_str_len(s);
uint32_t len32 = (uint32_t)slen;
if (fwrite(&len32, 4, 1, f) != 1) return RAY_ERR_IO;
if (slen > 0 && fwrite(sp, 1, slen, f) != slen) return RAY_ERR_IO;
}
return RAY_OK;
}
static ray_t* col_load_str_list(const uint8_t* ptr, size_t remaining) {
if (remaining < 8) return ray_error("corrupt", NULL);
int64_t count;
memcpy(&count, ptr, 8);
ptr += 8; remaining -= 8;
if (count < 0 || (uint64_t)count > remaining / 4)
return ray_error("corrupt", NULL);
ray_t* list = ray_list_new(count);
if (!list || RAY_IS_ERR(list)) return list;
for (int64_t i = 0; i < count; i++) {
if (remaining < 4) { ray_release(list); return ray_error("corrupt", NULL); }
uint32_t slen;
memcpy(&slen, ptr, 4);
ptr += 4; remaining -= 4;
if (slen > remaining) { ray_release(list); return ray_error("corrupt", NULL); }
ray_t* s = ray_str((const char*)ptr, (size_t)slen);
if (!s || RAY_IS_ERR(s)) { ray_release(list); return s; }
ptr += slen; remaining -= slen;
list = ray_list_append(list, s);
ray_release(s);
if (!list || RAY_IS_ERR(list)) return list;
}
return list;
}
static ray_t* col_load_str_vec(const uint8_t* ptr, size_t remaining) {
if (remaining > (size_t)INT64_MAX) return ray_error("range", "col load str-vec: payload size exceeds int64 max, got %zu bytes", remaining);
int64_t len = (int64_t)remaining;
ray_t* result = ray_de_raw((uint8_t*)ptr, &len);
if (!result || RAY_IS_ERR(result)) return result;
if (result->type != RAY_STR) {
const char* rt = ray_type_name(result->type);
ray_release(result);
return ray_error("type", "col load str-vec: decoded payload is not a str vector, got %s", rt);
}
return result;
}
static ray_err_t col_write_recursive(ray_t* obj, FILE* f);
static ray_err_t col_write_recursive(ray_t* obj, FILE* f) {
if (!obj || RAY_IS_ERR(obj)) return RAY_ERR_TYPE;
int8_t type = obj->type;
if (fwrite(&type, 1, 1, f) != 1) return RAY_ERR_IO;
if (type < 0) {
if (type == -RAY_STR || type == -RAY_SYM) {
ray_t* s = (type == -RAY_SYM) ? ray_sym_str(obj->i64) : obj;
if (!s) return RAY_ERR_CORRUPT;
const char* sp = ray_str_ptr(s);
size_t slen = ray_str_len(s);
uint32_t len32 = (uint32_t)slen;
if (fwrite(&len32, 4, 1, f) != 1) return RAY_ERR_IO;
if (slen > 0 && fwrite(sp, 1, slen, f) != slen) return RAY_ERR_IO;
} else {
if (fwrite(&obj->i64, 8, 1, f) != 1) return RAY_ERR_IO;
}
return RAY_OK;
}
if (type == RAY_SYM) {
int64_t len = obj->len;
if (fwrite(&len, 8, 1, f) != 1) return RAY_ERR_IO;
for (int64_t i = 0; i < len; i++) {
ray_t* s = ray_sym_vec_cell(obj, i);
if (!s) return RAY_ERR_CORRUPT;
const char* sp = ray_str_ptr(s);
size_t slen = ray_str_len(s);
uint32_t len32 = (uint32_t)slen;
if (fwrite(&len32, 4, 1, f) != 1) return RAY_ERR_IO;
if (slen > 0 && fwrite(sp, 1, slen, f) != slen) return RAY_ERR_IO;
}
return RAY_OK;
}
if (is_serializable_type(type)) {
int64_t len = obj->len;
if (fwrite(&len, 8, 1, f) != 1) return RAY_ERR_IO;
if (type == RAY_STR) {
uint8_t attrs = obj->attrs;
if (fwrite(&attrs, 1, 1, f) != 1) return RAY_ERR_IO;
}
uint8_t esz = ray_sym_elem_size(type, obj->attrs);
size_t data_size = (size_t)len * esz;
if (data_size > 0 && fwrite(ray_data(obj), 1, data_size, f) != data_size)
return RAY_ERR_IO;
if (type == RAY_STR) {
uint64_t pool_size = (uint64_t)col_str_pool_payload_len(obj);
if (fwrite(&pool_size, 8, 1, f) != 1) return RAY_ERR_IO;
if (pool_size > 0 &&
fwrite(ray_data(obj->str_pool), 1, (size_t)pool_size, f) != pool_size)
return RAY_ERR_IO;
}
return RAY_OK;
}
if (type == RAY_LIST) {
int64_t count = obj->len;
if (fwrite(&count, 8, 1, f) != 1) return RAY_ERR_IO;
ray_t** slots = (ray_t**)ray_data(obj);
for (int64_t i = 0; i < count; i++) {
ray_err_t err = col_write_recursive(slots[i], f);
if (err != RAY_OK) return err;
}
return RAY_OK;
}
if (type == RAY_TABLE) {
int64_t ncols = ray_table_ncols(obj);
int64_t nrows = ray_table_nrows(obj);
if (fwrite(&ncols, 8, 1, f) != 1) return RAY_ERR_IO;
if (fwrite(&nrows, 8, 1, f) != 1) return RAY_ERR_IO;
for (int64_t c = 0; c < ncols; c++) {
int64_t name_sym = ray_table_col_name(obj, c);
if (fwrite(&name_sym, 8, 1, f) != 1) return RAY_ERR_IO;
ray_t* col = ray_table_get_col_idx(obj, c);
ray_err_t err = col_write_recursive(col, f);
if (err != RAY_OK) return err;
}
return RAY_OK;
}
if (type == RAY_DICT) {
ray_t* keys = ray_dict_keys(obj);
ray_t* vals = ray_dict_vals(obj);
ray_err_t err = col_write_recursive(keys, f);
return err == RAY_OK ? col_write_recursive(vals, f) : err;
}
return RAY_ERR_NYI;
}
static ray_t* col_read_recursive(const uint8_t** pp, size_t* remaining);
static ray_t* col_read_recursive(const uint8_t** pp, size_t* remaining) {
if (*remaining < 1) return ray_error("corrupt", NULL);
int8_t type;
memcpy(&type, *pp, 1);
*pp += 1; *remaining -= 1;
if (type < 0) {
if (type == -RAY_STR || type == -RAY_SYM) {
if (*remaining < 4) return ray_error("corrupt", NULL);
uint32_t slen;
memcpy(&slen, *pp, 4);
*pp += 4; *remaining -= 4;
if (slen > *remaining) return ray_error("corrupt", NULL);
if (type == -RAY_SYM) {
int64_t id = ray_sym_intern((const char*)*pp, (size_t)slen);
*pp += slen; *remaining -= slen;
if (id < 0) return ray_error("oom", NULL);
return ray_sym(id);
}
ray_t* s = ray_str((const char*)*pp, (size_t)slen);
*pp += slen; *remaining -= slen;
return s;
} else {
if (*remaining < 8) return ray_error("corrupt", NULL);
int64_t val;
memcpy(&val, *pp, 8);
*pp += 8; *remaining -= 8;
ray_t* atom = ray_alloc(0);
if (!atom || RAY_IS_ERR(atom)) return atom;
atom->type = type;
atom->i64 = val;
return atom;
}
}
if (type == RAY_SYM) {
if (*remaining < 8) return ray_error("corrupt", NULL);
int64_t len;
memcpy(&len, *pp, 8);
*pp += 8; *remaining -= 8;
if (len < 0 || (uint64_t)len > *remaining / 4)
return ray_error("corrupt", NULL);
ray_t* vec = ray_sym_vec_new(RAY_SYM_W64, len);
if (!vec || RAY_IS_ERR(vec)) return vec;
vec->len = len;
int64_t* out = (int64_t*)ray_data(vec);
for (int64_t i = 0; i < len; i++) {
if (*remaining < 4) { ray_release(vec); return ray_error("corrupt", NULL); }
uint32_t slen;
memcpy(&slen, *pp, 4);
*pp += 4; *remaining -= 4;
if (slen > *remaining) { ray_release(vec); return ray_error("corrupt", NULL); }
int64_t id = ray_sym_intern((const char*)*pp, (size_t)slen);
*pp += slen; *remaining -= slen;
if (id < 0) { ray_release(vec); return ray_error("oom", NULL); }
out[i] = id;
}
return vec;
}
if (is_serializable_type(type)) {
if (*remaining < 8) return ray_error("corrupt", NULL);
int64_t len;
memcpy(&len, *pp, 8);
*pp += 8; *remaining -= 8;
if (len < 0) return ray_error("corrupt", NULL);
uint8_t attrs = 0;
if (type == RAY_STR) {
if (*remaining < 1) return ray_error("corrupt", NULL);
memcpy(&attrs, *pp, 1);
*pp += 1; *remaining -= 1;
}
uint8_t esz = ray_sym_elem_size(type, attrs);
if (esz > 0 && (uint64_t)len > SIZE_MAX / esz)
return ray_error("corrupt", NULL);
size_t data_size = (size_t)len * esz;
if (data_size > *remaining) return ray_error("corrupt", NULL);
ray_t* vec = ray_vec_new(type, len);
if (!vec || RAY_IS_ERR(vec)) return vec;
vec->len = len;
if (data_size > 0)
memcpy(ray_data(vec), *pp, data_size);
*pp += data_size; *remaining -= data_size;
if (type == RAY_STR) {
if (*remaining < 8) { ray_release(vec); return ray_error("corrupt", NULL); }
uint64_t pool_size;
memcpy(&pool_size, *pp, 8);
*pp += 8; *remaining -= 8;
if (pool_size > *remaining || pool_size > (uint64_t)INT64_MAX) {
ray_release(vec);
return ray_error("corrupt", NULL);
}
if (pool_size > 0) {
vec->str_pool = ray_alloc((size_t)pool_size);
if (!vec->str_pool || RAY_IS_ERR(vec->str_pool)) {
ray_t* err = vec->str_pool ? vec->str_pool : ray_error("oom", NULL);
vec->str_pool = NULL;
ray_release(vec);
return err;
}
vec->str_pool->type = RAY_U8;
vec->str_pool->len = (int64_t)pool_size;
memcpy(ray_data(vec->str_pool), *pp, (size_t)pool_size);
}
*pp += pool_size; *remaining -= (size_t)pool_size;
vec->attrs = attrs & COL_DISK_ATTRS_MASK;
}
return vec;
}
if (type == RAY_LIST) {
if (*remaining < 8) return ray_error("corrupt", NULL);
int64_t count;
memcpy(&count, *pp, 8);
*pp += 8; *remaining -= 8;
if (count < 0) return ray_error("corrupt", NULL);
ray_t* list = ray_list_new(count);
if (!list || RAY_IS_ERR(list)) return list;
for (int64_t i = 0; i < count; i++) {
ray_t* elem = col_read_recursive(pp, remaining);
if (!elem || RAY_IS_ERR(elem)) { ray_release(list); return elem; }
list = ray_list_append(list, elem);
ray_release(elem);
if (!list || RAY_IS_ERR(list)) return list;
}
return list;
}
if (type == RAY_TABLE) {
if (*remaining < 16) return ray_error("corrupt", NULL);
int64_t ncols, nrows;
memcpy(&ncols, *pp, 8);
*pp += 8; *remaining -= 8;
memcpy(&nrows, *pp, 8);
*pp += 8; *remaining -= 8;
(void)nrows;
if (ncols < 0) return ray_error("corrupt", NULL);
ray_t* tbl = ray_table_new(ncols);
if (!tbl || RAY_IS_ERR(tbl)) return tbl;
for (int64_t c = 0; c < ncols; c++) {
if (*remaining < 8) { ray_release(tbl); return ray_error("corrupt", NULL); }
int64_t name_sym;
memcpy(&name_sym, *pp, 8);
*pp += 8; *remaining -= 8;
ray_t* col = col_read_recursive(pp, remaining);
if (!col || RAY_IS_ERR(col)) { ray_release(tbl); return col; }
tbl = ray_table_add_col(tbl, name_sym, col);
ray_release(col);
if (!tbl || RAY_IS_ERR(tbl)) return tbl;
}
ray_t* shape_err = ray_table_validate_rectangular(tbl, "col load table");
if (shape_err) {
ray_release(tbl);
return shape_err;
}
return tbl;
}
if (type == RAY_DICT) {
ray_t* keys = col_read_recursive(pp, remaining);
if (!keys || RAY_IS_ERR(keys)) return keys;
ray_t* vals = col_read_recursive(pp, remaining);
if (!vals || RAY_IS_ERR(vals)) {
ray_release(keys);
return vals;
}
return ray_dict_new(keys, vals);
}
return ray_error("nyi", NULL);
}
static ray_err_t col_save_list(ray_t* list, FILE* f) {
uint32_t magic = LIST_MAGIC;
if (fwrite(&magic, 4, 1, f) != 1) return RAY_ERR_IO;
return col_write_recursive(list, f);
}
static ray_err_t col_save_table(ray_t* tbl, FILE* f) {
uint32_t magic = TABLE_MAGIC;
if (fwrite(&magic, 4, 1, f) != 1) return RAY_ERR_IO;
return col_write_recursive(tbl, f);
}
static void try_load_link_sidecar(ray_t* vec, const char* path) {
if (!vec || (vec->type != RAY_I32 && vec->type != RAY_I64)) return;
char link_path[1024];
size_t plen = strlen(path);
if (plen + 6 >= sizeof(link_path)) return;
memcpy(link_path, path, plen);
memcpy(link_path + plen, ".link", 6);
FILE* lf = fopen(link_path, "rb");
if (!lf) return;
if (fseek(lf, 0, SEEK_END) != 0) { fclose(lf); return; }
long fsz = ftell(lf);
if (fsz <= 0 || fsz > (1L << 20)) { fclose(lf); return; }
rewind(lf);
char* buf = (char*)ray_alloc_raw((size_t)fsz);
if (!buf) { fclose(lf); return; }
size_t n = fread(buf, 1, (size_t)fsz, lf);
fclose(lf);
if (n != (size_t)fsz) { ray_free_raw(buf); return; }
while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r'
|| buf[n-1] == ' ' || buf[n-1] == '\t'
|| buf[n-1] == '\0')) n--;
if (n == 0) { ray_free_raw(buf); return; }
int64_t target_sym = ray_sym_intern(buf, n);
ray_free_raw(buf);
if (target_sym < 0) return;
vec->link_target = target_sym;
vec->attrs |= RAY_ATTR_HAS_LINK;
}
ray_err_t ray_col_append_index(const char* path, const void* ix_v,
int64_t col_len, int8_t col_type) {
const ray_index_t* ix = (const ray_index_t*)ix_v;
if (!path || !ix) return RAY_ERR_DOMAIN;
(void)col_len; (void)col_type;
FILE* f = fopen(path, "r+b");
if (!f) return RAY_ERR_IO;
uint32_t cur_mg = 0;
if (fread(&cur_mg, 1, 4, f) == 4 && cur_mg == COL_IDX_AUX_MAGIC) {
fclose(f); return RAY_ERR_CORRUPT;
}
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return RAY_ERR_IO; }
long fsz = ftell(f);
if (fsz < 32) { fclose(f); return RAY_ERR_CORRUPT; }
int64_t payload_end = fsz;
int64_t region_off = (payload_end + 31) & ~(int64_t)31;
int64_t pad = region_off - payload_end;
static const uint8_t zeros[32] = {0};
if (pad > 0 && fwrite(zeros, 1, (size_t)pad, f) != (size_t)pad) { fclose(f); return RAY_ERR_IO; }
int64_t rsize = ray_index_inline_size(ix);
uint8_t* rbuf = (uint8_t*)ray_calloc_raw((size_t)(1) * ((size_t)rsize));
if (!rbuf) { fclose(f); return RAY_ERR_OOM; }
ray_index_inline_write(rbuf, ix);
size_t rw = fwrite(rbuf, 1, (size_t)rsize, f);
ray_free_raw(rbuf);
if (rw != (size_t)rsize) { fclose(f); return RAY_ERR_IO; }
if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return RAY_ERR_IO; }
uint32_t mg = COL_IDX_AUX_MAGIC;
if (fwrite(&mg, 1, 4, f) != 4) { fclose(f); return RAY_ERR_IO; }
if (fclose(f) != 0) return RAY_ERR_IO;
return RAY_OK;
}
static bool col_payload_has_nulls(ray_t* vec) {
int64_t n = vec->len;
if (n <= 0) return false;
const void* p = ray_data(vec);
switch (vec->type) {
case RAY_F64: {
const double* d = (const double*)p;
for (int64_t i = 0; i < n; i++) if (d[i] != d[i]) return true;
return false;
}
case RAY_F32: {
const float* d = (const float*)p;
for (int64_t i = 0; i < n; i++) if (d[i] != d[i]) return true;
return false;
}
case RAY_I64: case RAY_TIMESTAMP: {
const int64_t* d = (const int64_t*)p;
for (int64_t i = 0; i < n; i++) if (d[i] == NULL_I64) return true;
return false;
}
case RAY_I32: case RAY_DATE: case RAY_TIME: {
const int32_t* d = (const int32_t*)p;
for (int64_t i = 0; i < n; i++) if (d[i] == NULL_I32) return true;
return false;
}
case RAY_I16: {
const int16_t* d = (const int16_t*)p;
for (int64_t i = 0; i < n; i++) if (d[i] == NULL_I16) return true;
return false;
}
default: return false;
}
}
static ray_err_t col_save_impl(ray_t* vec, const char* path, bool durable) {
if (!vec || RAY_IS_ERR(vec)) return RAY_ERR_TYPE;
if (!path) return RAY_ERR_IO;
char tmp_path[1024];
if (snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", path) >= (int)sizeof(tmp_path))
return RAY_ERR_IO;
if (is_str_list(vec)) {
FILE* f = fopen(tmp_path, "wb");
if (!f) return RAY_ERR_IO;
ray_err_t err = col_save_str_list(vec, f);
fclose(f);
if (err != RAY_OK) { remove(tmp_path); return err; }
goto fsync_and_rename;
}
if (vec->type == RAY_LIST) {
FILE* f = fopen(tmp_path, "wb");
if (!f) return RAY_ERR_IO;
ray_err_t err = col_save_list(vec, f);
fclose(f);
if (err != RAY_OK) { remove(tmp_path); return err; }
goto fsync_and_rename;
}
if (vec->type == RAY_TABLE) {
FILE* f = fopen(tmp_path, "wb");
if (!f) return RAY_ERR_IO;
ray_err_t err = col_save_table(vec, f);
fclose(f);
if (err != RAY_OK) { remove(tmp_path); return err; }
goto fsync_and_rename;
}
if (!is_serializable_type(vec->type))
return RAY_ERR_NYI;
void* sym_xlate = NULL;
uint8_t sym_xlate_attrs = 0;
if (vec->type == RAY_SYM &&
ray_sym_vec_domain(vec) != ray_sym_runtime_domain()) {
struct ray_sym_domain_s* dom = ray_sym_vec_domain(vec);
const int64_t* lut = ray_sym_domain_runtime_lut(dom);
if (!lut) return RAY_ERR_OOM;
int64_t dcount = ray_sym_domain_count(dom);
uint8_t w = ray_sym_dict_width((int64_t)ray_sym_count());
uint8_t wesz = (uint8_t)(1u << w);
if (vec->len > 0 && (uint64_t)vec->len > SIZE_MAX / wesz)
return RAY_ERR_IO;
sym_xlate = ray_alloc_raw(vec->len > 0 ? (size_t)vec->len * wesz : 1);
if (!sym_xlate) return RAY_ERR_OOM;
const void* src = ray_data(vec);
for (int64_t i = 0; i < vec->len; i++) {
int64_t pos = ray_read_sym(src, i, RAY_SYM, vec->attrs);
if (pos < 0 || pos >= dcount) {
ray_free_raw(sym_xlate);
return RAY_ERR_CORRUPT;
}
ray_write_sym(sym_xlate, i, (uint64_t)lut[pos], RAY_SYM, w);
}
sym_xlate_attrs = w;
}
{
FILE* f = fopen(tmp_path, "wb");
if (!f) { ray_free_raw(sym_xlate); return RAY_ERR_IO; }
ray_t header;
memcpy(&header, vec, 32);
header.mmod = 0;
ray_col_stamp_format(&header);
header.rc = (vec->type == RAY_SYM) ? ray_sym_count() : 0;
bool persist_index = false;
if (vec->attrs & RAY_ATTR_HAS_INDEX) {
ray_index_t* ix = ray_index_payload(vec->index);
header.attrs &= ~RAY_ATTR_HAS_INDEX;
memcpy(header.aux, ix->saved_aux, 16);
persist_index = (vec->index != NULL) && vec->type != RAY_STR;
}
if (vec->attrs & RAY_ATTR_HAS_LINK) {
header.attrs &= (uint8_t)~RAY_ATTR_HAS_LINK;
memset(header.aux + 8, 0, 8);
}
if (vec->attrs & RAY_ATTR_SLICE) {
header.attrs &= (uint8_t)~RAY_ATTR_SLICE;
memset(header.aux, 0, 16);
}
if (!(header.attrs & RAY_ATTR_HAS_NULLS) && col_payload_has_nulls(vec))
header.attrs |= RAY_ATTR_HAS_NULLS;
if (!(header.attrs & RAY_ATTR_HAS_NULLS))
memset(header.aux, 0, 16);
if (vec->type == RAY_SYM)
memset(header.aux, 0, 16);
if (sym_xlate)
header.attrs = (uint8_t)((header.attrs &
~(uint8_t)(RAY_SYM_W_MASK | RAY_ATTR_SORTED)) | sym_xlate_attrs);
if (persist_index) {
memset(header.aux, 0, 16);
uint32_t mg = COL_IDX_AUX_MAGIC;
memcpy(header.aux, &mg, 4);
}
size_t written = fwrite(&header, 1, 32, f);
if (written != 32) { fclose(f); remove(tmp_path); ray_free_raw(sym_xlate); return RAY_ERR_IO; }
if (vec->len < 0) { fclose(f); remove(tmp_path); ray_free_raw(sym_xlate); return RAY_ERR_CORRUPT; }
uint8_t esz = ray_sym_elem_size(vec->type,
sym_xlate ? sym_xlate_attrs : vec->attrs);
if (esz == 0 && vec->len > 0) { fclose(f); remove(tmp_path); ray_free_raw(sym_xlate); return RAY_ERR_TYPE; }
if ((uint64_t)vec->len > (SIZE_MAX - 32) / (esz ? esz : 1)) {
fclose(f);
remove(tmp_path);
ray_free_raw(sym_xlate);
return RAY_ERR_IO;
}
size_t data_size = (size_t)vec->len * esz;
void* data;
if (sym_xlate) {
data = sym_xlate;
} else if (vec->attrs & RAY_ATTR_SLICE) {
ray_t* parent = vec->slice_parent;
if (!parent || vec->slice_offset < 0 ||
vec->slice_offset + vec->len > parent->len) {
fclose(f);
remove(tmp_path);
return RAY_ERR_IO;
}
data = (char*)ray_data(parent) + vec->slice_offset * esz;
} else {
data = ray_data(vec);
}
if (data_size > 0) {
written = fwrite(data, 1, data_size, f);
if (written != data_size) { fclose(f); remove(tmp_path); ray_free_raw(sym_xlate); return RAY_ERR_IO; }
}
ray_free_raw(sym_xlate);
sym_xlate = NULL;
if (vec->type == RAY_STR) {
size_t pool_size = col_str_pool_payload_len(vec);
ray_t pool_header;
memset(&pool_header, 0, sizeof(pool_header));
if (vec->str_pool && !RAY_IS_ERR(vec->str_pool)) {
memcpy(&pool_header, vec->str_pool, 32);
memset(pool_header.aux, 0, 16);
}
pool_header.mmod = 0;
pool_header.order = 0;
pool_header.type = RAY_U8;
pool_header.attrs = 0;
pool_header.rc = 0;
pool_header.len = (int64_t)pool_size;
written = fwrite(&pool_header, 1, 32, f);
if (written != 32) { fclose(f); remove(tmp_path); return RAY_ERR_IO; }
if (pool_size > 0) {
if (!vec->str_pool || RAY_IS_ERR(vec->str_pool)) {
fclose(f);
remove(tmp_path);
return RAY_ERR_CORRUPT;
}
written = fwrite(ray_data(vec->str_pool), 1, pool_size, f);
if (written != pool_size) { fclose(f); remove(tmp_path); return RAY_ERR_IO; }
}
}
if (persist_index && vec->type != RAY_STR) {
int64_t payload_end = 32 + (int64_t)data_size;
int64_t region_off = (payload_end + 31) & ~(int64_t)31;
int64_t pad = region_off - payload_end;
static const uint8_t zeros[32] = {0};
if (pad > 0 && fwrite(zeros, 1, (size_t)pad, f) != (size_t)pad) {
fclose(f); remove(tmp_path); return RAY_ERR_IO;
}
ray_index_t* ix = ray_index_payload(vec->index);
int64_t rsize = ray_index_inline_size(ix);
uint8_t* rbuf = (uint8_t*)ray_calloc_raw((size_t)(1) * ((size_t)rsize));
if (!rbuf) { fclose(f); remove(tmp_path); return RAY_ERR_OOM; }
ray_index_inline_write(rbuf, ix);
size_t rw = fwrite(rbuf, 1, (size_t)rsize, f);
ray_free_raw(rbuf);
if (rw != (size_t)rsize) { fclose(f); remove(tmp_path); return RAY_ERR_IO; }
}
fclose(f);
}
fsync_and_rename:;
ray_err_t err = RAY_OK;
if (durable) {
ray_fd_t tmp_fd = ray_file_open(tmp_path, RAY_OPEN_READ | RAY_OPEN_WRITE);
if (tmp_fd == RAY_FD_INVALID) { remove(tmp_path); return RAY_ERR_IO; }
err = ray_file_sync(tmp_fd);
ray_file_close(tmp_fd);
if (err != RAY_OK) { remove(tmp_path); return err; }
}
err = ray_file_rename(tmp_path, path);
if (err != RAY_OK) { remove(tmp_path); return err; }
if (durable) {
err = ray_file_sync_dir(path);
if (err != RAY_OK) return err;
}
{
char link_path[1024];
size_t plen = strlen(path);
if (plen + 6 < sizeof(link_path)) {
memcpy(link_path, path, plen);
memcpy(link_path + plen, ".link", 6);
if (vec->attrs & RAY_ATTR_HAS_LINK) {
ray_t* sym_str = ray_sym_str(vec->link_target);
const char* sp = sym_str ? ray_str_ptr(sym_str) : NULL;
size_t slen = sym_str ? ray_str_len(sym_str) : 0;
if (sp && slen > 0) {
char tmp_link[1024];
memcpy(tmp_link, link_path, plen + 6);
if (plen + 10 < sizeof(tmp_link)) {
memcpy(tmp_link + plen + 5, ".tmp", 5);
FILE* lf = fopen(tmp_link, "wb");
if (lf) {
size_t wrote = fwrite(sp, 1, slen, lf);
fclose(lf);
if (wrote == slen) {
if (ray_file_rename(tmp_link, link_path) == RAY_OK
&& durable)
(void)ray_file_sync_dir(link_path);
} else {
remove(tmp_link);
}
}
}
}
} else {
remove(link_path);
}
}
}
return RAY_OK;
}
ray_err_t ray_col_save(ray_t* vec, const char* path) {
return col_save_impl(vec, path, true);
}
ray_err_t ray_col_save_bulk(ray_t* vec, const char* path) {
return col_save_impl(vec, path, false);
}
ray_err_t ray_col_save_sym_encoded(ray_t* vec, const char* path,
struct ray_sym_domain_s* target,
bool durable) {
if (!vec || RAY_IS_ERR(vec) || vec->type != RAY_SYM) return RAY_ERR_TYPE;
if (!path || !target) return RAY_ERR_IO;
if (vec->len < 0) return RAY_ERR_CORRUPT;
struct ray_sym_domain_s* src = ray_sym_vec_domain(vec);
int64_t dcount = ray_sym_domain_count(target);
if (dcount > UINT32_MAX) return RAY_ERR_RANGE;
uint8_t w = ray_sym_dict_width(dcount);
uint8_t esz = (uint8_t)(1u << w);
char tmp_path[1024];
if (snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", path) >= (int)sizeof(tmp_path))
return RAY_ERR_IO;
FILE* f = fopen(tmp_path, "wb");
if (!f) return RAY_ERR_IO;
ray_err_t err = RAY_OK;
{
ray_t header;
memcpy(&header, vec, 32);
header.mmod = 0;
ray_col_stamp_format(&header);
header.rc = (uint32_t)dcount;
uint8_t keep_sorted = (src == target) ? (vec->attrs & RAY_ATTR_SORTED) : 0;
header.attrs = (uint8_t)(w | keep_sorted |
(vec->attrs & RAY_ATTR_HAS_NULLS));
memset(header.aux, 0, 16);
if (fwrite(&header, 1, 32, f) != 32) err = RAY_ERR_IO;
}
if (err == RAY_OK) {
uint8_t buf[8192];
int64_t per = (int64_t)(sizeof(buf) / esz);
const void* sdata = ray_data(vec);
for (int64_t off = 0; err == RAY_OK && off < vec->len; off += per) {
int64_t cnt = vec->len - off;
if (cnt > per) cnt = per;
for (int64_t i = 0; i < cnt; i++) {
int64_t pos = ray_read_sym(sdata, off + i, RAY_SYM, vec->attrs);
int64_t tpos;
if (src == target) {
tpos = pos;
if (tpos < 0 || tpos >= dcount) { err = RAY_ERR_CORRUPT; break; }
} else {
ray_t* s = ray_sym_domain_str(src, pos);
if (!s) { err = RAY_ERR_CORRUPT; break; }
tpos = ray_sym_domain_find(target, ray_str_ptr(s),
ray_str_len(s));
if (tpos < 0) { err = RAY_ERR_CORRUPT; break; }
}
ray_write_sym(buf, i, (uint64_t)tpos, RAY_SYM, w);
}
if (err == RAY_OK && cnt > 0 &&
fwrite(buf, esz, (size_t)cnt, f) != (size_t)cnt)
err = RAY_ERR_IO;
}
}
if (fclose(f) != 0 && err == RAY_OK) err = RAY_ERR_IO;
if (err != RAY_OK) { remove(tmp_path); return err; }
if (durable) {
ray_fd_t tmp_fd = ray_file_open(tmp_path, RAY_OPEN_READ | RAY_OPEN_WRITE);
if (tmp_fd == RAY_FD_INVALID) { remove(tmp_path); return RAY_ERR_IO; }
err = ray_file_sync(tmp_fd);
ray_file_close(tmp_fd);
if (err != RAY_OK) { remove(tmp_path); return err; }
}
err = ray_file_rename(tmp_path, path);
if (err != RAY_OK) { remove(tmp_path); return err; }
if (durable) {
err = ray_file_sync_dir(path);
if (err != RAY_OK) return err;
}
{
char link_path[1024];
size_t plen = strlen(path);
if (plen + 6 < sizeof(link_path)) {
memcpy(link_path, path, plen);
memcpy(link_path + plen, ".link", 6);
remove(link_path);
}
}
return RAY_OK;
}
typedef struct {
void* mapped;
size_t mapped_size;
ray_t* header;
uint8_t esz;
size_t data_size;
bool has_str_pool;
size_t str_pool_offset;
size_t str_pool_size;
size_t tail_offset;
uint32_t saved_sym_count;
bool has_index;
size_t index_offset;
} col_mapped_t;
static size_t col_str_pool_payload_len(const ray_t* vec) {
if (!vec || vec->type != RAY_STR || !vec->str_pool || RAY_IS_ERR(vec->str_pool))
return 0;
return vec->str_pool->len > 0 ? (size_t)vec->str_pool->len : 0;
}
static ray_t* col_copy_str_pool(const col_mapped_t* cm) {
if (!cm->has_str_pool) return NULL;
const ray_t* src = (const ray_t*)((const char*)cm->mapped + cm->str_pool_offset);
if (cm->str_pool_size == 0) return NULL;
ray_t* pool = ray_alloc(cm->str_pool_size);
if (!pool || RAY_IS_ERR(pool)) return pool ? pool : ray_error("oom", NULL);
uint8_t saved_order = pool->order;
uint8_t saved_mmod = pool->mmod;
memcpy(pool, src, 32 + cm->str_pool_size);
pool->order = saved_order;
pool->mmod = saved_mmod;
pool->attrs &= COL_DISK_ATTRS_MASK;
ray_atomic_store(&pool->rc, 1);
return pool;
}
static ray_err_t col_validate_str_region(ray_t* hdr, const void* ptr,
size_t mapped_size, col_mapped_t* out) {
size_t offset = 32 + out->data_size;
if (offset > mapped_size || mapped_size - offset < 32)
return RAY_ERR_CORRUPT;
ray_t* pool = (ray_t*)((char*)ptr + offset);
if (pool->type != RAY_U8 || pool->len < 0)
return RAY_ERR_CORRUPT;
size_t pool_size = (size_t)pool->len;
if (pool_size > mapped_size - offset - 32)
return RAY_ERR_CORRUPT;
ray_str_t* elems = (ray_str_t*)((char*)ptr + 32);
const char* pool_base = (const char*)ptr + offset + 32;
for (int64_t i = 0; i < hdr->len; i++) {
uint32_t len = elems[i].len;
if (len <= RAY_STR_INLINE_MAX) continue;
if (pool_size == 0 || elems[i].pool_off > pool_size ||
len > pool_size - elems[i].pool_off)
return RAY_ERR_CORRUPT;
if (len >= 4) {
const char* p = pool_base + elems[i].pool_off;
if (memcmp(elems[i].prefix, p, 4) != 0)
return RAY_ERR_CORRUPT;
}
}
for (int64_t i = 0; i < hdr->len; i++) {
if (ray_str_is_inline(&elems[i])) continue;
uint32_t h = (uint32_t)ray_str_t_hash(&elems[i], pool_base);
elems[i].hash32 = h != 0 ? h : 1u;
}
out->has_str_pool = true;
out->str_pool_offset = offset;
out->str_pool_size = pool_size;
out->tail_offset = offset + 32 + pool_size;
return RAY_OK;
}
static ray_t* col_validate_mapped(const char* path, col_mapped_t* out) {
size_t mapped_size = 0;
void* ptr = ray_vm_map_file(path, &mapped_size);
if (!ptr) return ray_error("io", NULL);
if (mapped_size >= 4) {
uint32_t magic;
memcpy(&magic, ptr, 4);
if (magic == STR_LIST_MAGIC || magic == STR_VEC_MAGIC ||
magic == LIST_MAGIC || magic == TABLE_MAGIC) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("nyi", NULL);
}
}
if (mapped_size < 32) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("corrupt", NULL);
}
ray_t* hdr = (ray_t*)ptr;
if (ray_col_check_format(hdr) != RAY_OK) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("version", "col %s: bad format version", path);
}
if (!is_serializable_type(hdr->type)) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("nyi", NULL);
}
if (hdr->len < 0) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("corrupt", NULL);
}
uint8_t esz = ray_sym_elem_size(hdr->type, hdr->attrs);
if (esz == 0 && hdr->len > 0) {
const char* ht = ray_type_name(hdr->type);
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("type", "col mmap %s: on-disk type %s has no fixed element size for %lld rows",
path, ht, (long long)hdr->len);
}
if ((uint64_t)hdr->len > (SIZE_MAX - 32) / (esz ? esz : 1)) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("io", NULL);
}
size_t data_size = (size_t)hdr->len * esz;
if (32 + data_size > mapped_size) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error("corrupt", NULL);
}
out->data_size = data_size;
if (hdr->type == RAY_STR) {
ray_err_t se = col_validate_str_region(hdr, ptr, mapped_size, out);
if (se != RAY_OK) {
ray_vm_unmap_file(ptr, mapped_size);
return ray_error(ray_err_code_str(se), NULL);
}
} else {
out->has_str_pool = false;
out->str_pool_offset = 0;
out->str_pool_size = 0;
out->tail_offset = 32 + data_size;
}
if (hdr->type == RAY_SYM) {
uint32_t saved_sc;
memcpy(&saved_sc, (const char*)ptr + offsetof(ray_t, rc), sizeof(saved_sc));
out->saved_sym_count = saved_sc;
}
out->has_index = false;
out->index_offset = 0;
{
uint32_t idxmg;
memcpy(&idxmg, ptr, 4);
if (idxmg == COL_IDX_AUX_MAGIC) {
size_t region_off = (out->tail_offset + 31) & ~(size_t)31;
if (region_off + 32 + sizeof(ray_index_t) <= mapped_size) {
out->has_index = true;
out->index_offset = region_off;
}
}
}
out->mapped = ptr;
out->mapped_size = mapped_size;
out->header = hdr;
out->esz = esz;
out->data_size = data_size;
return NULL;
}
static ray_t* col_load_impl(const char* path, struct ray_sym_domain_s* dom,
bool require_dom) {
if (!path) return ray_error("io", NULL);
size_t mapped_size = 0;
void* ptr = ray_vm_map_file(path, &mapped_size);
if (!ptr) return ray_error("io", NULL);
if (mapped_size >= 4) {
uint32_t magic;
memcpy(&magic, ptr, 4);
if (magic == STR_LIST_MAGIC) {
ray_t* result = col_load_str_list((const uint8_t*)ptr + 4, mapped_size - 4);
ray_vm_unmap_file(ptr, mapped_size);
return result;
}
if (magic == STR_VEC_MAGIC) {
ray_t* result = col_load_str_vec((const uint8_t*)ptr + 4, mapped_size - 4);
ray_vm_unmap_file(ptr, mapped_size);
return result;
}
if (magic == LIST_MAGIC || magic == TABLE_MAGIC) {
const uint8_t* p = (const uint8_t*)ptr + 4;
size_t rem = mapped_size - 4;
ray_t* result = col_read_recursive(&p, &rem);
ray_vm_unmap_file(ptr, mapped_size);
return result;
}
}
ray_vm_unmap_file(ptr, mapped_size);
col_mapped_t cm = {0};
ray_t* err = col_validate_mapped(path, &cm);
if (err) return err;
ray_t* vec = ray_alloc(cm.data_size);
if (!vec || RAY_IS_ERR(vec)) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return vec ? vec : ray_error("oom", NULL);
}
uint8_t saved_order = vec->order;
memcpy(vec, cm.mapped, 32 + cm.data_size);
memset(vec->aux, 0, 16);
if (vec->type == RAY_STR) {
ray_t* pool = col_copy_str_pool(&cm);
if (pool && RAY_IS_ERR(pool)) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
ray_free(vec);
return pool;
}
vec->str_pool = pool;
}
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
vec->mmod = 0;
vec->order = saved_order;
vec->attrs &= COL_DISK_ATTRS_MASK;
ray_atomic_store(&vec->rc, 1);
if (vec->type == RAY_SYM) {
if (dom) {
vec->sym_domain = dom;
ray_sym_domain_retain(dom);
int64_t dcount = ray_sym_domain_count(dom);
if (cm.saved_sym_count > 0 && (int64_t)cm.saved_sym_count > dcount) {
ray_release(vec);
return ray_error("corrupt",
"column %s: saved sym count %u exceeds symfile count %lld",
path, cm.saved_sym_count, (long long)dcount);
}
uint32_t bound = dcount > UINT32_MAX ? UINT32_MAX : (uint32_t)dcount;
ray_err_t sym_err = validate_sym_bounds(ray_data(vec), vec->len,
vec->attrs, bound);
if (sym_err != RAY_OK) {
ray_release(vec);
return ray_error(ray_err_code_str(sym_err), NULL);
}
} else if (require_dom) {
vec->sym_domain = ray_sym_runtime_domain();
ray_release(vec);
return ray_error("sym",
"column %s: SYM data but no symfile resolved "
"(missing <dir>/sym or explicit sym argument)", path);
} else {
vec->sym_domain = ray_sym_runtime_domain();
uint32_t cur_sc = ray_sym_count();
if (cm.saved_sym_count > 0 && cur_sc > 0 && cur_sc < cm.saved_sym_count) {
ray_release(vec);
return ray_error("corrupt", NULL);
}
ray_err_t sym_err = validate_sym_bounds(ray_data(vec), vec->len,
vec->attrs, cur_sc);
if (sym_err != RAY_OK) {
ray_release(vec);
return ray_error(ray_err_code_str(sym_err), NULL);
}
}
}
try_load_link_sidecar(vec, path);
return vec;
}
ray_t* ray_col_load(const char* path) {
return col_load_impl(path, NULL, false);
}
ray_t* ray_col_load_dom(const char* path, struct ray_sym_domain_s* dom) {
return col_load_impl(path, dom, true);
}
static ray_t* col_mmap_impl(const char* path, struct ray_sym_domain_s* dom,
bool require_dom) {
if (!path) return ray_error("io", NULL);
col_mapped_t cm = {0};
ray_t* err = col_validate_mapped(path, &cm);
if (err) return err;
if (!cm.has_index && cm.tail_offset != cm.mapped_size) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error("io", NULL);
}
ray_t* vec = cm.header;
if (vec->type == RAY_SYM) {
if (dom) {
int64_t dcount = ray_sym_domain_count(dom);
if (cm.saved_sym_count > 0 && (int64_t)cm.saved_sym_count > dcount) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error("corrupt",
"column %s: saved sym count %u exceeds symfile count %lld",
path, cm.saved_sym_count, (long long)dcount);
}
if (cm.saved_sym_count == 0) {
uint32_t bound = dcount > UINT32_MAX ? UINT32_MAX
: (uint32_t)dcount;
ray_err_t sym_err = validate_sym_bounds(
(const char*)cm.mapped + 32, vec->len, vec->attrs, bound);
if (sym_err != RAY_OK) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error(ray_err_code_str(sym_err), NULL);
}
}
} else if (require_dom) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error("sym",
"column %s: SYM data but no symfile resolved "
"(missing <dir>/sym or explicit sym argument)", path);
} else {
uint32_t cur_sc = ray_sym_count();
if (cm.saved_sym_count > 0 && cur_sc > 0 &&
cur_sc < cm.saved_sym_count) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error("corrupt", NULL);
}
ray_err_t sym_err = validate_sym_bounds(
(const char*)cm.mapped + 32, vec->len, vec->attrs, cur_sc);
if (sym_err != RAY_OK) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error(ray_err_code_str(sym_err), NULL);
}
}
}
vec->mmod = 1;
vec->order = 0;
vec->attrs &= COL_DISK_ATTRS_MASK;
ray_atomic_store(&vec->rc, 1);
memset(vec->aux, 0, 16);
if (vec->type == RAY_STR) {
ray_t* pool = (ray_t*)((char*)cm.mapped + cm.str_pool_offset);
pool->order = 0;
pool->attrs &= COL_DISK_ATTRS_MASK;
ray_atomic_store(&pool->rc, 1);
ray_file_map_t* m = (ray_file_map_t*)ray_sys_alloc(sizeof(*m));
if (!m) {
ray_vm_unmap_file(cm.mapped, cm.mapped_size);
return ray_error("oom", NULL);
}
m->base = cm.mapped;
m->len = cm.mapped_size;
m->rc = 2;
m->next = NULL;
pool->mmod = 3;
pool->file_map = m;
vec->str_pool = pool;
}
if (vec->type == RAY_SYM) {
if (dom) {
vec->sym_domain = dom;
ray_sym_domain_retain(dom);
} else {
vec->sym_domain = ray_sym_runtime_domain();
}
}
try_load_link_sidecar(vec, path);
if (cm.has_index) {
ray_t* idx = ray_index_inline_map((uint8_t*)cm.mapped + cm.index_offset);
if (idx) {
ray_t* r = ray_index_attach_built(&vec, idx);
if (r && !RAY_IS_ERR(r)) vec = r;
}
}
return vec;
}
ray_t* ray_col_mmap(const char* path) {
return col_mmap_impl(path, NULL, false);
}
ray_t* ray_col_mmap_splayed_dom(const char* path, struct ray_sym_domain_s* dom) {
return col_mmap_impl(path, dom, true);
}