nim-ffi/examples/timer/c_bindings/my_timer_cbor.h
Ivan FB c5c7c373b4
docs(examples): add native (same-process) C example
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>
2026-05-31 18:37:06 +02:00

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 */