mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Lib: added a generic CBOR parser framework
This commit is contained in:
parent
850bbf02c1
commit
8d5fcfc6e8
@ -1,4 +1,4 @@
|
||||
src := bitmap.c bitops.c blake2s.c blake2b.c cbor.c cbor_parse_tools.c cbor_shortcuts.c checksum.c defer.c event.c flowspec.c idm.c ip.c lists.c lockfree.c mac.c md5.c mempool.c net.c netindex.c patmatch.c printf.c rcu.c resource.c runtime.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
src := bitmap.c bitops.c blake2s.c blake2b.c cbor.c cbor-parser.c cbor_parse_tools.c cbor_shortcuts.c checksum.c defer.c event.c flowspec.c idm.c ip.c lists.c lockfree.c mac.c md5.c mempool.c net.c netindex.c patmatch.c printf.c rcu.c resource.c runtime.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
obj := $(src-o-files)
|
||||
$(all-lib)
|
||||
|
||||
|
180
lib/cbor-parser.c
Normal file
180
lib/cbor-parser.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* BIRD CBOR parser
|
||||
*
|
||||
* (c) 2024 Maria Matejka <mq@jmq.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include "lib/birdlib.h"
|
||||
#include "lib/cbor.h"
|
||||
|
||||
struct cbor_parser_context *
|
||||
cbor_parser_new(pool *p, uint stack_max_depth)
|
||||
{
|
||||
linpool *lp = lp_new(p);
|
||||
struct cbor_parser_context *ctx = lp_allocz(
|
||||
lp, sizeof *ctx + (stack_max_depth + 1) * sizeof ctx->stack_countdown[0]);
|
||||
|
||||
ctx->lp = lp;
|
||||
ctx->flush = lp_save(lp);
|
||||
|
||||
ctx->type = 0xff;
|
||||
ctx->stack_countdown[0] = 1;
|
||||
ctx->stack_max = stack_max_depth;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void cbor_parser_reset(struct cbor_parser_context *ctx)
|
||||
{
|
||||
lp_restore(ctx->lp, ctx->flush);
|
||||
ctx->flush = lp_save(ctx->lp);
|
||||
|
||||
ctx->type = 0xff;
|
||||
ctx->target_buf = NULL;
|
||||
ctx->target_len = 0;
|
||||
ctx->error = NULL;
|
||||
ctx->partial_state = CPE_TYPE;
|
||||
ctx->partial_countdown = 0;
|
||||
ctx->stack_pos = 0;
|
||||
ctx->stack_countdown[0] = 1;
|
||||
}
|
||||
|
||||
#define CBOR_PARSER_ERROR(...) do { \
|
||||
ctx->error = lp_sprintf(ctx->lp, __VA_ARGS__);\
|
||||
return CPR_ERROR; \
|
||||
} while (0)
|
||||
|
||||
enum cbor_parse_result
|
||||
cbor_parse_byte(struct cbor_parser_context *ctx, const byte bp)
|
||||
{
|
||||
ctx->tflags = 0;
|
||||
|
||||
switch (ctx->partial_state)
|
||||
{
|
||||
case CPE_EXIT:
|
||||
CBOR_PARSER_ERROR("Trailing byte %02x", bp);
|
||||
|
||||
case CPE_ITEM_DONE:
|
||||
bug("You have to check cbor_parse_block_end() before running cbor_parse_byte()");
|
||||
|
||||
case CPE_TYPE:
|
||||
/* Split the byte to type and value */
|
||||
ctx->type = bp >> 5;
|
||||
ctx->value = bp & 0x1f;
|
||||
|
||||
if (ctx->type == 7)
|
||||
{
|
||||
if (ctx->value < 20)
|
||||
CBOR_PARSER_ERROR("Unknown simple value %u", ctx->value);
|
||||
else if (ctx->value < 24)
|
||||
; /* false, true, null, undefined */
|
||||
else if (ctx->value < 28)
|
||||
{
|
||||
/* Need more data */
|
||||
ctx->partial_state = CPE_READ_INT;
|
||||
ctx->partial_countdown = (1 << (ctx->value - 24));
|
||||
ctx->value = 0;
|
||||
break;
|
||||
}
|
||||
else if (ctx->value == 31)
|
||||
; /* break-stop */
|
||||
else
|
||||
CBOR_PARSER_ERROR("Unknown simple value %u", ctx->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx->value < 24)
|
||||
; /* Immediate value, fall through */
|
||||
else if (ctx->value < 28)
|
||||
{
|
||||
/* Need more data */
|
||||
ctx->partial_state = CPE_READ_INT;
|
||||
ctx->partial_countdown = (1 << (ctx->value - 24));
|
||||
ctx->value = 0;
|
||||
break;
|
||||
}
|
||||
else if ((ctx->value == 31) && (ctx->type >= 2) && (ctx->type <= 5))
|
||||
/* Indefinite length, fall through */
|
||||
ctx->tflags |= CPT_VARLEN;
|
||||
else
|
||||
CBOR_PARSER_ERROR("Garbled additional value %u for type %u", ctx->value, ctx->type);
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case CPE_READ_INT:
|
||||
if (ctx->partial_state == CPE_READ_INT)
|
||||
{
|
||||
/* Reading a network order integer */
|
||||
ctx->value <<= 8;
|
||||
ctx->value |= bp;
|
||||
if (--ctx->partial_countdown)
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case CPE_COMPLETE_INT:
|
||||
/* Some types are completely parsed, some not yet */
|
||||
switch (ctx->type)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 7:
|
||||
ctx->partial_state = CPE_ITEM_DONE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
ctx->partial_state = CPE_READ_BYTE;
|
||||
ctx->partial_countdown = ctx->value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
if (++ctx->stack_pos >= ctx->stack_max)
|
||||
CBOR_PARSER_ERROR("Stack too deep");
|
||||
|
||||
/* set array/map size;
|
||||
* once for arrays, twice for maps;
|
||||
* ~0 for indefinite, plus one for the array/map head itself */
|
||||
ctx->stack_countdown[ctx->stack_pos] = (ctx->tflags & CPT_VARLEN) ? ~0ULL :
|
||||
(ctx->value * (ctx->type - 3)) ;
|
||||
ctx->partial_state = CPE_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process the value */
|
||||
return CPR_MAJOR;
|
||||
|
||||
case CPE_READ_BYTE:
|
||||
*ctx->target_buf = bp;
|
||||
ctx->target_buf++;
|
||||
if (--ctx->target_len)
|
||||
break;
|
||||
|
||||
ctx->target_buf = NULL;
|
||||
ctx->partial_state = CPE_ITEM_DONE;
|
||||
return CPR_STR_END;
|
||||
}
|
||||
|
||||
return CPR_MORE;
|
||||
}
|
||||
|
||||
bool
|
||||
cbor_parse_block_end(struct cbor_parser_context *ctx)
|
||||
{
|
||||
if (ctx->partial_state != CPE_ITEM_DONE)
|
||||
return false;
|
||||
|
||||
if (--ctx->stack_countdown[ctx->stack_pos])
|
||||
{
|
||||
ctx->partial_state = CPE_TYPE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctx->stack_pos--)
|
||||
ctx->partial_state = CPE_EXIT;
|
||||
|
||||
return true;
|
||||
}
|
18
lib/cbor.c
18
lib/cbor.c
@ -3,6 +3,24 @@
|
||||
|
||||
#include "lib/cbor.h"
|
||||
|
||||
static const char *cbor_type_str_a[] = {
|
||||
"POSINT",
|
||||
"NEGINT",
|
||||
"BYTES",
|
||||
"TEXT",
|
||||
"ARRAY",
|
||||
"MAP",
|
||||
"TAG",
|
||||
"SPECIAL",
|
||||
};
|
||||
|
||||
const char *
|
||||
cbor_type_str(enum cbor_basic_type t)
|
||||
{
|
||||
return (t < ARRAY_SIZE(cbor_type_str_a)) ?
|
||||
cbor_type_str_a[t] :
|
||||
tmp_sprintf("(unknown: %u)", t);
|
||||
}
|
||||
|
||||
void write_item(struct cbor_writer *writer, uint8_t major, uint64_t num);
|
||||
void check_memory(struct cbor_writer *writer, int add_size);
|
||||
|
76
lib/cbor.h
76
lib/cbor.h
@ -3,6 +3,18 @@
|
||||
|
||||
#include "nest/bird.h"
|
||||
|
||||
enum cbor_basic_type {
|
||||
CBOR_POSINT = 0,
|
||||
CBOR_NEGINT = 1,
|
||||
CBOR_BYTES = 2,
|
||||
CBOR_TEXT = 3,
|
||||
CBOR_ARRAY = 4,
|
||||
CBOR_MAP = 5,
|
||||
CBOR_TAG = 6,
|
||||
CBOR_SPECIAL = 7,
|
||||
};
|
||||
|
||||
const char *cbor_type_str(enum cbor_basic_type);
|
||||
|
||||
struct cbor_writer {
|
||||
int pt; // where will next byte go
|
||||
@ -55,4 +67,68 @@ void cbor_write_item_with_constant_val_length_4(struct cbor_writer *writer, uint
|
||||
|
||||
void rewrite_4bytes_int(struct cbor_writer *writer, int pt, int num);
|
||||
|
||||
/*
|
||||
* Parser bits
|
||||
*/
|
||||
|
||||
struct cbor_parser_context {
|
||||
/* Public part */
|
||||
linpool *lp; /* Linpool for in-parser allocations */
|
||||
|
||||
byte type; /* Last parsed type */
|
||||
enum {
|
||||
CPT_VARLEN = 1,
|
||||
} tflags; /* Additional flags for the type / value pair */
|
||||
u64 value; /* Last parsed (integer) value */
|
||||
|
||||
byte *target_buf; /* Target buf for CBOR_BYTES or CBOR_TEXT */
|
||||
uint target_len; /* Set how many bytes to store */
|
||||
|
||||
const char *error; /* Error message */
|
||||
|
||||
/* Private part */
|
||||
lp_state *flush; /* Linpool reset pointer */
|
||||
|
||||
enum { /* Multi-byte reader */
|
||||
CPE_TYPE = 0,
|
||||
CPE_READ_INT,
|
||||
CPE_COMPLETE_INT,
|
||||
CPE_READ_BYTE,
|
||||
CPE_ITEM_DONE,
|
||||
CPE_EXIT,
|
||||
} partial_state;
|
||||
|
||||
u64 partial_countdown; /* How many items remaining in CBOR_ARRAY / CBOR_MAP */
|
||||
|
||||
uint stack_pos, stack_max; /* Nesting of CBOR_ARRAY / CBOR_MAP */
|
||||
u64 stack_countdown[0];
|
||||
};
|
||||
|
||||
struct cbor_parser_context *cbor_parser_new(pool *, uint stack_max_depth);
|
||||
static inline void cbor_parser_free(struct cbor_parser_context *ctx)
|
||||
{ rfree(ctx->lp); }
|
||||
void cbor_parser_reset(struct cbor_parser_context *ctx);
|
||||
|
||||
enum cbor_parse_result {
|
||||
CPR_ERROR = 0,
|
||||
CPR_MORE = 1,
|
||||
CPR_MAJOR = 2,
|
||||
CPR_STR_END = 3,
|
||||
} cbor_parse_byte(struct cbor_parser_context *, const byte);
|
||||
bool cbor_parse_block_end(struct cbor_parser_context *);
|
||||
|
||||
#define CBOR_PARSE_IF(_ctx, _type, _target) if (((_ctx)->type == CBOR_##_type) && CBOR_STORE_##_type((_ctx), _target))
|
||||
#define CBOR_PARSE_ONLY(_ctx, _type, _target) CBOR_PARSE_IF(_ctx, _type, _target) {} else CBOR_PARSER_ERROR("Expected %s for %s, got %s", #_type, #_target, cbor_type_str((_ctx)->type))
|
||||
|
||||
#define CBOR_STORE_POSINT(_ctx, _target) ((_target = (_ctx)->value), 1)
|
||||
#define CBOR_STORE_NEGINT(_ctx, _target) ((_target = -1LL-(_ctx)->value), 1)
|
||||
#define CBOR_STORE_BYTES(_ctx, _target) ({ \
|
||||
if ((_ctx)->tflags & CPT_VARLEN) CBOR_PARSER_ERROR("Variable length string not supported yet"); \
|
||||
if ((_target)) CBOR_PARSER_ERROR("Duplicate argument %s", #_target); \
|
||||
ASSERT_DIE(!(_ctx)->target_buf); \
|
||||
_target = (_ctx)->target_buf = lp_alloc((_ctx)->lp, ((_ctx)->target_len = (_ctx)->value) + 1); \
|
||||
1; })
|
||||
#define CBOR_STORE_TEXT CBOR_STORE_BYTES
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user