Browse Source

CBOR extra improvements

* Shared error throwers for smaller footprint.

* Add explicit checks for a few potential wraps and out-of-range casts.

* Remove unnecessary cbor extra test helpers (for definite and indefinite
  length arrays and maps).

* Related CBOR extra Makefile changes and improvements.

* Add duk_cbor_init() to create the global object binding if caller
  wants it.

* CBOR README update.
pull/1801/head
Sami Vaarala 7 years ago
parent
commit
8639017862
  1. 88
      extras/cbor/Makefile
  2. 13
      extras/cbor/README.rst
  3. 6
      extras/cbor/definite_and_indefinite_arrays.py
  4. 7
      extras/cbor/definite_and_indefinite_maps.py
  5. 144
      extras/cbor/duk_cbor.c
  6. 5
      extras/cbor/duk_cbor.h
  7. 30
      extras/cbor/run_testvectors.js

88
extras/cbor/Makefile

@ -1,6 +1,9 @@
# For manual testing; say 'make' in extras/cbor.
VALGRIND=valgrind
CCOPTS=-std=c99 -Wall -Wextra -O2
#CCOPTS+=-fsanitize=undefined
.PHONY: all
all: clean jsoncbor test-runs
@ -8,6 +11,7 @@ all: clean jsoncbor test-runs
clean:
-rm -rf ./prep
-rm -f jsoncbor
-rm -f appendix_a.json
jsoncbor: jsoncbor.c duk_cbor.c duk_cbor.h
-rm -rf ./prep
@ -17,44 +21,20 @@ jsoncbor: jsoncbor.c duk_cbor.c duk_cbor.h
-DDUK_USE_JSON_EATWHITE_FASTPATH \
-DDUK_USE_JSON_QUOTESTRING_FASTPATH \
-DDUK_USE_JSON_STRINGIFY_FASTPATH
gcc -std=c99 -Wall -Wextra -O2 -o $@ -I./prep -I. \
./prep/duktape.c duk_cbor.c jsoncbor.c -lm
gcc $(CCOPTS) -o $@ -I./prep -I. ./prep/duktape.c duk_cbor.c jsoncbor.c -lm
appendix_a.json:
wget -O $@ https://raw.githubusercontent.com/cbor/test-vectors/master/appendix_a.json
.PHONY: test-runs
test-runs: jsoncbor \
enc-primitives enc-number enc-buffer enc-string enc-array enc-object enc-half-float enc-float enc-misc \
dec-half-float dec-half-float
dec-half-float dec-half-float dec-array dec-object
.PHONY: dec-misc
dec-misc: jsoncbor
python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| $(VALGRIND) -q ./jsoncbor -d # object key, gets string coerced in JSON output
python -c 'import sys; sys.stdout.write("\x7f\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7f\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5f\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5f\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| $(VALGRIND) -q ./jsoncbor -d
.PHONY: test-vectors
test-vectors: appendix_a.json
echo "Expects 'duk' to exist in local directory for now..."
./duk run_testvectors.js
.PHONY: dec-half-float
dec-half-float: jsoncbor
@ -116,6 +96,48 @@ dec-half-float: jsoncbor
python -c 'import sys; sys.stdout.write("\xf9\xff\x12")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xf9\xff\x12")'| $(VALGRIND) -q ./jsoncbor -d
.PHONY: dec-array
dec-array: jsoncbor
python -c 'import sys; sys.stdout.write("\x9f\x01\x02\x03\x9f\xff\x04\x80\x80\x9f\x10\x11\xff\x84\x04\x03\x02\x01\xff")' | python cbordecode.py
python -c 'import sys; sys.stdout.write("\x9f\x01\x02\x03\x9f\xff\x04\x80\x80\x9f\x10\x11\xff\x84\x04\x03\x02\x01\xff")' | $(VALGRIND) -q ./jsoncbor -d
.PHONY: dec-object
dec-object: jsoncbor
python -c 'import sys; sys.stdout.write("\xbf\x63foo\x63bar\x63bar\xa0\x64quux\xbf\xff\x63baz\xa2\x61a\x61b\x61c\x61d\x65quuux\xbf\x61x\x61y\x61z\x61w\xff\xff")' | python cbordecode.py
python -c 'import sys; sys.stdout.write("\xbf\x63foo\x63bar\x63bar\xa0\x64quux\xbf\xff\x63baz\xa2\x61a\x61b\x61c\x61d\x65quuux\xbf\x61x\x61y\x61z\x61w\xff\xff")' | $(VALGRIND) -q ./jsoncbor -d
.PHONY: dec-misc
dec-misc: jsoncbor
python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| $(VALGRIND) -q ./jsoncbor -d # object key, gets string coerced in JSON output
python -c 'import sys; sys.stdout.write("\x7f\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7f\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5f\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5f\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| $(VALGRIND) -q ./jsoncbor -d
python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| python cbordecode.py
python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| $(VALGRIND) -q ./jsoncbor -d
.PHONY: enc-primitives
enc-primitives: jsoncbor
echo 'void 0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py

