libwebsockets/test-apps/test-lecp.c
Andy Green dcaa0013b4 lecp: add CBOR stream parser LECP like JSON LEJP
This provides very memory-efficient CBOR stream parsing
and writing.

The parser  converts pieces of CBOR into callbacks that define
the structure and collate string and blobs into buffer chunks
for extensible and easy access.

It is fragementation-safe and does not need all the CBOR in
the same place at one time, chunks of CBOR are parsed and
discarded as provided.

It does not allocate and just needs a few hundred bytes of
stack for even huge CBOR objects.  Huge strings and blobs
are handled without needing memory to hold them atomically.

Includes ./minimal-examples/api-tests/api-test-lecp that
unit tests it against 82 official example CBORs and
26 additional test vectors from COSE (just checking the CBOR
parsing).

The writing apis allow printf style semantics with a variety
of CBOR-aware %-formats.  The apis write into a context that
manages output buffer usage, if the output buffer fills,
then the apis return with an AGAIN code that lets you issue
and reset the output buffer and repeat the api all to issue
more output.  The subsequent calls can occur much later or
from a different function context, so this is perfect for
WRITEABLE-mediated output from the network parts of lws.

See ./READMEs/README.cbor-lecp.md
2021-08-21 17:44:40 +01:00

171 lines
3.9 KiB
C

/*
* lejp test app
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates a minimal http server that performs a form GET with a couple
* of parameters. It dumps the parameters to the console log and redirects
* to another page.
*/
#include <libwebsockets.h>
#include <string.h>
static const char * const reason_names[] = {
"LECPCB_CONSTRUCTED",
"LECPCB_DESTRUCTED",
"LECPCB_START",
"LECPCB_COMPLETE",
"LECPCB_FAILED",
"LECPCB_PAIR_NAME",
"LECPCB_VAL_TRUE",
"LECPCB_VAL_FALSE",
"LECPCB_VAL_NULL",
"LECPCB_VAL_NUM_INT",
"LECPCB_VAL_RESERVED", /* float in lejp */
"LECPCB_VAL_STR_START",
"LECPCB_VAL_STR_CHUNK",
"LECPCB_VAL_STR_END",
"LECPCB_ARRAY_START",
"LECPCB_ARRAY_END",
"LECPCB_OBJECT_START",
"LECPCB_OBJECT_END",
"LECPCB_TAG_START",
"LECPCB_TAG_END",
"LECPCB_VAL_NUM_UINT",
"LECPCB_VAL_UNDEFINED",
"LECPCB_VAL_FLOAT16",
"LECPCB_VAL_FLOAT32",
"LECPCB_VAL_FLOAT64",
"LECPCB_VAL_SIMPLE",
"LECPCB_VAL_BLOB_START",
"LECPCB_VAL_BLOB_CHUNK",
"LECPCB_VAL_BLOB_END",
};
static const char * const tok[] = {
"dummy___"
};
static signed char
cb(struct lecp_ctx *ctx, char reason)
{
char buf[1024], *p = buf, *end = &buf[sizeof(buf)];
int n;
for (n = 0; n < ctx->sp; n++)
*p++ = ' ';
*p = '\0';
lwsl_notice("%s%s: path %s match %d statckp %d\r\n", buf,
reason_names[(unsigned int)(reason) &
(LEJP_FLAG_CB_IS_VALUE - 1)], ctx->path,
ctx->path_match, ctx->pst[ctx->pst_sp].ppos);
if (reason & LECP_FLAG_CB_IS_VALUE) {
switch (reason) {
case LECPCB_VAL_NUM_UINT:
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
" value %llu ",
(unsigned long long)ctx->item.u.u64);
break;
case LECPCB_VAL_STR_START:
case LECPCB_VAL_STR_CHUNK:
case LECPCB_VAL_STR_END:
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
" value '%s' ", ctx->buf);
break;
case LECPCB_VAL_BLOB_START:
case LECPCB_VAL_BLOB_CHUNK:
case LECPCB_VAL_BLOB_END:
if (ctx->npos)
lwsl_hexdump_notice(ctx->buf, (size_t)ctx->npos);
break;
case LECPCB_VAL_NUM_INT:
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
" value %lld ",
(long long)ctx->item.u.i64);
break;
case LECPCB_VAL_FLOAT16:
case LECPCB_VAL_FLOAT32:
case LECPCB_VAL_FLOAT64:
break;
case LECPCB_VAL_SIMPLE:
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
" simple %llu ",
(unsigned long long)ctx->item.u.u64);
break;
}
if (ctx->ipos) {
int n;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "(array indexes: ");
for (n = 0; n < ctx->ipos; n++)
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%d ", ctx->i[n]);
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ") ");
}
lwsl_notice("%s \r\n", buf);
(void)reason_names; /* NO_LOGS... */
return 0;
}
switch (reason) {
case LECPCB_COMPLETE:
lwsl_notice("%sParsing Completed (LEJPCB_COMPLETE)\n", buf);
break;
case LECPCB_PAIR_NAME:
lwsl_notice("%spath: '%s' (LEJPCB_PAIR_NAME)\n", buf, ctx->path);
break;
case LECPCB_TAG_START:
lwsl_notice("LECPCB_TAG_START: %llu\r\n", (unsigned long long)ctx->item.u.u64);
return 0;
}
return 0;
}
int
main(int argc, char *argv[])
{
int fd, n = 1, ret = 1, m = 0;
struct lecp_ctx ctx;
char buf[128];
lws_set_log_level(7, NULL);
lwsl_notice("libwebsockets-test-lecp (C) 2017 - 2021 andy@warmcat.com\n");
lwsl_notice(" usage: cat my.cbor | libwebsockets-test-lecp\n\n");
lecp_construct(&ctx, cb, NULL, tok, LWS_ARRAY_SIZE(tok));
fd = 0;
while (n > 0) {
n = (int)read(fd, buf, sizeof(buf));
if (n <= 0)
continue;
m = lecp_parse(&ctx, (uint8_t *)buf, (size_t)n);
if (m < 0 && m != LEJP_CONTINUE) {
lwsl_err("parse failed %d\n", m);
goto bail;
}
}
lwsl_notice("okay (%d)\n", m);
ret = 0;
bail:
lecp_destruct(&ctx);
return ret;
}