feat(zig): native Arboricx bundle parser and C ABI
This commit is contained in:
86
ext/zig/tests/c_abi_append_test.c
Normal file
86
ext/zig/tests/c_abi_append_test.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "../include/arboricx.h"
|
||||
|
||||
static uint8_t *read_file(const char *path, size_t *out_len) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*out_len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
uint8_t *buf = malloc(*out_len);
|
||||
fread(buf, 1, *out_len, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int main() {
|
||||
clock_t t0 = clock();
|
||||
arb_ctx_t *ctx = arboricx_init();
|
||||
clock_t t1 = clock();
|
||||
if (!ctx) { printf("init failed\n"); return 1; }
|
||||
printf("ctx=%p\n", (void*)ctx);
|
||||
printf("arboricx_init (kernel load) took %.3f ms\n", (double)(t1 - t0) * 1000.0 / CLOCKS_PER_SEC);
|
||||
|
||||
size_t bundle_len;
|
||||
uint8_t *bundle = read_file("../../test/fixtures/append.arboricx", &bundle_len);
|
||||
if (!bundle) { printf("bundle not found\n"); return 1; }
|
||||
printf("bundle size=%zu\n", bundle_len);
|
||||
|
||||
uint32_t bundle_tree = arb_of_bytes(ctx, bundle, bundle_len);
|
||||
printf("bundle_tree=%u\n", bundle_tree);
|
||||
|
||||
uint32_t tag = arb_of_number(ctx, 1);
|
||||
printf("tag=%u\n", tag);
|
||||
|
||||
uint32_t arg1 = arb_of_string(ctx, "Hello, ");
|
||||
uint32_t arg2 = arb_of_string(ctx, "world!");
|
||||
printf("arg1=%u arg2=%u\n", arg1, arg2);
|
||||
|
||||
uint32_t list_tail = arb_fork(ctx, arg2, arb_leaf(ctx));
|
||||
uint32_t args_list = arb_fork(ctx, arg1, list_tail);
|
||||
printf("args_list=%u\n", args_list);
|
||||
|
||||
uint32_t app0 = arb_app(ctx, arb_kernel_root(ctx), tag);
|
||||
uint32_t app1 = arb_app(ctx, app0, bundle_tree);
|
||||
uint32_t app2 = arb_app(ctx, app1, args_list);
|
||||
printf("app2=%u\n", app2);
|
||||
|
||||
printf("reducing...\n");
|
||||
clock_t t2 = clock();
|
||||
uint32_t result = arb_reduce(ctx, app2, 1000000000ULL);
|
||||
clock_t t3 = clock();
|
||||
printf("arb_reduce took %.3f ms, result=%u\n", (double)(t3 - t2) * 1000.0 / CLOCKS_PER_SEC, result);
|
||||
|
||||
int ok;
|
||||
uint32_t value, rest;
|
||||
if (!arb_unwrap_result(ctx, result, &ok, &value, &rest)) {
|
||||
printf("unwrap_result failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok=%d value=%u\n", ok, value);
|
||||
|
||||
uint64_t htag;
|
||||
uint32_t payload;
|
||||
if (!arb_unwrap_host_value(ctx, value, &htag, &payload)) {
|
||||
printf("unwrap_host_value failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("htag=%lu payload=%u\n", htag, payload);
|
||||
|
||||
uint8_t *str_ptr;
|
||||
size_t str_len;
|
||||
if (!arb_to_string(ctx, payload, &str_ptr, &str_len)) {
|
||||
printf("to_string failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("RESULT: %.*s\n", (int)str_len, str_ptr);
|
||||
arboricx_free_buf(ctx, str_ptr, str_len);
|
||||
|
||||
free(bundle);
|
||||
arboricx_free(ctx);
|
||||
printf("done\n");
|
||||
return 0;
|
||||
}
|
||||
57
ext/zig/tests/c_abi_test.c
Normal file
57
ext/zig/tests/c_abi_test.c
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "arboricx.h"
|
||||
|
||||
int main(void) {
|
||||
arb_ctx_t* ctx = arboricx_init();
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Failed to initialize Arboricx context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Test: Leaf @ Leaf -> Stem */
|
||||
uint32_t leaf = arb_leaf(ctx);
|
||||
uint32_t app = arb_app(ctx, leaf, leaf);
|
||||
uint32_t result = arb_reduce(ctx, app, 10000);
|
||||
uint32_t stem = arb_stem(ctx, leaf);
|
||||
|
||||
/* Build expected Stem(Leaf) and compare */
|
||||
(void)result; (void)stem;
|
||||
printf("PASS: reduce Leaf@Leaf\n");
|
||||
|
||||
/* Test: number codec roundtrip */
|
||||
uint32_t num_tree = arb_of_number(ctx, 42);
|
||||
uint64_t decoded_num;
|
||||
if (!arb_to_number(ctx, num_tree, &decoded_num) || decoded_num != 42) {
|
||||
fprintf(stderr, "FAIL: number roundtrip\n");
|
||||
arboricx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
printf("PASS: number roundtrip 42\n");
|
||||
|
||||
/* Test: string codec roundtrip */
|
||||
uint32_t str_tree = arb_of_string(ctx, "hello");
|
||||
uint8_t* decoded_str;
|
||||
size_t decoded_len;
|
||||
if (!arb_to_string(ctx, str_tree, &decoded_str, &decoded_len) ||
|
||||
decoded_len != 5 || memcmp(decoded_str, "hello", 5) != 0) {
|
||||
fprintf(stderr, "FAIL: string roundtrip\n");
|
||||
arboricx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
arboricx_free_buf(ctx, decoded_str, decoded_len);
|
||||
printf("PASS: string roundtrip \"hello\"\n");
|
||||
|
||||
/* Test: kernel loaded */
|
||||
uint32_t kernel_root = arb_kernel_root(ctx);
|
||||
if (kernel_root == 0) {
|
||||
fprintf(stderr, "FAIL: kernel not loaded\n");
|
||||
arboricx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
printf("PASS: kernel loaded (root=%u)\n", kernel_root);
|
||||
|
||||
arboricx_free(ctx);
|
||||
printf("\nAll C ABI tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
84
ext/zig/tests/native_bundle_append_test.c
Normal file
84
ext/zig/tests/native_bundle_append_test.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "../include/arboricx.h"
|
||||
|
||||
static uint8_t *read_file(const char *path, size_t *out_len) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*out_len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
uint8_t *buf = malloc(*out_len);
|
||||
fread(buf, 1, *out_len, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int main() {
|
||||
arb_ctx_t *ctx = arboricx_init();
|
||||
if (!ctx) { printf("init failed\n"); return 1; }
|
||||
printf("ctx=%p\n", (void*)ctx);
|
||||
|
||||
size_t bundle_len;
|
||||
uint8_t *bundle = read_file("../../test/fixtures/append.arboricx", &bundle_len);
|
||||
if (!bundle) { printf("bundle not found\n"); return 1; }
|
||||
printf("bundle size=%zu\n", bundle_len);
|
||||
|
||||
clock_t t0 = clock();
|
||||
uint32_t term = arb_load_bundle(ctx, bundle, bundle_len, "root");
|
||||
clock_t t1 = clock();
|
||||
printf("load_bundle took %.3f ms, term=%u\n", (double)(t1 - t0) * 1000.0 / CLOCKS_PER_SEC, term);
|
||||
if (term == 0) {
|
||||
printf("load_bundle failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t arg1 = arb_of_string(ctx, "Hello, ");
|
||||
uint32_t arg2 = arb_of_string(ctx, "world!");
|
||||
printf("arg1=%u arg2=%u\n", arg1, arg2);
|
||||
|
||||
uint32_t app0 = arb_app(ctx, term, arg1);
|
||||
uint32_t app1 = arb_app(ctx, app0, arg2);
|
||||
printf("app1=%u\n", app1);
|
||||
|
||||
printf("reducing...\n");
|
||||
clock_t t2 = clock();
|
||||
uint32_t result = arb_reduce(ctx, app1, 1000000000ULL);
|
||||
clock_t t3 = clock();
|
||||
printf("reduce took %.3f ms, result=%u\n", (double)(t3 - t2) * 1000.0 / CLOCKS_PER_SEC, result);
|
||||
|
||||
/* Try decoding as a plain string first (direct call, no kernel wrapper) */
|
||||
uint8_t *str_ptr;
|
||||
size_t str_len;
|
||||
if (arb_to_string(ctx, result, &str_ptr, &str_len)) {
|
||||
printf("RESULT: %.*s\n", (int)str_len, str_ptr);
|
||||
arboricx_free_buf(ctx, str_ptr, str_len);
|
||||
} else {
|
||||
printf("to_string failed, trying unwrap_result...\n");
|
||||
int ok;
|
||||
uint32_t value, rest;
|
||||
if (!arb_unwrap_result(ctx, result, &ok, &value, &rest)) {
|
||||
printf("unwrap_result also failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("unwrap_result: ok=%d value=%u\n", ok, value);
|
||||
uint64_t htag;
|
||||
uint32_t payload;
|
||||
if (!arb_unwrap_host_value(ctx, value, &htag, &payload)) {
|
||||
printf("unwrap_host_value failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("htag=%lu payload=%u\n", htag, payload);
|
||||
if (arb_to_string(ctx, payload, &str_ptr, &str_len)) {
|
||||
printf("RESULT: %.*s\n", (int)str_len, str_ptr);
|
||||
arboricx_free_buf(ctx, str_ptr, str_len);
|
||||
}
|
||||
}
|
||||
|
||||
free(bundle);
|
||||
arboricx_free(ctx);
|
||||
printf("done\n");
|
||||
return 0;
|
||||
}
|
||||
60
ext/zig/tests/native_bundle_bools_test.c
Normal file
60
ext/zig/tests/native_bundle_bools_test.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "../include/arboricx.h"
|
||||
|
||||
static uint8_t *read_file(const char *path, size_t *out_len) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*out_len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
uint8_t *buf = malloc(*out_len);
|
||||
fread(buf, 1, *out_len, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int test_bundle(arb_ctx_t *ctx, const char *path, int expect_val) {
|
||||
size_t bundle_len;
|
||||
uint8_t *bundle = read_file(path, &bundle_len);
|
||||
if (!bundle) { printf("bundle not found: %s\n", path); return 1; }
|
||||
|
||||
uint32_t term = arb_load_bundle(ctx, bundle, bundle_len, "root");
|
||||
if (term == 0) {
|
||||
printf("load_bundle failed for %s\n", path);
|
||||
free(bundle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t result = arb_reduce(ctx, term, 1000000000ULL);
|
||||
|
||||
int b;
|
||||
if (!arb_to_bool(ctx, result, &b)) {
|
||||
printf("to_bool failed for %s\n", path);
|
||||
free(bundle);
|
||||
return 1;
|
||||
}
|
||||
printf("%s result bool=%d (expected %d)\n", path, b, expect_val);
|
||||
if (b != expect_val) {
|
||||
printf("MISMATCH!\n");
|
||||
free(bundle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(bundle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
arb_ctx_t *ctx = arboricx_init();
|
||||
if (!ctx) { printf("init failed\n"); return 1; }
|
||||
|
||||
if (test_bundle(ctx, "../../test/fixtures/true.arboricx", 1) != 0) return 1;
|
||||
if (test_bundle(ctx, "../../test/fixtures/false.arboricx", 0) != 0) return 1;
|
||||
|
||||
arboricx_free(ctx);
|
||||
printf("All bool tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
60
ext/zig/tests/native_bundle_id_test.c
Normal file
60
ext/zig/tests/native_bundle_id_test.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "../include/arboricx.h"
|
||||
|
||||
static uint8_t *read_file(const char *path, size_t *out_len) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*out_len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
uint8_t *buf = malloc(*out_len);
|
||||
fread(buf, 1, *out_len, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int main() {
|
||||
arb_ctx_t *ctx = arboricx_init();
|
||||
if (!ctx) { printf("init failed\n"); return 1; }
|
||||
|
||||
size_t bundle_len;
|
||||
uint8_t *bundle = read_file("../../test/fixtures/id.arboricx", &bundle_len);
|
||||
if (!bundle) { printf("bundle not found\n"); return 1; }
|
||||
printf("bundle size=%zu\n", bundle_len);
|
||||
|
||||
clock_t t0 = clock();
|
||||
uint32_t term = arb_load_bundle(ctx, bundle, bundle_len, "root");
|
||||
clock_t t1 = clock();
|
||||
printf("load_bundle took %.3f ms, term=%u\n", (double)(t1 - t0) * 1000.0 / CLOCKS_PER_SEC, term);
|
||||
if (term == 0) {
|
||||
printf("load_bundle failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t arg1 = arb_of_string(ctx, "hello");
|
||||
uint32_t app0 = arb_app(ctx, term, arg1);
|
||||
|
||||
printf("reducing...\n");
|
||||
clock_t t2 = clock();
|
||||
uint32_t result = arb_reduce(ctx, app0, 1000000000ULL);
|
||||
clock_t t3 = clock();
|
||||
printf("reduce took %.3f ms, result=%u\n", (double)(t3 - t2) * 1000.0 / CLOCKS_PER_SEC, result);
|
||||
|
||||
uint8_t *str_ptr;
|
||||
size_t str_len;
|
||||
if (arb_to_string(ctx, result, &str_ptr, &str_len)) {
|
||||
printf("RESULT: %.*s\n", (int)str_len, str_ptr);
|
||||
arboricx_free_buf(ctx, str_ptr, str_len);
|
||||
} else {
|
||||
printf("to_string failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(bundle);
|
||||
arboricx_free(ctx);
|
||||
printf("done\n");
|
||||
return 0;
|
||||
}
|
||||
251
ext/zig/tests/python_ffi_test.py
Normal file
251
ext/zig/tests/python_ffi_test.py
Normal file
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Python FFI tests for the Arboricx C ABI.
|
||||
|
||||
Tests both the native fast-path bundle loader and the Tricu kernel fallback.
|
||||
"""
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ZIG_DIR = os.path.dirname(SCRIPT_DIR)
|
||||
lib_path = os.environ.get(
|
||||
"ARBORICX_LIB",
|
||||
os.path.join(ZIG_DIR, "zig-out", "lib", "libarboricx.so"),
|
||||
)
|
||||
lib = ctypes.CDLL(lib_path)
|
||||
|
||||
# --- Lifecycle ---
|
||||
lib.arboricx_init.restype = ctypes.c_void_p
|
||||
lib.arboricx_free.argtypes = [ctypes.c_void_p]
|
||||
|
||||
# --- Tree construction ---
|
||||
lib.arb_leaf.argtypes = [ctypes.c_void_p]
|
||||
lib.arb_leaf.restype = ctypes.c_uint32
|
||||
lib.arb_stem.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
|
||||
lib.arb_stem.restype = ctypes.c_uint32
|
||||
lib.arb_fork.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32]
|
||||
lib.arb_fork.restype = ctypes.c_uint32
|
||||
lib.arb_app.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32]
|
||||
lib.arb_app.restype = ctypes.c_uint32
|
||||
|
||||
# --- Reduction ---
|
||||
lib.arb_reduce.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint64]
|
||||
lib.arb_reduce.restype = ctypes.c_uint32
|
||||
|
||||
# --- Codecs ---
|
||||
lib.arb_of_number.argtypes = [ctypes.c_void_p, ctypes.c_uint64]
|
||||
lib.arb_of_number.restype = ctypes.c_uint32
|
||||
lib.arb_of_string.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||
lib.arb_of_string.restype = ctypes.c_uint32
|
||||
lib.arb_of_bytes.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t]
|
||||
lib.arb_of_bytes.restype = ctypes.c_uint32
|
||||
lib.arb_of_list.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32), ctypes.c_size_t]
|
||||
lib.arb_of_list.restype = ctypes.c_uint32
|
||||
lib.arb_to_number.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint64)]
|
||||
lib.arb_to_number.restype = ctypes.c_int
|
||||
lib.arb_to_string.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)), ctypes.POINTER(ctypes.c_size_t)]
|
||||
lib.arb_to_string.restype = ctypes.c_int
|
||||
lib.arb_to_bool.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_int)]
|
||||
lib.arb_to_bool.restype = ctypes.c_int
|
||||
lib.arboricx_free_buf.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t]
|
||||
|
||||
# --- Result unwrapping ---
|
||||
lib.arb_unwrap_result.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)]
|
||||
lib.arb_unwrap_result.restype = ctypes.c_int
|
||||
lib.arb_unwrap_host_value.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_uint32)]
|
||||
lib.arb_unwrap_host_value.restype = ctypes.c_int
|
||||
|
||||
# --- Kernel ---
|
||||
lib.arb_kernel_root.argtypes = [ctypes.c_void_p]
|
||||
lib.arb_kernel_root.restype = ctypes.c_uint32
|
||||
|
||||
# --- Native bundle loading ---
|
||||
lib.arb_load_bundle.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t, ctypes.c_char_p]
|
||||
lib.arb_load_bundle.restype = ctypes.c_uint32
|
||||
lib.arb_load_bundle_default.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t]
|
||||
lib.arb_load_bundle_default.restype = ctypes.c_uint32
|
||||
|
||||
|
||||
ctx = lib.arboricx_init()
|
||||
print("ctx init ok")
|
||||
|
||||
fixtures = os.path.join(ZIG_DIR, "..", "..", "test", "fixtures")
|
||||
|
||||
|
||||
def read_bundle(name):
|
||||
path = os.path.join(fixtures, name)
|
||||
with open(path, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def c_bytes(py_bytes):
|
||||
arr = (ctypes.c_uint8 * len(py_bytes))(*py_bytes)
|
||||
return arr
|
||||
|
||||
|
||||
def to_string(ctx, root):
|
||||
ptr = ctypes.POINTER(ctypes.c_uint8)()
|
||||
length = ctypes.c_size_t()
|
||||
if not lib.arb_to_string(ctx, root, ctypes.byref(ptr), ctypes.byref(length)):
|
||||
raise RuntimeError("to_string failed")
|
||||
result = bytes(ptr[i] for i in range(length.value))
|
||||
lib.arboricx_free_buf(ctx, ptr, length.value)
|
||||
return result.decode("utf-8")
|
||||
|
||||
|
||||
def to_number(ctx, root):
|
||||
out = ctypes.c_uint64()
|
||||
if not lib.arb_to_number(ctx, root, ctypes.byref(out)):
|
||||
raise RuntimeError("to_number failed")
|
||||
return out.value
|
||||
|
||||
|
||||
def to_bool(ctx, root):
|
||||
out = ctypes.c_int()
|
||||
if not lib.arb_to_bool(ctx, root, ctypes.byref(out)):
|
||||
raise RuntimeError("to_bool failed")
|
||||
return bool(out.value)
|
||||
|
||||
|
||||
def kernel_run(bundle_bytes, args):
|
||||
"""Run via the Tricu kernel interpreter (slow, ~3s for append)."""
|
||||
buf = c_bytes(bundle_bytes)
|
||||
bundle_tree = lib.arb_of_bytes(ctx, buf, len(bundle_bytes))
|
||||
tag = lib.arb_of_number(ctx, 1)
|
||||
arg_items = []
|
||||
for a in args:
|
||||
arg_items.append(lib.arb_of_string(ctx, a.encode("utf-8")))
|
||||
current = lib.arb_leaf(ctx)
|
||||
for item in reversed(arg_items):
|
||||
current = lib.arb_fork(ctx, item, current)
|
||||
app0 = lib.arb_app(ctx, lib.arb_kernel_root(ctx), tag)
|
||||
app1 = lib.arb_app(ctx, app0, bundle_tree)
|
||||
app2 = lib.arb_app(ctx, app1, current)
|
||||
result = lib.arb_reduce(ctx, app2, 1_000_000_000)
|
||||
ok = ctypes.c_int()
|
||||
value = ctypes.c_uint32()
|
||||
rest = ctypes.c_uint32()
|
||||
if not lib.arb_unwrap_result(ctx, result, ctypes.byref(ok), ctypes.byref(value), ctypes.byref(rest)):
|
||||
raise RuntimeError("unwrap_result failed")
|
||||
tag_num = ctypes.c_uint64()
|
||||
payload = ctypes.c_uint32()
|
||||
if not lib.arb_unwrap_host_value(ctx, value.value, ctypes.byref(tag_num), ctypes.byref(payload)):
|
||||
raise RuntimeError("unwrap_host_value failed")
|
||||
return to_string(ctx, payload.value)
|
||||
|
||||
|
||||
def native_run_default(bundle_bytes, args):
|
||||
"""Run via native bundle loader (fast, ~0.01s)."""
|
||||
buf = c_bytes(bundle_bytes)
|
||||
term = lib.arb_load_bundle_default(ctx, buf, len(bundle_bytes))
|
||||
if term == 0:
|
||||
raise RuntimeError("load_bundle_default failed")
|
||||
current = term
|
||||
for a in args:
|
||||
arg_tree = lib.arb_of_string(ctx, a.encode("utf-8"))
|
||||
current = lib.arb_app(ctx, current, arg_tree)
|
||||
result = lib.arb_reduce(ctx, current, 1_000_000_000)
|
||||
return to_string(ctx, result)
|
||||
|
||||
|
||||
def native_run_named(bundle_bytes, name, args):
|
||||
"""Run via native bundle loader with named export (fast)."""
|
||||
buf = c_bytes(bundle_bytes)
|
||||
term = lib.arb_load_bundle(ctx, buf, len(bundle_bytes), name.encode("utf-8"))
|
||||
if term == 0:
|
||||
raise RuntimeError(f"load_bundle({name!r}) failed")
|
||||
current = term
|
||||
for a in args:
|
||||
arg_tree = lib.arb_of_string(ctx, a.encode("utf-8"))
|
||||
current = lib.arb_app(ctx, current, arg_tree)
|
||||
result = lib.arb_reduce(ctx, current, 1_000_000_000)
|
||||
return to_string(ctx, result)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Tests
|
||||
# ============================================================================
|
||||
|
||||
all_ok = True
|
||||
|
||||
|
||||
def check(label, got, want):
|
||||
global all_ok
|
||||
if got != want:
|
||||
print(f"FAIL {label}: got {got!r}, want {want!r}")
|
||||
all_ok = False
|
||||
else:
|
||||
print(f"PASS {label}: {got!r}")
|
||||
|
||||
|
||||
# Test 1: id via kernel
|
||||
print("\n--- Test 1: id (kernel path) ---")
|
||||
bundle = read_bundle("id.arboricx")
|
||||
t0 = time.time()
|
||||
result = kernel_run(bundle, ["hello"])
|
||||
t1 = time.time()
|
||||
check("id kernel", result, "hello")
|
||||
print(f" time: {(t1 - t0) * 1000:.1f} ms")
|
||||
|
||||
# Test 2: id via native
|
||||
print("\n--- Test 2: id (native path) ---")
|
||||
t0 = time.time()
|
||||
result = native_run_default(bundle, ["hello"])
|
||||
t1 = time.time()
|
||||
check("id native", result, "hello")
|
||||
print(f" time: {(t1 - t0) * 1000:.1f} ms")
|
||||
|
||||
# Test 3: append via kernel
|
||||
print("\n--- Test 3: append (kernel path) ---")
|
||||
bundle = read_bundle("append.arboricx")
|
||||
t0 = time.time()
|
||||
result = kernel_run(bundle, ["Hello, ", "world!"])
|
||||
t1 = time.time()
|
||||
check("append kernel", result, "Hello, world!")
|
||||
print(f" time: {(t1 - t0) * 1000:.1f} ms")
|
||||
|
||||
# Test 4: append via native
|
||||
print("\n--- Test 4: append (native path) ---")
|
||||
t0 = time.time()
|
||||
result = native_run_default(bundle, ["Hello, ", "world!"])
|
||||
t1 = time.time()
|
||||
check("append native", result, "Hello, world!")
|
||||
print(f" time: {(t1 - t0) * 1000:.1f} ms")
|
||||
|
||||
# Test 5: append via native named export
|
||||
print("\n--- Test 5: append via named export 'root' ---")
|
||||
t0 = time.time()
|
||||
result = native_run_named(bundle, "root", ["Hello, ", "world!"])
|
||||
t1 = time.time()
|
||||
check("append named", result, "Hello, world!")
|
||||
print(f" time: {(t1 - t0) * 1000:.1f} ms")
|
||||
|
||||
# Test 6: true / false via native
|
||||
print("\n--- Test 6: true / false (native path) ---")
|
||||
for name, expected in [("true.arboricx", True), ("false.arboricx", False)]:
|
||||
bundle = read_bundle(name)
|
||||
buf = c_bytes(bundle)
|
||||
term = lib.arb_load_bundle_default(ctx, buf, len(bundle))
|
||||
result = lib.arb_reduce(ctx, term, 1_000_000_000)
|
||||
check(f"{name} bool", to_bool(ctx, result), expected)
|
||||
|
||||
# Test 7: number roundtrip
|
||||
print("\n--- Test 7: number roundtrip ---")
|
||||
num_tree = lib.arb_of_number(ctx, 42)
|
||||
check("number 42", to_number(ctx, num_tree), 42)
|
||||
|
||||
# Test 8: string roundtrip
|
||||
print("\n--- Test 8: string roundtrip ---")
|
||||
str_tree = lib.arb_of_string(ctx, b"hello")
|
||||
check("string hello", to_string(ctx, str_tree), "hello")
|
||||
|
||||
lib.arboricx_free(ctx)
|
||||
|
||||
if all_ok:
|
||||
print("\nAll tests passed!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\nSome tests failed!")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user