Core C API Reference
Lifecycle, memory management, atoms, vectors, string vectors, lists, tables, and error handling. All functions are declared in include/rayforce.h.
<rayforce.h> to access the entire public API. Link against librayforce.a (static) or librayforce.so (shared). No external dependencies.
Lifecycle
Every program must initialize the heap allocator and symbol table before using any other API. Destroy them in reverse order when done.
ray_heap_init
ray_heap_destroy
ray_t objects.ray_sym_init
ray_heap_init() and before interning any symbols, loading CSV files, or creating tables with symbol columns. Returns RAY_OK on success.ray_sym_destroy
ray_heap_destroy().Typical lifecycle pattern
#include <rayforce.h>
int main(void) {
ray_heap_init();
ray_sym_init();
/* ... use Rayforce API ... */
ray_sym_destroy();
ray_heap_destroy();
return 0;
}
Memory Management
Rayforce uses its own buddy allocator with slab cache. Never use malloc/free for ray_t objects. All objects use COW (copy-on-write) ref counting for safe sharing.
ray_alloc
ray_t block with at least data_size bytes of data space (beyond the 32-byte header). Uses the slab cache for common sizes, buddy allocator for larger blocks. The returned block has rc = 1. Returns NULL on OOM.ray_free
ray_t block to the allocator. Supports cross-thread free: blocks freed from a non-owning thread are enqueued to a lock-free LIFO and reclaimed when the owning heap flushes foreign blocks. Do not call directly on shared objects — use ray_release() instead.ray_retain
ray_t object. Call when storing an additional reference to an object (e.g., adding a column to a table while keeping the original pointer).ray_release
rc reaches zero, the object and any owned children are freed. This is the primary way to dispose of ray_t objects. Safe to call on NULL.ray_cow
v is the sole owner (rc == 1), returns v unchanged. Otherwise, creates a deep copy and returns it with rc = 1. The original is not modified. After ray_cow(), if the returned pointer differs from the original, the caller must release it on error paths.COW pattern
ray_t* original = ray_vec_new(RAY_I64, 100);
ray_retain(original); /* rc = 2: shared */
ray_t* writable = ray_cow(original);
/* writable != original (copy was made because rc > 1) */
/* Now safe to mutate writable without affecting original */
/* On error paths, release the copy: */
if (writable != original) ray_release(writable);
Atom Constructors
Atoms are scalar values stored in the ray_t header (no trailing data). Each constructor returns a heap-allocated ray_t* with rc = 1 and a negative type field indicating an atom.
ray_bool
b8 field of the header.ray_u8
ray_i16
ray_i32
ray_i64
ray_f64
ray_str
s.ray_sym
ray_sym_intern() to get the ID first. The symbol references the global intern table; the atom itself stores only the integer ID.ray_date
ray_time
ray_timestamp
ray_guid
Vector API
Vectors are the fundamental columnar container. Each vector has a typed element array following the 32-byte header. All vector operations process data in 1024-element morsels.
ray_vec_new
ray_vec_append
elem pointer must point to a value of the correct type and size. May reallocate if capacity is exceeded, returning a new pointer. The old pointer may be invalidated.ray_vec_set
idx. The index must be in range [0, len). Uses COW internally: if the vector is shared (rc > 1), a copy is made first. Returns the (possibly new) vector pointer.ray_vec_get
idx. The returned pointer is valid until the vector is reallocated or freed. Returns NULL if idx is out of range.ray_vec_slice
offset to offset + len. The slice retains the parent (incrementing its rc), so the parent must not be freed while the slice exists. Mutations require COW.ray_vec_concat
a followed by all elements of b. Both vectors must have the same type. Returns a new vector with rc = 1.ray_vec_from_raw
count elements from a raw C array. The data is copied; the caller retains ownership of the source array.Null bitmap operations
/* Set element at index 5 as null */
ray_vec_set_null(vec, 5, true);
/* Check if element is null */
if (ray_vec_is_null(vec, 5)) { /* ... */ }
/* Checked version returns RAY_ERR_RANGE on out-of-bounds */
ray_err_t err = ray_vec_set_null_checked(vec, idx, true);
String Vector API
RAY_STR vectors have a specialized API because elements are variable-length. These functions handle the SSO/pool layout transparently.
ray_str_vec_append
ray_str_vec_get
idx and writes the length to *out_len. The returned pointer is valid until the vector is modified. Returns NULL if the element is null or index is out of range. The string is not null-terminated.ray_str_vec_set
idx. Uses COW if the vector is shared. The old pool data is not reclaimed immediately; use ray_str_vec_compact() to reclaim unused pool space.ray_str_vec_compact
ray_str_vec_set() calls that leave dead pool entries.List API
Lists are heterogeneous containers holding ray_t* pointers. Used for table column lists, function argument lists, and mixed-type collections.
ray_list_new
capacity items.ray_list_append
ray_list_get
idx. Does not increment the reference count. Returns NULL if out of range.ray_list_set
idx. The old item is released and the new item is retained.Table API
Tables are the primary data structure: a list of named columns (vectors) with a shared row count. Column names are symbol IDs from the global intern table.
ray_table_new
ncols columns. Columns are added with ray_table_add_col().ray_table_add_col
name_id is a symbol ID (from ray_sym_intern()). The column vector is retained. Returns the table pointer (may reallocate).ray_table_get_col
NULL if not found. Does not increment the reference count.ray_table_ncols
ray_table_nrows
Complete table example
ray_heap_init();
ray_sym_init();
/* Create a 3-column table */
ray_t* ids = ray_vec_from_raw(RAY_I64, (int64_t[]){1, 2, 3}, 3);
ray_t* prices = ray_vec_from_raw(RAY_F64, (double[]){9.99, 19.99, 29.99}, 3);
ray_t* names = ray_vec_new(RAY_STR, 3);
names = ray_str_vec_append(names, "Widget", 6);
names = ray_str_vec_append(names, "Gadget", 6);
names = ray_str_vec_append(names, "Doohickey", 9);
ray_t* tbl = ray_table_new(3);
tbl = ray_table_add_col(tbl, ray_sym_intern("id", 2), ids);
tbl = ray_table_add_col(tbl, ray_sym_intern("price", 5), prices);
tbl = ray_table_add_col(tbl, ray_sym_intern("name", 4), names);
printf("cols: %lld, rows: %lld\n",
ray_table_ncols(tbl), ray_table_nrows(tbl));
/* Output: cols: 3, rows: 3 */
ray_release(ids);
ray_release(prices);
ray_release(names);
ray_release(tbl);
ray_sym_destroy();
ray_heap_destroy();
Error Handling
Rayforce uses two error mechanisms: ray_err_t enum return codes for non-pointer functions, and error objects (RAY_ERROR) for pointer-returning functions.
ray_error
ray_t with type == RAY_ERROR. Use RAY_IS_ERR() to test return values.RAY_IS_ERR
ray_t* is an error object. Safe to call on NULL. Returns true if p is non-null and has type == RAY_ERROR.Error codes
| Code | Enum | Description |
|---|---|---|
RAY_OK | 0 | Success |
RAY_ERR_OOM | 1 | Out of memory |
RAY_ERR_TYPE | 2 | Type mismatch |
RAY_ERR_RANGE | 3 | Index out of range |
RAY_ERR_LENGTH | 4 | Length mismatch |
RAY_ERR_RANK | 5 | Rank error |
RAY_ERR_DOMAIN | 6 | Domain error (invalid value) |
RAY_ERR_NYI | 7 | Not yet implemented |
RAY_ERR_IO | 8 | I/O error |
RAY_ERR_SCHEMA | 9 | Schema mismatch |
RAY_ERR_CORRUPT | 10 | Data corruption |
RAY_ERR_CANCEL | 11 | Operation cancelled |
RAY_ERR_PARSE | 12 | Parse error |
RAY_ERR_NAME | 13 | Name not found |
RAY_ERR_LIMIT | 14 | Limit exceeded |