203 lines
6.7 KiB
C
Raw Normal View History

/*
* c-client: Alice-Bob message exchange written entirely in C.
*
* Demonstrates that the client-ffi C API is straightforward to consume
* directly no Rust glue required. Build with the provided Makefile.
*/
#include "client_ffi.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* ------------------------------------------------------------------
* Convenience macros for building slice_ref_uint8_t values.
* SLICE(p, n) arbitrary pointer + length.
* STR(s) string literal (length computed at compile time).
* ------------------------------------------------------------------ */
#define SLICE(p, n) ((slice_ref_uint8_t){ .ptr = (const uint8_t *)(p), .len = (n) })
#define STR(s) SLICE(s, sizeof(s) - 1)
/* ------------------------------------------------------------------
* In-memory delivery bus (shared by all clients, like InProcessDelivery)
* ------------------------------------------------------------------ */
#define MAX_ENVELOPES 32
#define MAX_ENVELOPE_SZ 2048
typedef struct {
uint8_t data[MAX_ENVELOPE_SZ];
size_t len;
} Envelope;
typedef struct {
Envelope items[MAX_ENVELOPES];
int head;
int tail;
int count;
} Queue;
static Queue bus;
static void queue_init(Queue *q)
{
memset(q, 0, sizeof(*q));
}
static void queue_push(Queue *q, const uint8_t *data, size_t len)
{
assert(q->count < MAX_ENVELOPES && "delivery queue overflow");
assert(len <= MAX_ENVELOPE_SZ && "envelope too large");
memcpy(q->items[q->tail].data, data, len);
q->items[q->tail].len = len;
q->tail = (q->tail + 1) % MAX_ENVELOPES;
q->count++;
}
static int queue_pop(Queue *q, const uint8_t **data_out, size_t *len_out)
{
if (q->count == 0) return 0;
*data_out = q->items[q->head].data;
*len_out = q->items[q->head].len;
q->head = (q->head + 1) % MAX_ENVELOPES;
q->count--;
return 1;
}
/* ------------------------------------------------------------------
* Delivery callback: all clients share one bus.
* ------------------------------------------------------------------ */
static int32_t deliver_cb(
const uint8_t *addr_ptr, size_t addr_len,
const uint8_t *data_ptr, size_t data_len)
{
(void)addr_ptr; (void)addr_len;
queue_push(&bus, data_ptr, data_len);
return 0;
}
/* ------------------------------------------------------------------
* Helper: pop one envelope from the bus and push it into receiver.
* Returns a heap-allocated result; caller frees with
* push_inbound_result_free().
* ------------------------------------------------------------------ */
static PushInboundResult_t *route(ClientHandle_t *receiver)
{
const uint8_t *data;
size_t len;
int ok = queue_pop(&bus, &data, &len);
assert(ok && "expected an envelope in the bus");
PushInboundResult_t *r = client_receive(receiver, SLICE(data, len));
assert(push_inbound_result_error_code(r) == 0 && "push_inbound failed");
return r;
}
/* ------------------------------------------------------------------
* Main
* ------------------------------------------------------------------ */
int main(void)
{
queue_init(&bus);
/* Create clients — both share the same delivery bus */
ClientHandle_t *alice = client_create(STR("alice"), deliver_cb);
ClientHandle_t *bob = client_create(STR("bob"), deliver_cb);
assert(alice && "client_create returned NULL for alice");
assert(bob && "client_create returned NULL for bob");
/* Bob generates an intro bundle */
CreateIntroResult_t *bob_intro = client_create_intro_bundle(bob);
assert(create_intro_result_error_code(bob_intro) == 0);
slice_ref_uint8_t intro_bytes = create_intro_result_bytes(bob_intro);
/* Alice initiates a conversation with Bob */
CreateConvoResult_t *alice_convo = client_create_conversation(
alice, intro_bytes, STR("hello bob"));
assert(create_convo_result_error_code(alice_convo) == 0);
create_intro_result_free(bob_intro);
/* Route alice -> bob */
PushInboundResult_t *recv = route(bob);
assert(push_inbound_result_has_content(recv) && "expected content from alice");
assert(push_inbound_result_is_new_convo(recv) && "expected new-conversation flag");
slice_ref_uint8_t content = push_inbound_result_content(recv);
assert(content.len == 9);
assert(memcmp(content.ptr, "hello bob", 9) == 0);
printf("Bob received: \"%.*s\"\n", (int)content.len, content.ptr);
/* Copy Bob's convo_id before freeing recv */
slice_ref_uint8_t cid_ref = push_inbound_result_convo_id(recv);
uint8_t bob_cid[256];
size_t bob_cid_len = cid_ref.len;
if (bob_cid_len >= sizeof(bob_cid)) {
fprintf(stderr, "conversation id too long (%zu bytes)\n", bob_cid_len);
return 1;
}
memcpy(bob_cid, cid_ref.ptr, bob_cid_len);
push_inbound_result_free(recv);
/* Bob replies */
ErrorCode_t rc = client_send_message(
bob, SLICE(bob_cid, bob_cid_len), STR("hi alice"));
assert(rc == ERROR_CODE_NONE);
recv = route(alice);
assert(push_inbound_result_has_content(recv) && "expected content from bob");
assert(!push_inbound_result_is_new_convo(recv) && "unexpected new-convo flag");
content = push_inbound_result_content(recv);
assert(content.len == 8);
assert(memcmp(content.ptr, "hi alice", 8) == 0);
printf("Alice received: \"%.*s\"\n", (int)content.len, content.ptr);
push_inbound_result_free(recv);
/* Multiple back-and-forth rounds */
slice_ref_uint8_t alice_cid = create_convo_result_id(alice_convo);
for (int i = 0; i < 3; i++) {
char msg[32];
int mlen = snprintf(msg, sizeof(msg), "msg %d", i);
rc = client_send_message(alice, alice_cid, SLICE(msg, (size_t)mlen));
assert(rc == ERROR_CODE_NONE);
recv = route(bob);
assert(push_inbound_result_has_content(recv));
content = push_inbound_result_content(recv);
assert((int)content.len == mlen);
assert(memcmp(content.ptr, msg, (size_t)mlen) == 0);
push_inbound_result_free(recv);
char reply[32];
int rlen = snprintf(reply, sizeof(reply), "reply %d", i);
rc = client_send_message(
bob, SLICE(bob_cid, bob_cid_len), SLICE(reply, (size_t)rlen));
assert(rc == ERROR_CODE_NONE);
recv = route(alice);
assert(push_inbound_result_has_content(recv));
content = push_inbound_result_content(recv);
assert((int)content.len == rlen);
assert(memcmp(content.ptr, reply, (size_t)rlen) == 0);
push_inbound_result_free(recv);
}
/* Cleanup */
create_convo_result_free(alice_convo);
client_destroy(alice);
client_destroy(bob);
printf("Message exchange complete.\n");
return 0;
}