Path: blob/main/contrib/libxo/encoder/cbor/enc_cbor.c
48255 views
/*1* Copyright (c) 2015, Juniper Networks, Inc.2* All rights reserved.3* This SOFTWARE is licensed under the LICENSE provided in the4* ../Copyright file. By downloading, installing, copying, or otherwise5* using the SOFTWARE, you agree to be bound by the terms of that6* LICENSE.7* Phil Shafer, August 20158*/910/*11* CBOR (RFC 7049) mades a suitable test case for libxo's external12* encoder API. It's simple, streaming, well documented, and an13* IETF standard.14*15* This encoder uses the "pretty" flag for diagnostics, which isn't16* really kosher, but it's example code.17*/1819#include <string.h>20#include <sys/types.h>21#include <unistd.h>22#include <stdint.h>23#include <ctype.h>24#include <stdlib.h>25#include <limits.h>2627#include "xo.h"28#include "xo_encoder.h"29#include "xo_buf.h"3031/*32* memdump(): dump memory contents in hex/ascii330 1 2 3 4 5 6 734012345678901234567890123456789012345678901234567890123456789012345678901234535XX XX XX XX XX XX XX XX - XX XX XX XX XX XX XX XX abcdefghijklmnop36*/37static void38cbor_memdump (FILE *fp, const char *title, const char *data,39size_t len, const char *tag, int indent)40{41enum { MAX_PER_LINE = 16 };42char buf[ 80 ];43char text[ 80 ];44char *bp, *tp;45size_t i;46#if 047static const int ends[ MAX_PER_LINE ] = { 2, 5, 8, 11, 15, 18, 21, 24,4829, 32, 35, 38, 42, 45, 48, 51 };49#endif5051if (fp == NULL)52fp = stdout;53if (tag == NULL)54tag = "";5556fprintf(fp, "%*s[%s] @ %p (%lx/%lu)\n", indent + 1, tag,57title, data, (unsigned long) len, (unsigned long) len);5859while (len > 0) {60bp = buf;61tp = text;6263for (i = 0; i < MAX_PER_LINE && i < len; i++) {64if (i && (i % 4) == 0) *bp++ = ' ';65if (i == 8) {66*bp++ = '-';67*bp++ = ' ';68}69sprintf(bp, "%02x ", (unsigned char) *data);70bp += strlen(bp);71*tp++ = (isprint((int) *data) && *data >= ' ') ? *data : '.';72data += 1;73}7475*tp = 0;76*bp = 0;77fprintf(fp, "%*s%-54s%s\n", indent + 1, tag, buf, text);78len -= i;79}80}8182/*83* CBOR breaks the first byte into two pieces, the major type in the84* top 3 bits and the minor value in the low 5 bits. The value can be85* a small value (0 .. 23), an 8-bit value (24), a 16-bit value (25),86* a 32-bit value (26), or a 64-bit value (27). A value of 3187* represents an unknown length, which we'll use extensively for88* streaming our content.89*/90#define CBOR_MAJOR_MASK 0xE091#define CBOR_MINOR_MASK 0x1F92#define CBOR_MAJOR_SHIFT 59394#define CBOR_MAJOR(_x) ((_x) & CBOR_MAJOR_MASK)95#define CBOR_MAJOR_VAL(_x) ((_x) << CBOR_MAJOR_SHIFT)96#define CBOR_MINOR_VAL(_x) ((_x) & CBOR_MINOR_MASK)9798/* Major type codes */99#define CBOR_UNSIGNED CBOR_MAJOR_VAL(0) /* 0x00 */100#define CBOR_NEGATIVE CBOR_MAJOR_VAL(1) /* 0x20 */101#define CBOR_BYTES CBOR_MAJOR_VAL(2) /* 0x40 */102#define CBOR_STRING CBOR_MAJOR_VAL(3) /* 0x60 */103#define CBOR_ARRAY CBOR_MAJOR_VAL(4) /* 0x80 */104#define CBOR_MAP CBOR_MAJOR_VAL(5) /* 0xa0 */105#define CBOR_SEMANTIC CBOR_MAJOR_VAL(6) /* 0xc0 */106#define CBOR_SPECIAL CBOR_MAJOR_VAL(7) /* 0xe0 */107108#define CBOR_ULIMIT 24 /* Largest unsigned value */109#define CBOR_NLIMIT 23 /* Largest negative value */110111#define CBOR_BREAK 0xFF112#define CBOR_INDEF 0x1F113114#define CBOR_FALSE 0xF4115#define CBOR_TRUE 0xF5116#define CBOR_NULL 0xF6117#define CBOR_UNDEF 0xF7118119#define CBOR_LEN8 0x18 /* 24 - 8-bit value */120#define CBOR_LEN16 0x19 /* 25 - 16-bit value */121#define CBOR_LEN32 0x1a /* 26 - 32-bit value */122#define CBOR_LEN64 0x1b /* 27 - 64-bit value */123#define CBOR_LEN128 0x1c /* 28 - 128-bit value */124125typedef struct cbor_private_s {126xo_buffer_t c_data; /* Our data buffer */127unsigned c_indent; /* Indent level */128unsigned c_open_leaf_list; /* Open leaf list construct? */129} cbor_private_t;130131static void132cbor_encode_uint (xo_buffer_t *xbp, uint64_t minor, unsigned limit)133{134char *bp = xbp->xb_curp;135int i, m;136137if (minor > (1ULL << 32)) {138*bp++ |= CBOR_LEN64;139m = 64;140141} else if (minor > (1<<16)) {142*bp++ |= CBOR_LEN32;143m = 32;144145} else if (minor > (1<<8)) {146*bp++ |= CBOR_LEN16;147m = 16;148149} else if (minor > limit) {150*bp++ |= CBOR_LEN8;151m = 8;152} else {153*bp++ |= minor & CBOR_MINOR_MASK;154m = 0;155}156157if (m) {158for (i = m - 8; i >= 0; i -= 8)159*bp++ = minor >> i;160}161162xbp->xb_curp = bp;163}164165static void166cbor_append (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp,167unsigned major, unsigned minor, const char *data)168{169if (!xo_buf_has_room(xbp, minor + 2))170return;171172unsigned offset = xo_buf_offset(xbp);173174*xbp->xb_curp = major;175cbor_encode_uint(xbp, minor, CBOR_ULIMIT);176if (data)177xo_buf_append(xbp, data, minor);178179if (xo_get_flags(xop) & XOF_PRETTY)180cbor_memdump(stdout, "append", xo_buf_data(xbp, offset),181xbp->xb_curp - xbp->xb_bufp - offset, "",182cbor->c_indent * 2);183}184185static int186cbor_create (xo_handle_t *xop)187{188cbor_private_t *cbor = xo_realloc(NULL, sizeof(*cbor));189if (cbor == NULL)190return -1;191192bzero(cbor, sizeof(*cbor));193xo_buf_init(&cbor->c_data);194195xo_set_private(xop, cbor);196197cbor_append(xop, cbor, &cbor->c_data, CBOR_MAP | CBOR_INDEF, 0, NULL);198199return 0;200}201202static int203cbor_content (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp,204const char *value)205{206int rc = 0;207208unsigned offset = xo_buf_offset(xbp);209210if (value == NULL || *value == '\0' || xo_streq(value, "true"))211cbor_append(xop, cbor, &cbor->c_data, CBOR_TRUE, 0, NULL);212else if (xo_streq(value, "false"))213cbor_append(xop, cbor, &cbor->c_data, CBOR_FALSE, 0, NULL);214else {215int negative = 0;216if (*value == '-') {217value += 1;218negative = 1;219}220221char *ep;222unsigned long long ival;223ival = strtoull(value, &ep, 0);224if (ival == ULLONG_MAX) /* Sometimes a string is just a string */225cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value);226else {227*xbp->xb_curp = negative ? CBOR_NEGATIVE : CBOR_UNSIGNED;228if (negative)229ival -= 1; /* Don't waste a negative zero */230cbor_encode_uint(xbp, ival, negative ? CBOR_NLIMIT : CBOR_ULIMIT);231}232}233234if (xo_get_flags(xop) & XOF_PRETTY)235cbor_memdump(stdout, "content", xo_buf_data(xbp, offset),236xbp->xb_curp - xbp->xb_bufp - offset, "",237cbor->c_indent * 2);238239return rc;240}241242static int243cbor_handler (XO_ENCODER_HANDLER_ARGS)244{245int rc = 0;246cbor_private_t *cbor = private;247xo_buffer_t *xbp = cbor ? &cbor->c_data : NULL;248249if (xo_get_flags(xop) & XOF_PRETTY) {250printf("%*sop %s: [%s] [%s]\n", cbor ? cbor->c_indent * 2 + 4 : 0, "",251xo_encoder_op_name(op), name, value);252fflush(stdout);253}254255/* If we don't have private data, we're sunk */256if (cbor == NULL && op != XO_OP_CREATE)257return -1;258259switch (op) {260case XO_OP_CREATE: /* Called when the handle is init'd */261rc = cbor_create(xop);262break;263264case XO_OP_OPEN_CONTAINER:265cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name);266cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL);267cbor->c_indent += 1;268break;269270case XO_OP_CLOSE_CONTAINER:271cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL);272cbor->c_indent -= 1;273break;274275case XO_OP_OPEN_LIST:276cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name);277cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL);278cbor->c_indent += 1;279break;280281case XO_OP_CLOSE_LIST:282cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL);283cbor->c_indent -= 1;284break;285286case XO_OP_OPEN_LEAF_LIST:287cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name);288cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL);289cbor->c_indent += 1;290cbor->c_open_leaf_list = 1;291break;292293case XO_OP_CLOSE_LEAF_LIST:294cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL);295cbor->c_indent -= 1;296cbor->c_open_leaf_list = 0;297break;298299case XO_OP_OPEN_INSTANCE:300cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL);301cbor->c_indent += 1;302break;303304case XO_OP_CLOSE_INSTANCE:305cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL);306cbor->c_indent -= 1;307break;308309case XO_OP_STRING: /* Quoted UTF-8 string */310if (!cbor->c_open_leaf_list)311cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name);312cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value);313break;314315case XO_OP_CONTENT: /* Other content */316if (!cbor->c_open_leaf_list)317cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name);318319/*320* It's content, not string, so we need to look at the321* string and build some content. Turns out we only322* care about true, false, null, and numbers.323*/324cbor_content(xop, cbor, xbp, value);325break;326327case XO_OP_FINISH: /* Clean up function */328cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL);329cbor->c_indent -= 1;330break;331332case XO_OP_FLUSH: /* Clean up function */333if (xo_get_flags(xop) & XOF_PRETTY)334cbor_memdump(stdout, "cbor",335xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp,336">", 0);337else {338rc = write(1, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);339if (rc > 0)340rc = 0;341}342break;343344case XO_OP_DESTROY: /* Clean up function */345break;346347case XO_OP_ATTRIBUTE: /* Attribute name/value */348break;349350case XO_OP_VERSION: /* Version string */351break;352353}354355return rc;356}357358int359xo_encoder_library_init (XO_ENCODER_INIT_ARGS)360{361arg->xei_handler = cbor_handler;362arg->xei_version = XO_ENCODER_VERSION;363364return 0;365}366367368