13
extras/cbor/README.rst

@ -8,7 +8,16 @@ Overview
C functions to encode/decode values in CBOR format, and a simple command
line utility to convert between JSON and CBOR.
Basic usage of the conversion tool::
To integrate CBOR into your application:
* Call ``duk_cbor_encode()`` and ``duk_cbor_decode()`` directly if a C API
is enough.
* Call ``duk_cbor_init()`` to register a global ``CBOR`` object with
Ecmascript bindings ``CBOR.encode()`` and ``CBOR.decode()``, roughly
matching https://github.com/paroga/cbor-js.
Basic usage of the ``jsoncbor`` conversion tool::
$ make jsoncbor
[...]
@ -48,8 +57,6 @@ Future work
- Decode test cases, also for cases we don't produce.
- https://datatracker.ietf.org/doc/draft-jroatch-cbor-tags/?include_text=1
could be used for typed arrays.
- Half-float encoding and decoding.
- Single precision encoding when no precision lost.
- 64-bit integer encoding.
- Better 64-bit integer decoding (workaround for non-64-bit targets).
- Objects with non-string keys, could be represented as a Map.

6
extras/cbor/definite_and_indefinite_arrays.py

@ -1,6 +0,0 @@
#!/usr/bin/env python2
import sys
x = '\x9f' + ( '\x01' + '\x02' + '\x03' + '\x9f\xff' + '\x04' + '\x80' + '\x80' + \
'\x9f\x10\x11\xff' + '\x84\x04\x03\x02\x01' ) + '\xff'
sys.stdout.write(x)

7
extras/cbor/definite_and_indefinite_maps.py

@ -1,7 +0,0 @@
#!/usr/bin/env python2
import sys
x = '\xbf' + ( '\x63foo\x63bar' + '\x63bar\xa0' + '\x64quux\xbf\xff' + \
'\x63baz\xa2\x61a\x61b\x61c\x61d' + '\x65quuux\xbf\x61x\x61y\x61z\x61w\xff' ) + \
'\xff'
sys.stdout.write(x)

144
extras/cbor/duk_cbor.c

