mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-20 16:29:31 +00:00
The C codegen already emitted `my_timer.h` / `my_timer_cbor.h`, but the example had no runnable driver. Add `example.c` exercising the native ABI end-to-end (ctor with a struct param, string-returning version, struct-param echo, and a deeply nested ComplexRequest), plus a Makefile that builds the Nim dylib from the repo root — where the vendored Nimble deps resolve — and links the driver. Native is the same-process path; the companion CBOR headers are for crossing a process/machine boundary (see the forthcoming ipc example). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
178 lines
5.7 KiB
C
178 lines
5.7 KiB
C
// Generated by nim-ffi C codegen. Do not edit by hand.
|
|
//
|
|
// CBOR ABI (`<name>_cbor`). Use this for callers that cross a process or machine
|
|
// boundary (the request has to be serialized anyway) or any generic / cross-
|
|
// language caller. Build the request with the FfiCbor helpers below — a CBOR map
|
|
// whose keys are the Nim parameter names (listed per proc) — call the matching
|
|
// `<name>_cbor`, and decode the RET_OK response (a CBOR-encoded value; for
|
|
// string-returning procs a CBOR text string) with ffi_decode_text. RET_ERR
|
|
// delivers raw error text. For same-process callers, prefer the native `<name>`
|
|
// ABI in the companion <lib>.h header.
|
|
#ifndef NIM_FFI_GEN_MY_TIMER_CBOR_H
|
|
#define NIM_FFI_GEN_MY_TIMER_CBOR_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifndef NIM_FFI_RET_CODES
|
|
#define NIM_FFI_RET_CODES
|
|
#define RET_OK 0
|
|
#define RET_ERR 1
|
|
#define RET_MISSING_CALLBACK 2
|
|
#endif
|
|
|
|
#ifndef NIM_FFI_CALLBACK_T
|
|
#define NIM_FFI_CALLBACK_T
|
|
typedef void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData);
|
|
#endif
|
|
|
|
#ifndef NIM_FFI_CBOR_HELPERS
|
|
#define NIM_FFI_CBOR_HELPERS
|
|
// --- minimal growable CBOR request encoder --------------------------------
|
|
typedef struct {
|
|
uint8_t *buf;
|
|
size_t cap;
|
|
size_t len;
|
|
} FfiCbor;
|
|
|
|
static inline FfiCbor ffi_cbor_new(void) {
|
|
FfiCbor c;
|
|
c.cap = 256;
|
|
c.len = 0;
|
|
c.buf = (uint8_t *)malloc(c.cap);
|
|
return c;
|
|
}
|
|
static inline void ffi_cbor_free(FfiCbor *c) {
|
|
free(c->buf);
|
|
c->buf = NULL;
|
|
}
|
|
static inline void ffi_cbor_put(FfiCbor *c, uint8_t b) {
|
|
if (c->len >= c->cap) {
|
|
c->cap *= 2;
|
|
c->buf = (uint8_t *)realloc(c->buf, c->cap);
|
|
}
|
|
c->buf[c->len++] = b;
|
|
}
|
|
static inline void ffi_cbor_head(FfiCbor *c, uint8_t major, uint64_t arg) {
|
|
uint8_t mt = (uint8_t)(major << 5);
|
|
if (arg < 24) {
|
|
ffi_cbor_put(c, mt | (uint8_t)arg);
|
|
} else if (arg <= 0xff) {
|
|
ffi_cbor_put(c, mt | 24);
|
|
ffi_cbor_put(c, (uint8_t)arg);
|
|
} else if (arg <= 0xffff) {
|
|
ffi_cbor_put(c, mt | 25);
|
|
ffi_cbor_put(c, (uint8_t)(arg >> 8));
|
|
ffi_cbor_put(c, (uint8_t)arg);
|
|
} else if (arg <= 0xffffffffULL) {
|
|
ffi_cbor_put(c, mt | 26);
|
|
ffi_cbor_put(c, (uint8_t)(arg >> 24));
|
|
ffi_cbor_put(c, (uint8_t)(arg >> 16));
|
|
ffi_cbor_put(c, (uint8_t)(arg >> 8));
|
|
ffi_cbor_put(c, (uint8_t)arg);
|
|
} else {
|
|
ffi_cbor_put(c, mt | 27);
|
|
for (int s = 56; s >= 0; s -= 8) ffi_cbor_put(c, (uint8_t)(arg >> s));
|
|
}
|
|
}
|
|
static inline void ffi_cbor_map(FfiCbor *c, size_t n) { ffi_cbor_head(c, 5, n); }
|
|
static inline void ffi_cbor_text(FfiCbor *c, const char *s) {
|
|
size_t n = s ? strlen(s) : 0;
|
|
ffi_cbor_head(c, 3, n);
|
|
for (size_t i = 0; i < n; i++) ffi_cbor_put(c, (uint8_t)s[i]);
|
|
}
|
|
static inline void ffi_cbor_kv_text(FfiCbor *c, const char *k, const char *v) {
|
|
ffi_cbor_text(c, k);
|
|
ffi_cbor_text(c, v);
|
|
}
|
|
static inline void ffi_cbor_kv_uint(FfiCbor *c, const char *k, uint64_t v) {
|
|
ffi_cbor_text(c, k);
|
|
ffi_cbor_head(c, 0, v);
|
|
}
|
|
static inline void ffi_cbor_kv_int(FfiCbor *c, const char *k, int64_t v) {
|
|
ffi_cbor_text(c, k);
|
|
if (v >= 0)
|
|
ffi_cbor_head(c, 0, (uint64_t)v);
|
|
else
|
|
ffi_cbor_head(c, 1, (uint64_t)(-(v + 1)));
|
|
}
|
|
|
|
// --- response decoding -----------------------------------------------------
|
|
// Zero-copy view of a top-level CBOR text string (the RET_OK payload). Sets
|
|
// *out/*outLen to point INTO `data` (no allocation; valid only while `data` is)
|
|
// and returns 1; returns 0 for a non-text-string payload.
|
|
static inline int ffi_text_view(const uint8_t *data, size_t len,
|
|
const uint8_t **out, size_t *outLen) {
|
|
if (len < 1 || (data[0] >> 5) != 3) return 0;
|
|
uint8_t info = data[0] & 0x1f;
|
|
size_t p = 1;
|
|
uint64_t slen = 0;
|
|
if (info < 24) {
|
|
slen = info;
|
|
} else if (info == 24) {
|
|
if (len < p + 1) return 0;
|
|
slen = data[p++];
|
|
} else if (info == 25) {
|
|
if (len < p + 2) return 0;
|
|
slen = ((uint64_t)data[p] << 8) | data[p + 1];
|
|
p += 2;
|
|
} else if (info == 26) {
|
|
if (len < p + 4) return 0;
|
|
slen = ((uint64_t)data[p] << 24) | ((uint64_t)data[p + 1] << 16) |
|
|
((uint64_t)data[p + 2] << 8) | data[p + 3];
|
|
p += 4;
|
|
} else {
|
|
return 0;
|
|
}
|
|
if (len < p + slen) return 0;
|
|
*out = data + p;
|
|
*outLen = (size_t)slen;
|
|
return 1;
|
|
}
|
|
|
|
// Owning variant: malloc a NUL-terminated copy. NULL for a non-text payload.
|
|
// Caller frees.
|
|
static inline char *ffi_decode_text(const uint8_t *data, size_t len) {
|
|
const uint8_t *view;
|
|
size_t slen;
|
|
if (!ffi_text_view(data, len, &view, &slen)) return NULL;
|
|
char *out = (char *)malloc(slen + 1);
|
|
if (!out) return NULL;
|
|
memcpy(out, view, slen);
|
|
out[slen] = '\0';
|
|
return out;
|
|
}
|
|
#endif // NIM_FFI_CBOR_HELPERS
|
|
|
|
|
|
// request map keys: {"config": TimerConfig}
|
|
void *my_timer_create_cbor(const uint8_t *reqCbor, size_t reqCborLen, FFICallBack callback, void *userData);
|
|
|
|
// request map keys: {"req": EchoRequest}
|
|
int my_timer_echo_cbor(void *ctx, FFICallBack callback, void *userData, const uint8_t *reqCbor, size_t reqCborLen);
|
|
|
|
// request: empty CBOR map (0xA0)
|
|
int my_timer_version_cbor(void *ctx, FFICallBack callback, void *userData, const uint8_t *reqCbor, size_t reqCborLen);
|
|
|
|
// request map keys: {"req": ComplexRequest}
|
|
int my_timer_complex_cbor(void *ctx, FFICallBack callback, void *userData, const uint8_t *reqCbor, size_t reqCborLen);
|
|
|
|
// request map keys: {"job": JobSpec, "retry": RetryPolicy, "schedule": ScheduleConfig}
|
|
int my_timer_schedule_cbor(void *ctx, FFICallBack callback, void *userData, const uint8_t *reqCbor, size_t reqCborLen);
|
|
|
|
int my_timer_destroy(void *ctx);
|
|
|
|
uint64_t my_timer_add_event_listener(void *ctx, const char *eventName, FFICallBack callback, void *userData);
|
|
int my_timer_remove_event_listener(void *ctx, uint64_t listenerId);
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif
|
|
|
|
#endif /* NIM_FFI_GEN_MY_TIMER_CBOR_H */ |