From ccfc1275059b6a1566db2deadcd4bfcbd0b10714 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 7 Oct 2024 15:05:21 +0200 Subject: [PATCH] Lib: mostly rewritten CBOR encoder --- lib/Makefile | 2 +- lib/cbor.c | 183 ++++++++++++++++++++++++++++++++++++++++++--------- lib/cbor.h | 86 +++++++++++++++++++----- 3 files changed, 223 insertions(+), 48 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 963b9061..5593a8bf 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,4 @@ -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 +src := bitmap.c bitops.c blake2s.c blake2b.c cbor.c cbor-parser.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) diff --git a/lib/cbor.c b/lib/cbor.c index cdc1be2a..233758ec 100644 --- a/lib/cbor.c +++ b/lib/cbor.c @@ -3,6 +3,7 @@ #include "lib/cbor.h" +/* String versions of type constants */ static const char *cbor_type_str_a[] = { "POSINT", "NEGINT", @@ -22,62 +23,179 @@ cbor_type_str(enum cbor_basic_type 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); +/* Raw data writing */ -struct cbor_writer *cbor_init(uint8_t *buff, uint32_t capacity, struct linpool *lp) +bool cbor_put_check(struct cbor_writer *w, u64 amount) { - struct cbor_writer *writer = (struct cbor_writer*)lp_alloc(lp, sizeof(struct cbor_writer)); - writer->cbor = buff; - writer->capacity = capacity; - writer->pt = 0; - writer->lp = lp; - return writer; -} - -void cbor_open_block(struct cbor_writer *writer) { // We will need to close the block later manualy - check_memory(writer, 2); - writer->cbor[writer->pt] = 0xbf; - writer->pt++; + return w->data.pos + amount <= w->data.end; } -void cbor_open_list(struct cbor_writer *writer) +#define CBOR_PUT(amount) ({ \ + byte *put = w->data.pos; \ + if ((w->data.pos += (amount)) >= w->data.end) return false; \ + put; }) + +bool cbor_put_raw_u8(struct cbor_writer *w, byte b) { - check_memory(writer, 2); - writer->cbor[writer->pt] = 0x9f; - writer->pt++; + *(CBOR_PUT(1)) = b; + return true; } -void cbor_close_block_or_list(struct cbor_writer *writer) +bool cbor_put_raw_u16(struct cbor_writer *w, u16 val) { - check_memory(writer, 2); - writer->cbor[writer->pt] = 0xff; - writer->pt++; + put_u16(CBOR_PUT(2), val); + return true; } -void cbor_open_block_with_length(struct cbor_writer *writer, uint32_t length) +bool cbor_put_raw_u32(struct cbor_writer *w, u32 val) { - write_item(writer, 5, length); + put_u32(CBOR_PUT(4), val); + return true; } -void cbor_open_list_with_length(struct cbor_writer *writer, uint32_t length) +bool cbor_put_raw_u64(struct cbor_writer *w, u64 val) { - write_item(writer, 4, length); + put_u64(CBOR_PUT(8), val); + return true; } - -void cbor_add_int(struct cbor_writer *writer, int64_t item) +bool cbor_put_raw_data(struct cbor_writer *w, const byte *block, u64 size) { - if (item >= 0) + memcpy(CBOR_PUT(size), block, size); + return true; +} + +/* Basic value putting */ +bool cbor_put(struct cbor_writer *w, enum cbor_basic_type type, u64 value) +{ + ASSERT_DIE((type >= 0) && (type <= 8)); + w->stack[w->stack_pos].items++; + byte tt = type << 5; + if (value < 0x18) + return + cbor_put_raw_u8(w, tt | value); + else if (value < 0x100) + return + cbor_put_raw_u8(w, tt | 0x18) && + cbor_put_raw_u8(w, value); + else if (value < 0x10000) + return + cbor_put_raw_u8(w, tt | 0x19) && + cbor_put_raw_u16(w, value); + else if (value < 0x100000000) + return + cbor_put_raw_u8(w, tt | 0x1a) && + cbor_put_raw_u32(w, value); + else + return + cbor_put_raw_u8(w, tt | 0x1b) && + cbor_put_raw_u64(w, value); +} + +bool cbor_put_int(struct cbor_writer *w, int64_t value) +{ + if (value >= 0) + return cbor_put(w, CBOR_POSINT, value); + else + return cbor_put(w, CBOR_NEGINT, -1-value); +} + +/* Strings */ +bool cbor_put_raw_bytes(struct cbor_writer *w, enum cbor_basic_type type, const byte *block, u64 size) +{ + return + cbor_put(w, type, size) && + cbor_put_raw_data(w, block, size); +} + +/* Arrays and maps */ +bool cbor_put_open(struct cbor_writer *w, enum cbor_basic_type type) +{ + if (++w->stack_pos >= w->stack_max) + return false; + + w->stack[w->stack_pos].head = w->data.pos; + w->stack[w->stack_pos].items = 0; + + return cbor_put(w, type, ~0ULL); +} + +bool cbor_put_close(struct cbor_writer *w, u64 actual_size, bool strict) +{ + ASSERT_DIE(w->stack_pos > 0); + + /* Pop the stack */ + byte *head = w->stack[w->stack_pos].head; + u64 items = w->stack[w->stack_pos].items; + + w->stack_pos--; + + /* Check the original head position */ + ASSERT_DIE((head[0] & 0x1f) == 0x1f); + ASSERT_DIE(w->data.pos >= w->data.start + 9); + switch (head[0] >> 5) { - write_item(writer, 0, item); // 0 is the "major" (three bits) introducing positive int, 1 is for negative + case CBOR_ARRAY: + if (strict && (items != actual_size)) + bug("Inconsistent array item count"); + break; + + case CBOR_MAP: + if (strict && (items != actual_size * 2)) + bug("Inconsistent map item count"); + else if (items & 1) + bug("Trailing map key"); + else + items /= 2; + break; + + default: + bug("Head points to something other than array or map"); + } + + /* Move the data back */ + + if (items < 0x18) + { + memmove(head+1, head+9, w->data.pos - (head+9)); + head[0] &= (0xe0 | items); + w->data.pos -= 8; + } + else if (items < 0x100) + { + memmove(head+2, head+9, w->data.pos - (head+9)); + head[0] &= 0xf8; + head[1] = items; + w->data.pos -= 7; + } + else if (items < 0x10000) + { + memmove(head+3, head+9, w->data.pos - (head+9)); + head[0] &= 0xf9; + put_u16(head+1, items); + w->data.pos -= 6; + } + else if (items < 0x100000000) + { + memmove(head+5, head+9, w->data.pos - (head+9)); + head[0] &= 0xfa; + put_u32(head+1, items); + w->data.pos -= 4; } else { - write_item(writer, 1, -item - 1); + head[0] &= 0xfb; + put_u64(head+1, items); } + + return true; } +/* Tags: TODO! */ + + +#if 0 + void cbor_epoch_time(struct cbor_writer *writer, int64_t time, int shift) { write_item(writer, 6, 1); // 6 is TAG, 1 is tag number for epoch time @@ -253,3 +371,4 @@ void check_memory(struct cbor_writer *writer, int add_size) bug("There is not enough space for cbor response in given buffer"); } } +#endif diff --git a/lib/cbor.h b/lib/cbor.h index f71ca9fb..c3d3b32a 100644 --- a/lib/cbor.h +++ b/lib/cbor.h @@ -3,6 +3,10 @@ #include "nest/bird.h" +/** + * CBOR Commonalities + **/ + enum cbor_basic_type { CBOR_POSINT = 0, CBOR_NEGINT = 1, @@ -16,27 +20,78 @@ enum cbor_basic_type { const char *cbor_type_str(enum cbor_basic_type); +/** + * CBOR Writer + **/ + struct cbor_writer { - int pt; // where will next byte go - int capacity; - int8_t *cbor; - struct linpool *lp; + buffer data; + uint stack_pos, stack_max; /* Nesting of CBOR_ARRAY / CBOR_MAP */ + struct cbor_writer_stack_item { + u64 items; + byte *head; + } stack[0]; }; +/* Initialization */ +static inline struct cbor_writer *cbor_writer_init(struct cbor_writer *w, uint stack_max_depth, byte *buf, uint size) +{ + *w = (struct cbor_writer) { + .data = { + .start = buf, + .pos = buf, + .end = buf + size, + }, + .stack_max = stack_max_depth, + }; + return w; +} -struct cbor_writer *cbor_init(uint8_t *buff, uint32_t capacity, struct linpool *lp); - -void cbor_open_block(struct cbor_writer *writer); - -void cbor_open_list(struct cbor_writer *writer); - -void cbor_close_block_or_list(struct cbor_writer *writer); - -void cbor_open_block_with_length(struct cbor_writer *writer, uint32_t length); - -void cbor_open_list_with_length(struct cbor_writer *writer, uint32_t length); +#define cbor_writer_new(p, smax, buf, size) cbor_writer_init(mb_alloc((p), sizeof(struct cbor_writer) + (smax) * sizeof(struct cbor_writer_stack_item)), (smax), (buf), (size)) +/* Return how many items have been encoded */ +static inline int cbor_writer_done(struct cbor_writer *w) +{ + if (w->stack_pos > 0) + return -1; + else + return w->stack[0].items; +} + +/* Integer types */ +bool cbor_put(struct cbor_writer *w, enum cbor_basic_type type, u64 value); +#define cbor_put_posint(w,v) cbor_put((w), CBOR_POSINT, (v)) +#define cbor_put_negint(w,v) cbor_put((w), CBOR_NEGINT, -1-(v)) +bool cbor_put_int(struct cbor_writer *w, int64_t value); + +/* String types */ +bool cbor_put_raw_bytes(struct cbor_writer *w, enum cbor_basic_type type, const byte *block, u64 size); +#define cbor_put_bytes(w, b, s) cbor_put_raw_bytes((w), CBOR_BYTES, (b), (s)) +#define cbor_put_text(w, b, s) cbor_put_raw_bytes((w), CBOR_TEXT, (b), (s)) +#define cbor_put_string(w, s) cbor_put_raw_bytes((w), CBOR_TEXT, (s), strlen(s)) +#define cbor_put_toks(w, s) cbor_put_raw_bytes((w), CBOR_TEXT, #s, sizeof #s) + +/* Compound types */ +bool cbor_put_open(struct cbor_writer *w, enum cbor_basic_type type); +bool cbor_put_close(struct cbor_writer *w, u64 actual_size, bool strict); +#define cbor_open_array(w) cbor_put_open((w), CBOR_ARRAY) +#define cbor_open_map(w) cbor_put_open((w), CBOR_MAP) + +#define cbor_close_array(w) cbor_put_close((w), 0, 0) +#define cbor_close_map(w) cbor_put_close((w), 0, 0) + +#define CBOR_PUT_ARRAY(w) for (struct cbor_writer *_w = w, *_ww = cbor_open_array(_w) ? (_w) : (bug("buffer overflow on CBOR_ARRAY"), NULL); (_w = NULL), _ww; cbor_close_array(_ww), _ww = NULL) + +#define CBOR_PUT_MAP(w) for (struct cbor_writer *_w = w, *_ww = cbor_open_map(_w) ? (_w) : (bug("buffer overflow on CBOR_MAP"), NULL); (_w = NULL), _ww; cbor_close_map(_ww), _ww = NULL) + +/* Specials */ +#define cbor_put_false(w) cbor_put((w), CBOR_SPECIAL, 20); +#define cbor_put_true(w) cbor_put((w), CBOR_SPECIAL, 21); +#define cbor_put_null(w) cbor_put((w), CBOR_SPECIAL, 22); +#define cbor_put_undef(w) cbor_put((w), CBOR_SPECIAL, 23); + +#if 0 void cbor_add_int(struct cbor_writer *writer, int64_t item); void cbor_add_ipv4(struct cbor_writer *writer, ip4_addr); @@ -66,6 +121,7 @@ void write_item(struct cbor_writer *writer, uint8_t major, uint64_t num); void cbor_write_item_with_constant_val_length_4(struct cbor_writer *writer, uint8_t major, uint64_t num); void rewrite_4bytes_int(struct cbor_writer *writer, int pt, int num); +#endif /* * Parser bits