Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions src/interface/ffi/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ var state: ?ReferenceState = null;
//==============================================================================

/// Initialise the reference workload. Returns 0 on success.
export fn chapeliser_ref_init() callconv(.C) c_int {
export fn c_init() callconv(.C) c_int {
if (state != null) return @intFromEnum(Result.err); // already initialised
state = ReferenceState.init(std.heap.c_allocator);
return @intFromEnum(Result.ok);
}

/// Shut down the reference workload. Returns 0 on success.
export fn chapeliser_ref_shutdown() callconv(.C) c_int {
export fn c_shutdown() callconv(.C) c_int {
if (state) |*s| {
s.deinit();
state = null;
Expand All @@ -93,15 +93,15 @@ export fn chapeliser_ref_shutdown() callconv(.C) c_int {
//==============================================================================

/// Return the total number of input items.
export fn chapeliser_ref_get_total_items() callconv(.C) c_int {
export fn c_get_total_items() callconv(.C) c_int {
if (state) |s| {
return @intCast(s.items.items.len);
}
return 0;
}

/// Serialise input item `idx` into `buf`. Set `*len` to bytes written.
export fn chapeliser_ref_load_item(idx: c_int, buf: [*]u8, len: *usize) callconv(.C) c_int {
export fn c_load_item(idx: c_int, buf: [*]u8, len: *usize) callconv(.C) c_int {
const s = &(state orelse return @intFromEnum(Result.err));
const i: usize = @intCast(idx);
if (i >= s.items.items.len) return @intFromEnum(Result.invalid_param);
Expand All @@ -115,7 +115,7 @@ export fn chapeliser_ref_load_item(idx: c_int, buf: [*]u8, len: *usize) callconv
}

/// Receive a processed result at index `idx`.
export fn chapeliser_ref_store_result(idx: c_int, buf: [*]const u8, len: usize) callconv(.C) c_int {
export fn c_store_result(idx: c_int, buf: [*]const u8, len: usize) callconv(.C) c_int {
const s = &(state orelse return @intFromEnum(Result.err));

// Copy the result data
Expand All @@ -139,7 +139,7 @@ export fn chapeliser_ref_store_result(idx: c_int, buf: [*]const u8, len: usize)
//==============================================================================

/// Process a single item: for the reference implementation, just echo it back.
export fn chapeliser_ref_process_item(
export fn c_process_item(
in_buf: [*]const u8,
in_len: usize,
out_buf: [*]u8,
Expand All @@ -152,7 +152,7 @@ export fn chapeliser_ref_process_item(
}

/// Process a chunk of items: for reference, process each individually.
export fn chapeliser_ref_process_chunk(
export fn c_process_chunk(
items_buf: [*]const u8,
item_count: c_int,
item_offsets: [*]const c_int,
Expand All @@ -178,7 +178,7 @@ export fn chapeliser_ref_process_chunk(
//==============================================================================

/// Combine two results: for reference, concatenate them.
export fn chapeliser_ref_reduce(
export fn c_reduce(
a_buf: [*]const u8,
a_len: usize,
b_buf: [*]const u8,
Expand All @@ -197,7 +197,7 @@ export fn chapeliser_ref_reduce(
//==============================================================================

/// Check if a result matches: for reference, match if first byte is non-zero.
export fn chapeliser_ref_is_match(buf: [*]const u8, len: usize) callconv(.C) c_int {
export fn c_is_match(buf: [*]const u8, len: usize) callconv(.C) c_int {
if (len == 0) return 0;
return if (buf[0] != 0) 1 else 0;
}
Expand All @@ -207,7 +207,7 @@ export fn chapeliser_ref_is_match(buf: [*]const u8, len: usize) callconv(.C) c_i
//==============================================================================

/// Hash an item's key: for reference, use FNV-1a on the first 8 bytes.
export fn chapeliser_ref_key_hash(buf: [*]const u8, len: usize) callconv(.C) c_uint {
export fn c_key_hash(buf: [*]const u8, len: usize) callconv(.C) c_uint {
const hash_len = @min(len, 8);
var h: u32 = 2166136261; // FNV offset basis
for (buf[0..hash_len]) |b| {
Expand All @@ -222,7 +222,7 @@ export fn chapeliser_ref_key_hash(buf: [*]const u8, len: usize) callconv(.C) c_u
//==============================================================================

/// Save checkpoint: reference implementation is a no-op.
export fn chapeliser_ref_checkpoint_save(
export fn c_checkpoint_save(
_: [*]const u8,
_: usize,
_: [*:0]const u8,
Expand All @@ -231,7 +231,7 @@ export fn chapeliser_ref_checkpoint_save(
}

/// Load checkpoint: reference implementation is a no-op.
export fn chapeliser_ref_checkpoint_load(
export fn c_checkpoint_load(
_: [*]u8,
_: *usize,
_: [*:0]const u8,
Expand All @@ -243,11 +243,11 @@ export fn chapeliser_ref_checkpoint_load(
// Version
//==============================================================================

export fn chapeliser_ref_version() callconv(.C) [*:0]const u8 {
export fn chapeliser_version() callconv(.C) [*:0]const u8 {
return VERSION.ptr;
}

export fn chapeliser_ref_build_info() callconv(.C) [*:0]const u8 {
export fn chapeliser_build_info() callconv(.C) [*:0]const u8 {
return BUILD_INFO.ptr;
}

Expand All @@ -256,12 +256,12 @@ export fn chapeliser_ref_build_info() callconv(.C) [*:0]const u8 {
//==============================================================================

test "lifecycle" {
const rc_init = chapeliser_ref_init();
const rc_init = c_init();
try std.testing.expectEqual(@as(c_int, 0), rc_init);
defer _ = chapeliser_ref_shutdown();
defer _ = c_shutdown();

// Double init should fail
const rc_double = chapeliser_ref_init();
const rc_double = c_init();
try std.testing.expectEqual(@as(c_int, 1), rc_double);
}

Expand All @@ -270,7 +270,7 @@ test "process_item echo" {
var output: [256]u8 = undefined;
var out_len: usize = 0;

const rc = chapeliser_ref_process_item(input.ptr, input.len, &output, &out_len);
const rc = c_process_item(input.ptr, input.len, &output, &out_len);
try std.testing.expectEqual(@as(c_int, 0), rc);
try std.testing.expectEqual(input.len, out_len);
try std.testing.expectEqualStrings(input, output[0..out_len]);
Expand All @@ -282,33 +282,33 @@ test "reduce concatenates" {
var output: [256]u8 = undefined;
var out_len: usize = 0;

const rc = chapeliser_ref_reduce(a.ptr, a.len, b.ptr, b.len, &output, &out_len);
const rc = c_reduce(a.ptr, a.len, b.ptr, b.len, &output, &out_len);
try std.testing.expectEqual(@as(c_int, 0), rc);
try std.testing.expectEqualStrings("foobar", output[0..out_len]);
}

test "key_hash deterministic" {
const data = "testkey1";
const h1 = chapeliser_ref_key_hash(data.ptr, data.len);
const h2 = chapeliser_ref_key_hash(data.ptr, data.len);
const h1 = c_key_hash(data.ptr, data.len);
const h2 = c_key_hash(data.ptr, data.len);
try std.testing.expectEqual(h1, h2);
}

test "is_match" {
const yes = [_]u8{ 0x42, 0x00 };
const no = [_]u8{ 0x00, 0x42 };
try std.testing.expectEqual(@as(c_int, 1), chapeliser_ref_is_match(&yes, yes.len));
try std.testing.expectEqual(@as(c_int, 0), chapeliser_ref_is_match(&no, no.len));
try std.testing.expectEqual(@as(c_int, 1), c_is_match(&yes, yes.len));
try std.testing.expectEqual(@as(c_int, 0), c_is_match(&no, no.len));
}

test "checkpoint not implemented" {
var buf: [64]u8 = undefined;
var len: usize = buf.len;
try std.testing.expectEqual(@as(c_int, -1), chapeliser_ref_checkpoint_save(&buf, len, "test"));
try std.testing.expectEqual(@as(c_int, -1), chapeliser_ref_checkpoint_load(&buf, &len, "test"));
try std.testing.expectEqual(@as(c_int, -1), c_checkpoint_save(&buf, len, "test"));
try std.testing.expectEqual(@as(c_int, -1), c_checkpoint_load(&buf, &len, "test"));
}

test "version" {
const ver = std.mem.span(chapeliser_ref_version());
const ver = std.mem.span(chapeliser_version());
try std.testing.expectEqualStrings("0.1.0", ver);
}
76 changes: 38 additions & 38 deletions src/interface/ffi/test/integration_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,44 @@ const std = @import("std");
const testing = std.testing;

// Import reference FFI functions
extern fn chapeliser_ref_init() c_int;
extern fn chapeliser_ref_shutdown() c_int;
extern fn chapeliser_ref_get_total_items() c_int;
extern fn chapeliser_ref_load_item(c_int, [*]u8, *usize) c_int;
extern fn chapeliser_ref_store_result(c_int, [*]const u8, usize) c_int;
extern fn chapeliser_ref_process_item([*]const u8, usize, [*]u8, *usize) c_int;
extern fn chapeliser_ref_process_chunk([*]const u8, c_int, [*]const c_int, [*]const c_int, [*]u8, *usize) c_int;
extern fn chapeliser_ref_reduce([*]const u8, usize, [*]const u8, usize, [*]u8, *usize) c_int;
extern fn chapeliser_ref_is_match([*]const u8, usize) c_int;
extern fn chapeliser_ref_key_hash([*]const u8, usize) c_uint;
extern fn chapeliser_ref_checkpoint_save([*]const u8, usize, [*:0]const u8) c_int;
extern fn chapeliser_ref_checkpoint_load([*]u8, *usize, [*:0]const u8) c_int;
extern fn chapeliser_ref_version() [*:0]const u8;
extern fn chapeliser_ref_build_info() [*:0]const u8;
extern fn c_init() c_int;
extern fn c_shutdown() c_int;
extern fn c_get_total_items() c_int;
extern fn c_load_item(c_int, [*]u8, *usize) c_int;
extern fn c_store_result(c_int, [*]const u8, usize) c_int;
extern fn c_process_item([*]const u8, usize, [*]u8, *usize) c_int;
extern fn c_process_chunk([*]const u8, c_int, [*]const c_int, [*]const c_int, [*]u8, *usize) c_int;
extern fn c_reduce([*]const u8, usize, [*]const u8, usize, [*]u8, *usize) c_int;
extern fn c_is_match([*]const u8, usize) c_int;
extern fn c_key_hash([*]const u8, usize) c_uint;
extern fn c_checkpoint_save([*]const u8, usize, [*:0]const u8) c_int;
extern fn c_checkpoint_load([*]u8, *usize, [*:0]const u8) c_int;
extern fn chapeliser_version() [*:0]const u8;
extern fn chapeliser_build_info() [*:0]const u8;

//==============================================================================
// Lifecycle Tests
//==============================================================================

test "init and shutdown" {
const rc_init = chapeliser_ref_init();
const rc_init = c_init();
try testing.expectEqual(@as(c_int, 0), rc_init);

const rc_shutdown = chapeliser_ref_shutdown();
const rc_shutdown = c_shutdown();
try testing.expectEqual(@as(c_int, 0), rc_shutdown);
}

test "double init fails" {
const rc1 = chapeliser_ref_init();
const rc1 = c_init();
try testing.expectEqual(@as(c_int, 0), rc1);
defer _ = chapeliser_ref_shutdown();
defer _ = c_shutdown();

const rc2 = chapeliser_ref_init();
const rc2 = c_init();
try testing.expectEqual(@as(c_int, 1), rc2); // error: already initialised
}

test "shutdown without init fails" {
const rc = chapeliser_ref_shutdown();
const rc = c_shutdown();
try testing.expectEqual(@as(c_int, 1), rc); // error: not initialised
}

Expand All @@ -58,7 +58,7 @@ test "process_item preserves data" {
var output: [256]u8 = undefined;
var out_len: usize = 0;

const rc = chapeliser_ref_process_item(input.ptr, input.len, &output, &out_len);
const rc = c_process_item(input.ptr, input.len, &output, &out_len);
try testing.expectEqual(@as(c_int, 0), rc);
try testing.expectEqual(input.len, out_len);
try testing.expectEqualStrings(input, output[0..out_len]);
Expand All @@ -69,7 +69,7 @@ test "process_item handles empty input" {
var output: [256]u8 = undefined;
var out_len: usize = 0;

const rc = chapeliser_ref_process_item(input.ptr, input.len, &output, &out_len);
const rc = c_process_item(input.ptr, input.len, &output, &out_len);
try testing.expectEqual(@as(c_int, 0), rc);
try testing.expectEqual(@as(usize, 0), out_len);
}
Expand All @@ -84,7 +84,7 @@ test "reduce combines two results" {
var output: [256]u8 = undefined;
var out_len: usize = 0;

const rc = chapeliser_ref_reduce(a.ptr, a.len, b.ptr, b.len, &output, &out_len);
const rc = c_reduce(a.ptr, a.len, b.ptr, b.len, &output, &out_len);
try testing.expectEqual(@as(c_int, 0), rc);
try testing.expectEqualStrings("hello world", output[0..out_len]);
}
Expand All @@ -104,12 +104,12 @@ test "reduce is associative for concatenation" {
var final2: usize = 0;

// (a + b) + c
_ = chapeliser_ref_reduce(a.ptr, a.len, b.ptr, b.len, &tmp1, &len1);
_ = chapeliser_ref_reduce(&tmp1, len1, c.ptr, c.len, &result1, &final1);
_ = c_reduce(a.ptr, a.len, b.ptr, b.len, &tmp1, &len1);
_ = c_reduce(&tmp1, len1, c.ptr, c.len, &result1, &final1);

// a + (b + c)
_ = chapeliser_ref_reduce(b.ptr, b.len, c.ptr, c.len, &tmp2, &len2);
_ = chapeliser_ref_reduce(a.ptr, a.len, &tmp2, len2, &result2, &final2);
_ = c_reduce(b.ptr, b.len, c.ptr, c.len, &tmp2, &len2);
_ = c_reduce(a.ptr, a.len, &tmp2, len2, &result2, &final2);

try testing.expectEqualStrings(result1[0..final1], result2[0..final2]);
}
Expand All @@ -120,17 +120,17 @@ test "reduce is associative for concatenation" {

test "is_match returns 1 for non-zero first byte" {
const data = [_]u8{ 0xFF, 0x00, 0x00 };
try testing.expectEqual(@as(c_int, 1), chapeliser_ref_is_match(&data, data.len));
try testing.expectEqual(@as(c_int, 1), c_is_match(&data, data.len));
}

test "is_match returns 0 for zero first byte" {
const data = [_]u8{ 0x00, 0xFF, 0xFF };
try testing.expectEqual(@as(c_int, 0), chapeliser_ref_is_match(&data, data.len));
try testing.expectEqual(@as(c_int, 0), c_is_match(&data, data.len));
}

test "is_match returns 0 for empty buffer" {
const data = [_]u8{};
try testing.expectEqual(@as(c_int, 0), chapeliser_ref_is_match(&data, 0));
try testing.expectEqual(@as(c_int, 0), c_is_match(&data, 0));
}

//==============================================================================
Expand All @@ -139,16 +139,16 @@ test "is_match returns 0 for empty buffer" {

test "key_hash is deterministic" {
const key = "partition-key";
const h1 = chapeliser_ref_key_hash(key.ptr, key.len);
const h2 = chapeliser_ref_key_hash(key.ptr, key.len);
const h1 = c_key_hash(key.ptr, key.len);
const h2 = c_key_hash(key.ptr, key.len);
try testing.expectEqual(h1, h2);
}

test "key_hash differs for different keys" {
const k1 = "key-alpha";
const k2 = "key-bravo";
const h1 = chapeliser_ref_key_hash(k1.ptr, k1.len);
const h2 = chapeliser_ref_key_hash(k2.ptr, k2.len);
const h1 = c_key_hash(k1.ptr, k1.len);
const h2 = c_key_hash(k2.ptr, k2.len);
try testing.expect(h1 != h2);
}

Expand All @@ -158,14 +158,14 @@ test "key_hash differs for different keys" {

test "checkpoint save returns not-implemented" {
const data = "checkpoint data";
const rc = chapeliser_ref_checkpoint_save(data.ptr, data.len, "locale-0");
const rc = c_checkpoint_save(data.ptr, data.len, "locale-0");
try testing.expectEqual(@as(c_int, -1), rc);
}

test "checkpoint load returns not-implemented" {
var buf: [256]u8 = undefined;
var len: usize = buf.len;
const rc = chapeliser_ref_checkpoint_load(&buf, &len, "locale-0");
const rc = c_checkpoint_load(&buf, &len, "locale-0");
try testing.expectEqual(@as(c_int, -1), rc);
}

Expand All @@ -174,13 +174,13 @@ test "checkpoint load returns not-implemented" {
//==============================================================================

test "version is semantic" {
const ver = std.mem.span(chapeliser_ref_version());
const ver = std.mem.span(chapeliser_version());
try testing.expect(ver.len > 0);
try testing.expect(std.mem.count(u8, ver, ".") >= 1);
}

test "build_info is not empty" {
const info = std.mem.span(chapeliser_ref_build_info());
const info = std.mem.span(chapeliser_build_info());
try testing.expect(info.len > 0);
try testing.expect(std.mem.indexOf(u8, info, "chapeliser") != null);
}
Loading