/* * Copyright (c) 2015, Juniper Networks, Inc. * All rights reserved. * This SOFTWARE is licensed under the LICENSE provided in the * ../Copyright file. By downloading, installing, copying, or otherwise * using the SOFTWARE, you agree to be bound by the terms of that * LICENSE. * Phil Shafer, August 2015 */ /* * CBOR (RFC 7049) mades a suitable test case for libxo's external * encoder API. It's simple, streaming, well documented, and an * IETF standard. * * This encoder uses the "pretty" flag for diagnostics, which isn't * really kosher, but it's example code. */ #include #include #include #include #include #include #include #include "xo.h" #include "xo_encoder.h" #include "xo_buf.h" /* * memdump(): dump memory contents in hex/ascii 0 1 2 3 4 5 6 7 0123456789012345678901234567890123456789012345678901234567890123456789012345 XX XX XX XX XX XX XX XX - XX XX XX XX XX XX XX XX abcdefghijklmnop */ static void cbor_memdump (FILE *fp, const char *title, const char *data, size_t len, const char *tag, int indent) { enum { MAX_PER_LINE = 16 }; char buf[ 80 ]; char text[ 80 ]; char *bp, *tp; size_t i; #if 0 static const int ends[ MAX_PER_LINE ] = { 2, 5, 8, 11, 15, 18, 21, 24, 29, 32, 35, 38, 42, 45, 48, 51 }; #endif if (fp == NULL) fp = stdout; if (tag == NULL) tag = ""; fprintf(fp, "%*s[%s] @ %p (%lx/%lu)\n", indent + 1, tag, title, data, (unsigned long) len, (unsigned long) len); while (len > 0) { bp = buf; tp = text; for (i = 0; i < MAX_PER_LINE && i < len; i++) { if (i && (i % 4) == 0) *bp++ = ' '; if (i == 8) { *bp++ = '-'; *bp++ = ' '; } sprintf(bp, "%02x ", (unsigned char) *data); bp += strlen(bp); *tp++ = (isprint((int) *data) && *data >= ' ') ? *data : '.'; data += 1; } *tp = 0; *bp = 0; fprintf(fp, "%*s%-54s%s\n", indent + 1, tag, buf, text); len -= i; } } /* * CBOR breaks the first byte into two pieces, the major type in the * top 3 bits and the minor value in the low 5 bits. The value can be * a small value (0 .. 23), an 8-bit value (24), a 16-bit value (25), * a 32-bit value (26), or a 64-bit value (27). A value of 31 * represents an unknown length, which we'll use extensively for * streaming our content. */ #define CBOR_MAJOR_MASK 0xE0 #define CBOR_MINOR_MASK 0x1F #define CBOR_MAJOR_SHIFT 5 #define CBOR_MAJOR(_x) ((_x) & CBOR_MAJOR_MASK) #define CBOR_MAJOR_VAL(_x) ((_x) << CBOR_MAJOR_SHIFT) #define CBOR_MINOR_VAL(_x) ((_x) & CBOR_MINOR_MASK) /* Major type codes */ #define CBOR_UNSIGNED CBOR_MAJOR_VAL(0) /* 0x00 */ #define CBOR_NEGATIVE CBOR_MAJOR_VAL(1) /* 0x20 */ #define CBOR_BYTES CBOR_MAJOR_VAL(2) /* 0x40 */ #define CBOR_STRING CBOR_MAJOR_VAL(3) /* 0x60 */ #define CBOR_ARRAY CBOR_MAJOR_VAL(4) /* 0x80 */ #define CBOR_MAP CBOR_MAJOR_VAL(5) /* 0xa0 */ #define CBOR_SEMANTIC CBOR_MAJOR_VAL(6) /* 0xc0 */ #define CBOR_SPECIAL CBOR_MAJOR_VAL(7) /* 0xe0 */ #define CBOR_ULIMIT 24 /* Largest unsigned value */ #define CBOR_NLIMIT 23 /* Largest negative value */ #define CBOR_BREAK 0xFF #define CBOR_INDEF 0x1F #define CBOR_FALSE 0xF4 #define CBOR_TRUE 0xF5 #define CBOR_NULL 0xF6 #define CBOR_UNDEF 0xF7 #define CBOR_LEN8 0x18 /* 24 - 8-bit value */ #define CBOR_LEN16 0x19 /* 25 - 16-bit value */ #define CBOR_LEN32 0x1a /* 26 - 32-bit value */ #define CBOR_LEN64 0x1b /* 27 - 64-bit value */ #define CBOR_LEN128 0x1c /* 28 - 128-bit value */ typedef struct cbor_private_s { xo_buffer_t c_data; /* Our data buffer */ unsigned c_indent; /* Indent level */ unsigned c_open_leaf_list; /* Open leaf list construct? */ } cbor_private_t; static void cbor_encode_uint (xo_buffer_t *xbp, uint64_t minor, unsigned limit) { char *bp = xbp->xb_curp; int i, m; if (minor > (1ULL << 32)) { *bp++ |= CBOR_LEN64; m = 64; } else if (minor > (1<<16)) { *bp++ |= CBOR_LEN32; m = 32; } else if (minor > (1<<8)) { *bp++ |= CBOR_LEN16; m = 16; } else if (minor > limit) { *bp++ |= CBOR_LEN8; m = 8; } else { *bp++ |= minor & CBOR_MINOR_MASK; m = 0; } if (m) { for (i = m - 8; i >= 0; i -= 8) *bp++ = minor >> i; } xbp->xb_curp = bp; } static void cbor_append (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp, unsigned major, unsigned minor, const char *data) { if (!xo_buf_has_room(xbp, minor + 2)) return; unsigned offset = xo_buf_offset(xbp); *xbp->xb_curp = major; cbor_encode_uint(xbp, minor, CBOR_ULIMIT); if (data) xo_buf_append(xbp, data, minor); if (xo_get_flags(xop) & XOF_PRETTY) cbor_memdump(stdout, "append", xo_buf_data(xbp, offset), xbp->xb_curp - xbp->xb_bufp - offset, "", cbor->c_indent * 2); } static int cbor_create (xo_handle_t *xop) { cbor_private_t *cbor = xo_realloc(NULL, sizeof(*cbor)); if (cbor == NULL) return -1; bzero(cbor, sizeof(*cbor)); xo_buf_init(&cbor->c_data); xo_set_private(xop, cbor); cbor_append(xop, cbor, &cbor->c_data, CBOR_MAP | CBOR_INDEF, 0, NULL); return 0; } static int cbor_content (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp, const char *value) { int rc = 0; unsigned offset = xo_buf_offset(xbp); if (value == NULL || *value == '\0' || xo_streq(value, "true")) cbor_append(xop, cbor, &cbor->c_data, CBOR_TRUE, 0, NULL); else if (xo_streq(value, "false")) cbor_append(xop, cbor, &cbor->c_data, CBOR_FALSE, 0, NULL); else { int negative = 0; if (*value == '-') { value += 1; negative = 1; } char *ep; unsigned long long ival; ival = strtoull(value, &ep, 0); if (ival == ULLONG_MAX) /* Sometimes a string is just a string */ cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value); else { *xbp->xb_curp = negative ? CBOR_NEGATIVE : CBOR_UNSIGNED; if (negative) ival -= 1; /* Don't waste a negative zero */ cbor_encode_uint(xbp, ival, negative ? CBOR_NLIMIT : CBOR_ULIMIT); } } if (xo_get_flags(xop) & XOF_PRETTY) cbor_memdump(stdout, "content", xo_buf_data(xbp, offset), xbp->xb_curp - xbp->xb_bufp - offset, "", cbor->c_indent * 2); return rc; } static int cbor_handler (XO_ENCODER_HANDLER_ARGS) { int rc = 0; cbor_private_t *cbor = private; xo_buffer_t *xbp = cbor ? &cbor->c_data : NULL; if (xo_get_flags(xop) & XOF_PRETTY) { printf("%*sop %s: [%s] [%s]\n", cbor ? cbor->c_indent * 2 + 4 : 0, "", xo_encoder_op_name(op), name, value); fflush(stdout); } /* If we don't have private data, we're sunk */ if (cbor == NULL && op != XO_OP_CREATE) return -1; switch (op) { case XO_OP_CREATE: /* Called when the handle is init'd */ rc = cbor_create(xop); break; case XO_OP_OPEN_CONTAINER: cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL); cbor->c_indent += 1; break; case XO_OP_CLOSE_CONTAINER: cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); cbor->c_indent -= 1; break; case XO_OP_OPEN_LIST: cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL); cbor->c_indent += 1; break; case XO_OP_CLOSE_LIST: cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); cbor->c_indent -= 1; break; case XO_OP_OPEN_LEAF_LIST: cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL); cbor->c_indent += 1; cbor->c_open_leaf_list = 1; break; case XO_OP_CLOSE_LEAF_LIST: cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); cbor->c_indent -= 1; cbor->c_open_leaf_list = 0; break; case XO_OP_OPEN_INSTANCE: cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL); cbor->c_indent += 1; break; case XO_OP_CLOSE_INSTANCE: cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); cbor->c_indent -= 1; break; case XO_OP_STRING: /* Quoted UTF-8 string */ if (!cbor->c_open_leaf_list) cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value); break; case XO_OP_CONTENT: /* Other content */ if (!cbor->c_open_leaf_list) cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); /* * It's content, not string, so we need to look at the * string and build some content. Turns out we only * care about true, false, null, and numbers. */ cbor_content(xop, cbor, xbp, value); break; case XO_OP_FINISH: /* Clean up function */ cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); cbor->c_indent -= 1; break; case XO_OP_FLUSH: /* Clean up function */ if (xo_get_flags(xop) & XOF_PRETTY) cbor_memdump(stdout, "cbor", xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp, ">", 0); else { rc = write(1, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); if (rc > 0) rc = 0; } break; case XO_OP_DESTROY: /* Clean up function */ break; case XO_OP_ATTRIBUTE: /* Attribute name/value */ break; case XO_OP_VERSION: /* Version string */ break; } return rc; } int xo_encoder_library_init (XO_ENCODER_INIT_ARGS) { arg->xei_handler = cbor_handler; arg->xei_version = XO_ENCODER_VERSION; return 0; }