#endif // MJSON_ENABLE_PRINT
-#if MJSON_ENABLE_RPC
-
-void jsonrpc_init(mjson_print_fn_t, void *userdata);
-int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
-
-struct jsonrpc_request {
- struct jsonrpc_ctx *ctx;
- const char *frame; // Points to the whole frame
- int frame_len; // Frame length
- const char *params; // Points to the "params" in the request frame
- int params_len; // Length of the "params"
- const char *id; // Points to the "id" in the request frame
- int id_len; // Length of the "id"
- const char *method; // Points to the "method" in the request frame
- int method_len; // Length of the "method"
- mjson_print_fn_t fn; // Printer function
- void *fndata; // Printer function data
- void *userdata; // Callback's user data as specified at export time
-};
-
-struct jsonrpc_method {
- const char *method;
- int method_sz;
- void (*cb)(struct jsonrpc_request *);
- struct jsonrpc_method *next;
-};
-
-// Main RPC context, stores current request information and a list of
-// exported RPC methods.
-struct jsonrpc_ctx {
- struct jsonrpc_method *methods;
- mjson_print_fn_t response_cb;
- void *response_cb_data;
-};
-
-// Registers function fn under the given name within the given RPC context
-#define jsonrpc_ctx_export(ctx, name, fn) \
- do { \
- static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0}; \
- m.next = (ctx)->methods; \
- (ctx)->methods = &m; \
- } while (0)
-
-void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
-void jsonrpc_return_error(struct jsonrpc_request *r, int code,
- const char *message, const char *data_fmt, ...);
-void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
- ...);
-void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
- mjson_print_fn_t fn, void *fndata, void *userdata);
-
-extern struct jsonrpc_ctx jsonrpc_default_context;
-
-#define jsonrpc_export(name, fn) \
- jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
-
-#define jsonrpc_process(buf, len, fn, fnd, ud) \
- jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
-
-#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
-#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
-#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
-#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
-
-#endif // MJSON_ENABLE_RPC
#ifdef __cplusplus
}
#endif
return d;
}
-#if MJSON_ENABLE_MERGE
-int mjson_merge(const char *s, int n, const char *s2, int n2,
- mjson_print_fn_t fn, void *userdata) {
- int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
- if (n < 2) return len;
- len += fn("{", 1, userdata);
- while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
- char *path = (char *) alloca(klen + 1);
- const char *val;
- memcpy(path, "$.", 2);
- memcpy(path + 2, s + koff + 1, klen - 2);
- path[klen] = '\0';
- if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
- if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
- } else {
- val = s + voff; // Key is not found in the update. Copy the old value.
- }
- if (comma) len += fn(",", 1, userdata);
- len += fn(s + koff, klen, userdata);
- len += fn(":", 1, userdata);
- if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
- len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
- } else {
- if (t2 != MJSON_TOK_INVALID) vlen = k;
- len += fn(val, vlen, userdata);
- }
- comma = 1;
- }
- // Add missing keys
- off = 0;
- while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
- char *path = (char *) alloca(klen + 1);
- const char *val;
- if (t == MJSON_TOK_NULL) continue;
- memcpy(path, "$.", 2);
- memcpy(path + 2, s2 + koff + 1, klen - 2);
- path[klen] = '\0';
- if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
- if (comma) len += fn(",", 1, userdata);
- len += fn(s2 + koff, klen, userdata);
- len += fn(":", 1, userdata);
- len += fn(s2 + voff, vlen, userdata);
- comma = 1;
- }
- len += fn("}", 1, userdata);
- return len;
-}
-#endif // MJSON_ENABLE_MERGE
-
-#if MJSON_ENABLE_PRETTY
-struct prettydata {
- int level;
- int len;
- int prev;
- const char *pad;
- int padlen;
- mjson_print_fn_t fn;
- void *userdata;
-};
-
-static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
- struct prettydata *d = (struct prettydata *) ud;
- int i;
- switch (ev) {
- case '{':
- case '[':
- d->level++;
- d->len += d->fn(s + off, len, d->userdata);
- break;
- case '}':
- case ']':
- d->level--;
- if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
- d->len += d->fn("\n", 1, d->userdata);
- for (i = 0; i < d->level; i++)
- d->len += d->fn(d->pad, d->padlen, d->userdata);
- }
- d->len += d->fn(s + off, len, d->userdata);
- break;
- case ',':
- d->len += d->fn(s + off, len, d->userdata);
- if (d->padlen > 0) {
- d->len += d->fn("\n", 1, d->userdata);
- for (i = 0; i < d->level; i++)
- d->len += d->fn(d->pad, d->padlen, d->userdata);
- }
- break;
- case ':':
- d->len += d->fn(s + off, len, d->userdata);
- if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
- break;
- case MJSON_TOK_KEY:
- if (d->prev == '{' && d->padlen > 0) {
- d->len += d->fn("\n", 1, d->userdata);
- for (i = 0; i < d->level; i++)
- d->len += d->fn(d->pad, d->padlen, d->userdata);
- }
- d->len += d->fn(s + off, len, d->userdata);
- break;
- default:
- if (d->prev == '[' && d->padlen > 0) {
- d->len += d->fn("\n", 1, d->userdata);
- for (i = 0; i < d->level; i++)
- d->len += d->fn(d->pad, d->padlen, d->userdata);
- }
- d->len += d->fn(s + off, len, d->userdata);
- break;
- }
- d->prev = ev;
- return 0;
-}
-
-int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
- void *userdata) {
- struct prettydata d = {0, 0, 0, pad, (int) strlen(pad), fn, userdata};
- if (mjson(s, n, pretty_cb, &d) < 0) return -1;
- return d.len;
-}
-#endif // MJSON_ENABLE_PRETTY
-
-#if MJSON_ENABLE_RPC
-struct jsonrpc_ctx jsonrpc_default_context;
-
-int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
- int i = 0, j = 0, ni = 0, nj = 0;
- while (i < n1 || j < n2) {
- if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
- i++, j++;
- } else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
- ni = i, nj = j + 1, i++;
- } else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
- i = ni, j = nj;
- } else {
- return 0;
- }
- }
- return 1;
-}
-
-void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
- const char *message, const char *data_fmt,
- va_list ap) {
- if (r->id_len == 0) return;
- mjson_printf(r->fn, r->fndata,
- "{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
- r->id, code, message == NULL ? "" : message);
- if (data_fmt != NULL) {
- mjson_printf(r->fn, r->fndata, ",\"data\":");
- mjson_vprintf(r->fn, r->fndata, data_fmt, ap);
- }
- mjson_printf(r->fn, r->fndata, "}}\n");
-}
-
-void jsonrpc_return_error(struct jsonrpc_request *r, int code,
- const char *message, const char *data_fmt, ...) {
- va_list ap;
- va_start(ap, data_fmt);
- jsonrpc_return_errorv(r, code, message, data_fmt, ap);
- va_end(ap);
-}
-
-void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
- va_list ap) {
- if (r->id_len == 0) return;
- mjson_printf(r->fn, r->fndata, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
- if (result_fmt != NULL) {
- mjson_vprintf(r->fn, r->fndata, result_fmt, ap);
- } else {
- mjson_printf(r->fn, r->fndata, "%s", "null");
- }
- mjson_printf(r->fn, r->fndata, "}\n");
-}
-
-void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
- ...) {
- va_list ap;
- va_start(ap, result_fmt);
- jsonrpc_return_successv(r, result_fmt, ap);
- va_end(ap);
-}
-
-void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *buf, int len,
- mjson_print_fn_t fn, void *fndata, void *ud) {
- const char *result = NULL, *error = NULL;
- int result_sz = 0, error_sz = 0;
- struct jsonrpc_method *m = NULL;
- struct jsonrpc_request r = {ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fndata, ud};
-
- // Is is a response frame?
- mjson_find(buf, len, "$.result", &result, &result_sz);
- if (result == NULL) mjson_find(buf, len, "$.error", &error, &error_sz);
- if (result_sz > 0 || error_sz > 0) {
- if (ctx->response_cb) ctx->response_cb(buf, len, ctx->response_cb_data);
- return;
- }
-
- // Method must exist and must be a string
- if (mjson_find(buf, len, "$.method", &r.method, &r.method_len) !=
- MJSON_TOK_STRING) {
- mjson_printf(fn, fndata, "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
- len, buf);
- return;
- }
-
- // id and params are optional
- mjson_find(buf, len, "$.id", &r.id, &r.id_len);
- mjson_find(buf, len, "$.params", &r.params, &r.params_len);
-
- for (m = ctx->methods; m != NULL; m = m->next) {
- if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
- r.method_len - 2) > 0) {
- if (r.params == NULL) r.params = "";
- m->cb(&r);
- break;
- }
- }
- if (m == NULL) {
- jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
- }
-}
-static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fndata,
- va_list *ap) {
- struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
- struct jsonrpc_method *m;
- int len = 0;
- for (m = ctx->methods; m != NULL; m = m->next) {
- if (m != ctx->methods) len += mjson_print_buf(fn, fndata, ",", 1);
- len += mjson_print_str(fn, fndata, m->method, (int) strlen(m->method));
- }
- return len;
-}
-
-static void rpclist(struct jsonrpc_request *r) {
- jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->ctx);
-}
-
-void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
- void *response_cb_data) {
- ctx->response_cb = response_cb;
- ctx->response_cb_data = response_cb_data;
- jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, rpclist);
-}
-
-void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
- jsonrpc_ctx_init(&jsonrpc_default_context, response_cb, userdata);
-}
-#endif // MJSON_ENABLE_RPC