@ -13,8 +13,8 @@
typedef struct {
duk_context *ctx;
duk_idx_t idx_buf;
duk_size_t len;
duk_size_t off;
duk_size_t len;
} duk_cbor_encode_context;
typedef struct {
@ -81,15 +81,35 @@ static void duk__cbor_bswap8(duk_uint8_t *x) {
* Encoding
*/
static void duk__cbor_encode_error(duk_cbor_encode_context *enc_ctx) {
(void) duk_type_error(enc_ctx->ctx, "cbor encode error");
}
/* Check that a size_t is in uint32 range to avoid out-of-range casts. */
static void duk__cbor_encode_sizet_uint32_check(duk_cbor_encode_context *enc_ctx, duk_size_t len) {
if (sizeof(duk_size_t) > sizeof(duk_uint32_t) && len > (duk_size_t) DUK_UINT32_MAX) {
duk__cbor_encode_error(enc_ctx);
}
}
static duk_uint8_t *duk__cbor_encode_reserve(duk_cbor_encode_context *enc_ctx, duk_size_t len) {
duk_uint8_t *res;
duk_size_t newlen;
duk_size_t ignlen;
while (enc_ctx->off + len > enc_ctx->len) {
for (;;) {
duk_size_t tmp;
duk_size_t newlen;
tmp = enc_ctx->off + len;
if (tmp < len) {
duk__cbor_encode_error(enc_ctx); /* Overflow. */
}
if (tmp <= enc_ctx->len) {
break;
}
newlen = enc_ctx->len * 2U;
if (newlen < enc_ctx->len) {
(void) duk_range_error(enc_ctx->ctx, "too large");
duk__cbor_encode_error(enc_ctx); /* Overflow. */
}
#if defined(DUK_CBOR_DPRINT)
fprintf(stderr, "resize to %ld\n", (long) newlen);
@ -108,7 +128,7 @@ static void duk__cbor_encode_overwritebyte(duk_cbor_encode_context *enc_ctx, duk
res = (duk_uint8_t *) duk_require_buffer(enc_ctx->ctx, enc_ctx->idx_buf, &len);
if (off >= len) {
(void) duk_type_error(enc_ctx->ctx, "internal error");
duk__cbor_encode_error(enc_ctx);
}
res[off] = val;
}
@ -123,8 +143,8 @@ static void duk__cbor_encode_emitbyte(duk_cbor_encode_context *enc_ctx, duk_uint
static void duk__cbor_encode_uint32(duk_cbor_encode_context *enc_ctx, duk_uint32_t u, duk_uint8_t base) {
duk_uint8_t *p;
if (u <= 23U) {
duk__cbor_encode_emitbyte(enc_ctx, base + (duk_uint8_t) u);
if (u <= 23UL) {
duk__cbor_encode_emitbyte(enc_ctx, (duk_uint8_t) (base + (duk_uint8_t) u));
} else if (u <= 0xffUL) {
p = duk__cbor_encode_reserve(enc_ctx, 1 + 1);
*p++ = base + 0x18U;
@ -208,10 +228,10 @@ static void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d)
u.d = d;
if (isfinite(d)) {
double t = floor(d);
double d_floor = floor(d);
duk_int16_t exp;
if (t == d) {
if (d_floor == d) {
if (d == 0.0) {
if (signbit(d)) {
/* Shortest -0 is using half-float. */
@ -402,9 +422,7 @@ static void duk__cbor_encode_string_top(duk_cbor_encode_context *enc_ctx) {
* representation is used as is for now.
*/
str = (const duk_uint8_t *) duk_require_lstring(enc_ctx->ctx, -1, &len);
if (len != (duk_uint32_t) len) {
(void) duk_type_error(enc_ctx->ctx, "invalid type");
}
duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x60U);
p = duk__cbor_encode_reserve(enc_ctx, len);
(void) memcpy((void *) p, (const void *) str, len);
@ -447,9 +465,7 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
if (duk_is_array(enc_ctx->ctx, -1)) {
duk_size_t i;
len = duk_get_length(enc_ctx->ctx, -1);
if (len != (duk_uint32_t) len) {
goto fail;
}
duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x80U);
for (i = 0; i < len; i++) {
duk_get_prop_index(enc_ctx->ctx, -1, (duk_uarridx_t) i);
@ -458,9 +474,7 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
} else if (duk_is_buffer_data(enc_ctx->ctx, -1)) {
/* Tag buffer data? */
buf = (duk_uint8_t *) duk_require_buffer_data(enc_ctx->ctx, -1, &len);
if (len != (duk_uint32_t) len) {
goto fail;
}
duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U);
p = duk__cbor_encode_reserve(enc_ctx, len);
(void) memcpy((void *) p, (const void *) buf, len);
@ -484,7 +498,7 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
duk__cbor_encode_value(enc_ctx);
count++;
if (count == 0U) {
(void) duk_range_error(enc_ctx->ctx, "too large");
duk__cbor_encode_error(enc_ctx);
}
}
duk_pop(enc_ctx->ctx);
@ -496,10 +510,9 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
}
break;
case DUK_TYPE_BUFFER:
/* Tag buffer data? */
buf = (duk_uint8_t *) duk_require_buffer(enc_ctx->ctx, -1, &len);
if (len != (duk_uint32_t) len) {
goto fail;
}
duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U);
p = duk__cbor_encode_reserve(enc_ctx, len);
(void) memcpy((void *) p, (const void *) buf, len);
@ -532,30 +545,34 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
return;
fail:
(void) duk_type_error(enc_ctx->ctx, "invalid type");
duk__cbor_encode_error(enc_ctx);
}
/*
* Decoding
*/
static void duk__cbor_decode_error(duk_cbor_decode_context *dec_ctx) {
(void) duk_type_error(dec_ctx->ctx, "cbor decode error");
}
static duk_uint8_t duk__cbor_decode_readbyte(duk_cbor_decode_context *dec_ctx) {
if (dec_ctx->off >= dec_ctx->len) {
(void) duk_type_error(dec_ctx->ctx, "invalid format");
duk__cbor_decode_error(dec_ctx);
}
return dec_ctx->buf[dec_ctx->off++];
}
static duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) {
if (dec_ctx->off >= dec_ctx->len) {
(void) duk_type_error(dec_ctx->ctx, "invalid format");
duk__cbor_decode_error(dec_ctx);
}
return dec_ctx->buf[dec_ctx->off];
}
static void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
if (len > dec_ctx->off) {
(void) duk_type_error(dec_ctx->ctx, "internal error");
duk__cbor_decode_error(dec_ctx);
}
dec_ctx->off -= len;
}
@ -563,15 +580,21 @@ static void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t
#if 0
static void duk__cbor_decode_ensure(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
if (dec_ctx->off + len > dec_ctx->len) {
(void) duk_type_error(dec_ctx->ctx, "truncated");
duk__cbor_decode_error(dec_ctx);
}
}
#endif
static const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
const duk_uint8_t *res;
if (dec_ctx->off + len > dec_ctx->len) {
(void) duk_type_error(dec_ctx->ctx, "truncated");
duk_size_t tmp;
tmp = dec_ctx->off + len;
if (tmp < len) {
duk__cbor_decode_error(dec_ctx); /* Overflow. */
}
if (tmp > dec_ctx->len) {
duk__cbor_decode_error(dec_ctx); /* Not enough input. */
}
res = dec_ctx->buf + dec_ctx->off;
dec_ctx->off += len;
@ -591,6 +614,10 @@ static duk_double_t duk__cbor_decode_aival_uint(duk_cbor_decode_context *dec_ctx
duk_uint32_t t, t2;
ai = ib & 0x1fU;
if (ai <= 0x17U) {
return (duk_double_t) ai;
}
switch (ai) {
case 0x18U: /* 1 byte */
return (duk_double_t) duk__cbor_decode_readbyte(dec_ctx);
@ -615,15 +642,10 @@ static duk_double_t duk__cbor_decode_aival_uint(duk_cbor_decode_context *dec_ctx
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 8U;
t += (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
return (duk_double_t) t2 * 4294967296.0 + (duk_double_t) t;
case 0x1cU:
case 0x1dU:
case 0x1eU:
case 0x1fU:
(void) duk_type_error(dec_ctx->ctx, "invalid ibyte");
break;
default:
return (duk_double_t) ai;
}
duk__cbor_decode_error(dec_ctx);
return 0.0;
}
static duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
@ -631,7 +653,7 @@ static duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_c
t = duk__cbor_decode_aival_uint(dec_ctx, ib);
if (t >= 4294967296.0) {
(void) duk_type_error(dec_ctx->ctx, "too long");
duk__cbor_decode_error(dec_ctx);
}
return (duk_uint32_t) t;
}
@ -644,7 +666,7 @@ static void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_
ib = duk__cbor_decode_readbyte(dec_ctx);
if ((ib & 0xe0U) != expected_base) {
(void) duk_type_error(dec_ctx->ctx, "invalid format");
duk__cbor_decode_error(dec_ctx);
}
/* indefinite format is rejected by the following */
len = duk__cbor_decode_aival_uint32(dec_ctx, ib);
@ -665,7 +687,7 @@ static void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_
duk_uint8_t *buf_data;
duk_size_t buf_size;
buf_data = duk_require_buffer(dec_ctx->ctx, idx, &buf_size);
buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->ctx, idx, &buf_size);
if (p != NULL) {
if (buf_size > 0U) {
(void) memcpy((void *) p, (const void *) buf_data, buf_size);
@ -674,7 +696,7 @@ static void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_
} else {
total_size += buf_size;
if (total_size < buf_size) {
(void) duk_type_error(dec_ctx->ctx, "internal error");
duk__cbor_decode_error(dec_ctx);
}
}
}
@ -682,7 +704,7 @@ static void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_
if (p != NULL) {
break;
} else {
p = duk_push_fixed_buffer(dec_ctx->ctx, total_size);
p = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->ctx, total_size);
}
}
@ -699,7 +721,7 @@ static void duk__cbor_decode_and_join_strbuf(duk_cbor_decode_context *dec_ctx, d
duk__cbor_decode_buffer(dec_ctx, expected_base);
count++;
if (count <= 0) {
(void) duk_type_error(dec_ctx->ctx, "internal error");
duk__cbor_decode_error(dec_ctx);
}
}
if (count == 0) {
@ -860,11 +882,11 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
* import as is, but reject strings that would create symbols.
*/
if (ai == 0x1fU) {
duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U);
duk_uint8_t *buf_data;
duk_size_t buf_size;
buf_data = duk_require_buffer(dec_ctx->ctx, -1, &buf_size);
duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U);
buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->ctx, -1, &buf_size);
(void) duk_push_lstring(dec_ctx->ctx, (const char *) buf_data, buf_size);
duk_remove(dec_ctx->ctx, -2);
} else {
@ -1026,13 +1048,43 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
return;
format_error:
(void) duk_type_error(dec_ctx->ctx, "format error");
duk__cbor_decode_error(dec_ctx);
}
/*
* Public APIs
*/
static duk_ret_t duk__cbor_encode_binding(duk_context *ctx) {
/* Produce an ArrayBuffer by first decoding into a plain buffer which
* mimics a Uint8Array and gettings its .buffer property.
*/
duk_cbor_encode(ctx, -1, 0);
duk_get_prop_string(ctx, -1, "buffer");
return 1;
}
static duk_ret_t duk__cbor_decode_binding(duk_context *ctx) {
/* Lenient: accept any buffer like. */
duk_cbor_decode(ctx, -1, 0);
return 1;
}
void duk_cbor_init(duk_context *ctx, duk_uint_t flags) {
(void) flags;
duk_push_global_object(ctx);
duk_push_string(ctx, "CBOR");
duk_push_object(ctx);
duk_push_string(ctx, "encode");
duk_push_c_function(ctx, duk__cbor_encode_binding, 1);
duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "decode");
duk_push_c_function(ctx, duk__cbor_decode_binding, 1);
duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE);
duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE);
duk_pop(ctx);
}
void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags) {
duk_cbor_encode_context enc_ctx;

5
extras/cbor/duk_cbor.h

@ -1,10 +1,15 @@
#if !defined(DUK_CBOR_H_INCLUDED)
#define DUK_CBOR_H_INCLUDED
#include "duktape.h"
#if defined(__cplusplus)
extern "C" {
#endif
/* No flags at present. */
extern void duk_cbor_init(duk_context *ctx, duk_uint_t flags);
extern void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags);
extern void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags);

30
extras/cbor/run_testvectors.js

@ -0,0 +1,30 @@
var testvectors = JSON.parse(new TextDecoder().decode(readFile('appendix_a.json')));
// Very rudimentary for now, just dump useful information about decode
// results and (simple, unstructured) comparison to expected. This is
// only useful for manually inspecting the results right now.
testvectors.forEach(function (test, idx) {
print('===', idx, '->', Duktape.enc('jx', test));
var cbor = Duktape.dec('base64', test.cbor);
try {
var dec = CBOR.decode(cbor);
} catch (e) {
print('decode failed: ' + e);
return;
}
print('dec (jx): ' + Duktape.enc('jx', dec));
if (dec !== test.decoded) {
print('decoded compare failed');
}
if (test.roundtrip) {
var enc = CBOR.encode(dec);
print('re-enc: ' + Duktape.enc('hex', enc));
if (Duktape.enc('base64', cbor) !== Duktape.enc('base64', enc)) {
print('roundtrip failed');
}
}
});
Loading…
Cancel
Save