Browse Source

Merge pull request #2154 from svaarala/cbor-followup

CBOR extra improvements
pull/2156/head
Sami Vaarala 5 years ago
committed by GitHub
parent
commit
3c4d646189
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      RELEASES.rst
  2. 4
      extras/cbor/README.rst
  3. 485
      extras/cbor/duk_cbor.c
  4. 154
      tests/ecmascript/test-bi-cbor-dec-fastint.js
  5. 2
      tests/ecmascript/test-dev-fastint-basic.js
  6. 41
      tests/perf/test-cbor-decode-fastints.js
  7. 30
      tests/perf/test-cbor-decode-strings.js
  8. 21
      tests/perf/test-cbor-encode-double.js
  9. 21
      tests/perf/test-cbor-encode-float.js
  10. 21
      tests/perf/test-cbor-encode-half-float.js

5
RELEASES.rst

@ -3443,9 +3443,10 @@ Planned
* Minor changes to CBOR extra type handling: encode non-UTF-8 strings
as CBOR byte strings (instead of text strings), encode Symbols as empty
objects, refuse to decode Symbols, encode pointers as "(%p)" instead of
"%p" to match JX (GH-2121)
"%p" to match JX, decode integers in most cases to fastints when possible
(GH-2121, GH-2154)
* Minor performance improvements to CBOR extra encoder (GH-2121)
* Minor performance improvements to CBOR extra (GH-2121, GH-2154)
* Add (untested) support for mixed endian targets to CBOR extra (GH-2121)

4
extras/cbor/README.rst

@ -144,3 +144,7 @@ Decoding:
so that revival can depend on tags present.
* Option to compact decoded objects and arrays.
* Improve fastint decoding support, e.g. decode non-optimally encoded
integers as fastints, decode compatible floating point values as
fastints.

485
extras/cbor/duk_cbor.c

@ -38,9 +38,7 @@
#define DUK_CBOR_NOINLINE
#endif
#if 0
#define DUK_CBOR_GCC_BUILTINS
#endif
/* #define DUK_CBOR_GCC_BUILTINS */
/* Default behavior for encoding strings: use CBOR text string if string
* is UTF-8 compatible, otherwise use CBOR byte string. These defines
@ -53,6 +51,7 @@
/* Misc. defines. */
/* #define DUK_CBOR_PREFER_SIZE */
/* #define DUK_CBOR_DOUBLE_AS_IS */
/* #define DUK_CBOR_DECODE_FASTPATH */
typedef struct {
duk_context *ctx;
@ -156,7 +155,7 @@ static DUK_CBOR_INLINE duk_uint16_t duk__cbor_bswap16(duk_uint16_t x) {
#endif
}
static DUK_CBOR_INLINE uint32_t duk__cbor_bswap32(duk_uint32_t x) {
static DUK_CBOR_INLINE duk_uint32_t duk__cbor_bswap32(duk_uint32_t x) {
#if defined(DUK_CBOR_GCC_BUILTINS)
return __builtin_bswap32(x);
#else
@ -193,6 +192,29 @@ static DUK_CBOR_INLINE void duk__cbor_write_uint16_big(duk_uint8_t *p, duk_uint1
}
}
static DUK_CBOR_INLINE duk_uint16_t duk__cbor_read_uint16_big(const duk_uint8_t *p) {
duk_uint16_t a, x;
#if 0
x = (((duk_uint16_t) p[0]) << 8U) +
((duk_uint16_t) p[1]);
#endif
switch (duk__cbor_check_endian()) {
case DUK__CBOR_LITTLE_ENDIAN:
case DUK__CBOR_MIXED_ENDIAN:
(void) memcpy((void *) &a, (const void *) p, 2);
x = duk__cbor_bswap16(a);
break;
case DUK__CBOR_BIG_ENDIAN:
(void) memcpy((void *) &a, (const void *) p, 2);
x = a;
break;
default:
DUK_CBOR_ASSERT(0);
}
return x;
}
static DUK_CBOR_INLINE void duk__cbor_write_uint32_big(duk_uint8_t *p, duk_uint32_t x) {
#if 0
*p++ = (duk_uint8_t) ((x >> 24) & 0xffU);
@ -217,6 +239,31 @@ static DUK_CBOR_INLINE void duk__cbor_write_uint32_big(duk_uint8_t *p, duk_uint3
}
}
static DUK_CBOR_INLINE duk_uint32_t duk__cbor_read_uint32_big(const duk_uint8_t *p) {
duk_uint32_t a, x;
#if 0
x = (((duk_uint32_t) p[0]) << 24U) +
(((duk_uint32_t) p[1]) << 16U) +
(((duk_uint32_t) p[2]) << 8U) +
((duk_uint32_t) p[3]);
#endif
switch (duk__cbor_check_endian()) {
case DUK__CBOR_LITTLE_ENDIAN:
case DUK__CBOR_MIXED_ENDIAN:
(void) memcpy((void *) &a, (const void *) p, 4);
x = duk__cbor_bswap32(a);
break;
case DUK__CBOR_BIG_ENDIAN:
(void) memcpy((void *) &a, (const void *) p, 4);
x = a;
break;
default:
DUK_CBOR_ASSERT(0);
}
return x;
}
static DUK_CBOR_INLINE void duk__cbor_write_double_big(duk_uint8_t *p, double x) {
duk_cbor_dblunion u;
duk_uint32_t a, b;
@ -250,6 +297,26 @@ static DUK_CBOR_INLINE void duk__cbor_write_double_big(duk_uint8_t *p, double x)
}
}
static DUK_CBOR_INLINE void duk__cbor_write_float_big(duk_uint8_t *p, float x) {
duk_cbor_fltunion u;
duk_uint32_t a;
u.f = x;
switch (duk__cbor_check_endian()) {
case DUK__CBOR_LITTLE_ENDIAN:
case DUK__CBOR_MIXED_ENDIAN:
a = u.i[0];
u.i[0] = duk__cbor_bswap32(a);
(void) memcpy((void *) p, (const void *) u.x, 4);
break;
case DUK__CBOR_BIG_ENDIAN:
(void) memcpy((void *) p, (const void *) u.x, 4);
break;
default:
DUK_CBOR_ASSERT(0);
}
}
static DUK_CBOR_INLINE void duk__cbor_dblunion_host_to_little(duk_cbor_dblunion *u) {
duk_uint32_t a, b;
@ -278,16 +345,15 @@ static DUK_CBOR_INLINE void duk__cbor_dblunion_host_to_little(duk_cbor_dblunion
}
static DUK_CBOR_INLINE void duk__cbor_dblunion_little_to_host(duk_cbor_dblunion *u) {
/* Same operation in practice. */
duk__cbor_dblunion_host_to_little(u);
}
static DUK_CBOR_INLINE void duk__cbor_dblunion_big_to_host(duk_cbor_dblunion *u) {
static DUK_CBOR_INLINE void duk__cbor_dblunion_host_to_big(duk_cbor_dblunion *u) {
duk_uint32_t a, b;
switch (duk__cbor_check_endian()) {
case DUK__CBOR_LITTLE_ENDIAN:
/* ABCDEFGH -> HGFEDCBA */
/* HGFEDCBA -> ABCDEFGH */
#if 0
u->i64[0] = duk__cbor_bswap64(u->i64[0]);
#else
@ -298,7 +364,7 @@ static DUK_CBOR_INLINE void duk__cbor_dblunion_big_to_host(duk_cbor_dblunion *u)
#endif
break;
case DUK__CBOR_MIXED_ENDIAN:
/* ABCDEFGH -> DCBAHGFE */
/* DCBAHGFE -> ABCDEFGH */
a = u->i[0];
b = u->i[1];
u->i[0] = duk__cbor_bswap32(a);
@ -310,11 +376,15 @@ static DUK_CBOR_INLINE void duk__cbor_dblunion_big_to_host(duk_cbor_dblunion *u)
}
}
static DUK_CBOR_INLINE void duk__cbor_fltunion_big_to_host(duk_cbor_fltunion *u) {
static DUK_CBOR_INLINE void duk__cbor_dblunion_big_to_host(duk_cbor_dblunion *u) {
duk__cbor_dblunion_host_to_big(u);
}
static DUK_CBOR_INLINE void duk__cbor_fltunion_host_to_big(duk_cbor_fltunion *u) {
switch (duk__cbor_check_endian()) {
case DUK__CBOR_LITTLE_ENDIAN:
case DUK__CBOR_MIXED_ENDIAN:
/* ABCD -> DCBA */
/* DCBA -> ABCD */
u->i[0] = duk__cbor_bswap32(u->i[0]);
break;
case DUK__CBOR_BIG_ENDIAN:
@ -323,6 +393,10 @@ static DUK_CBOR_INLINE void duk__cbor_fltunion_big_to_host(duk_cbor_fltunion *u)
}
}
static DUK_CBOR_INLINE void duk__cbor_fltunion_big_to_host(duk_cbor_fltunion *u) {
duk__cbor_fltunion_host_to_big(u);
}
/*
* Encoding
*/
@ -585,6 +659,7 @@ static void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double
* float: seeee eeeemmmm mmmmmmmm mmmmmmmm mmm00000 00000000 00000000 00000000
*/
int use_float;
duk_float_t d_float;
/* We could do this explicit mantissa check, but doing
* a double-float-double cast is fine because we've
@ -595,22 +670,12 @@ static void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double
use_float =
(u.x[0] == 0 && u.x[1] == 0 && u.x[2] == 0 && (u.x[3] & 0xe0U) == 0);
#endif
use_float = ((duk_double_t) (duk_float_t) d == d);
d_float = (duk_float_t) d;
use_float = ((duk_double_t) d_float == d);
if (use_float) {
duk_uint32_t t;
exp += 127;
t = (duk_uint32_t) (u.x[7] & 0x80U) << 24;
t += (duk_uint32_t) exp << 23;
t += ((duk_uint32_t) u.x[6] & 0x0fU) << 19;
t += ((duk_uint32_t) u.x[5]) << 11;
t += ((duk_uint32_t) u.x[4]) << 3;
t += ((duk_uint32_t) u.x[3]) >> 5;
/* seeeeeee emmmmmmm mmmmmmmm mmmmmmmm */
p = enc_ctx->ptr;
*p++ = 0xfaU;
duk__cbor_write_uint32_big(p, t);
duk__cbor_write_float_big(p, d_float);
p += 4;
enc_ctx->ptr = p;
return;
@ -1031,17 +1096,44 @@ static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
* Decoding
*/
static void duk__cbor_req_stack(duk_cbor_decode_context *dec_ctx) {
duk_require_stack(dec_ctx->ctx, 4);
}
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 (DUK_CBOR_UNLIKELY(dec_ctx->off >= dec_ctx->len)) {
DUK_CBOR_ASSERT(dec_ctx->off <= dec_ctx->len);
if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 1U)) {
duk__cbor_decode_error(dec_ctx);
}
return dec_ctx->buf[dec_ctx->off++];
}
static duk_uint16_t duk__cbor_decode_read_u16(duk_cbor_decode_context *dec_ctx) {
duk_uint16_t res;
if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 2U)) {
duk__cbor_decode_error(dec_ctx);
}
res = duk__cbor_read_uint16_big(dec_ctx->buf + dec_ctx->off);
dec_ctx->off += 2;
return res;
}
static duk_uint32_t duk__cbor_decode_read_u32(duk_cbor_decode_context *dec_ctx) {
duk_uint32_t res;
if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 4U)) {
duk__cbor_decode_error(dec_ctx);
}
res = duk__cbor_read_uint32_big(dec_ctx->buf + dec_ctx->off);
dec_ctx->off += 4;
return res;
}
static duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) {
if (DUK_CBOR_UNLIKELY(dec_ctx->off >= dec_ctx->len)) {
duk__cbor_decode_error(dec_ctx);
@ -1050,9 +1142,7 @@ static duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) {
}
static void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
if (len > dec_ctx->off) {
duk__cbor_decode_error(dec_ctx);
}
DUK_CBOR_ASSERT(len <= dec_ctx->off); /* Caller must ensure. */
dec_ctx->off -= len;
}
@ -1078,18 +1168,17 @@ static const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_
static int duk__cbor_decode_checkbreak(duk_cbor_decode_context *dec_ctx) {
if (duk__cbor_decode_peekbyte(dec_ctx) == 0xffU) {
#if 1
(void) duk__cbor_decode_readbyte(dec_ctx);
#else
DUK_CBOR_ASSERT(dec_ctx->off < dec_ctx->len);
dec_ctx->off++;
#if 0
(void) duk__cbor_decode_readbyte(dec_ctx);
#endif
return 1;
}
return 0;
}
static duk_double_t duk__cbor_decode_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) {
static void duk__cbor_decode_push_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) {
duk_uint8_t ai;
duk_uint32_t t, t1, t2;
#if 0
@ -1100,40 +1189,28 @@ static duk_double_t duk__cbor_decode_aival_int(duk_cbor_decode_context *dec_ctx,
ai = ib & 0x1fU;
if (ai <= 0x17U) {
d = (double) ai;
t = ai;
goto shared_exit;
}
switch (ai) {
case 0x18U: /* 1 byte */
d = (duk_double_t) duk__cbor_decode_readbyte(dec_ctx);
t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
goto shared_exit;
case 0x19U: /* 2 byte */
t = ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 8U;
t += (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
d = (duk_double_t) t;
t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx);
goto shared_exit;
case 0x1aU: /* 4 byte */
t = ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 24U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 16U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 8U;
t += (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
d = (duk_double_t) t;
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
goto shared_exit;
case 0x1bU: /* 8 byte */
/* For uint64 it's important to handle the -1.0 part before
* casting to double: otherwise the adjustment might be lost
* in the cast. Uses: -1.0 - d <=> -(d + 1.0).
*/
t = ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 24U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 16U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 8U;
t += (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
t2 = t;
t = ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 24U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 16U;
t += ((duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx)) << 8U;
t += (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
t1 = t;
#if 0
t3 = (duk_uint64_t) t2 * 0x100000000ULL + (duk_uint64_t) t1;
@ -1180,6 +1257,8 @@ static duk_double_t duk__cbor_decode_aival_int(duk_cbor_decode_context *dec_ctx,
/* Use two double parts, avoids dependency on 64-bit type.
* Avoid precision loss carefully, especially when dealing
* with the required +1 for negative values.
*
* No fastint check for this path at present.
*/
d1 = (duk_double_t) t1; /* XXX: cast helpers */
d2 = (duk_double_t) t2 * 4294967296.0;
@ -1191,24 +1270,74 @@ static duk_double_t duk__cbor_decode_aival_int(duk_cbor_decode_context *dec_ctx,
d = -d;
}
#endif
return d;
/* XXX: a push and check for fastint API would be nice */
duk_push_number(dec_ctx->ctx, d);
return;
}
duk__cbor_decode_error(dec_ctx);
return 0.0;
return;
shared_exit:
return (negative ? -1.0 - d : d);
if (negative) {
/* XXX: a push and check for fastint API would be nice */
if ((duk_uint_t) t <= (duk_uint_t) -(DUK_INT_MIN + 1)) {
duk_push_int(dec_ctx->ctx, -1 - ((duk_int_t) t));
} else {
duk_push_number(dec_ctx->ctx, -1.0 - (duk_double_t) t);
}
} else {
duk_push_uint(dec_ctx->ctx, (duk_uint_t) t);
}
}
static duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
duk_double_t t;
static void duk__cbor_decode_skip_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
const duk_int8_t skips[32] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, -1, -1, -1, -1
};
duk_uint8_t ai;
duk_int8_t skip;
t = duk__cbor_decode_aival_int(dec_ctx, ib, 0 /*negative*/);
if (t >= 4294967296.0) {
ai = ib & 0x1fU;
skip = skips[ai];
if (DUK_UNLIKELY(skip < 0)) {
duk__cbor_decode_error(dec_ctx);
}
return duk__cbor_double_to_uint32(t);
duk__cbor_decode_consume(dec_ctx, (duk_size_t) skip);
return;
}
static duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
duk_uint8_t ai;
duk_uint32_t t;
ai = ib & 0x1fU;
if (ai <= 0x17U) {
return (duk_uint32_t) ai;
}
switch (ai) {
case 0x18U: /* 1 byte */
t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
return t;
case 0x19U: /* 2 byte */
t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx);
return t;
case 0x1aU: /* 4 byte */
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
return t;
case 0x1bU: /* 8 byte */
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
if (t != 0U) {
break;
}
t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
return t;
}
duk__cbor_decode_error(dec_ctx);
return 0U;
}
static void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) {
@ -1221,7 +1350,7 @@ static void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_
if ((ib & 0xe0U) != expected_base) {
duk__cbor_decode_error(dec_ctx);
}
/* indefinite format is rejected by the following on purpose */
/* Indefinite format is rejected by the following on purpose. */
len = duk__cbor_decode_aival_uint32(dec_ctx, ib);
inp = duk__cbor_decode_consume(dec_ctx, len);
/* XXX: duk_push_fixed_buffer_with_data() would be a nice API addition. */
@ -1416,6 +1545,8 @@ static void duk__cbor_decode_string(duk_cbor_decode_context *dec_ctx, duk_uint8_
static duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) {
duk_uint32_t idx, len;
duk__cbor_req_stack(dec_ctx);
/* Support arrays up to 0xfffffffeU in length. 0xffffffff is
* used as an indefinite length marker.
*/
@ -1454,6 +1585,8 @@ static duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_u
static duk_bool_t duk__cbor_decode_map(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) {
duk_uint32_t count;
duk__cbor_req_stack(dec_ctx);
if (ai == 0x1fU) {
count = 0xffffffffUL;
} else {
@ -1511,13 +1644,223 @@ static duk_double_t duk__cbor_decode_double(duk_cbor_decode_context *dec_ctx) {
return u.d;
}
#if defined(DUK_CBOR_DECODE_FASTPATH)
#define DUK__CBOR_AI (ib & 0x1fU)
static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
duk_uint8_t ib;
/* Any paths potentially recursing back to duk__cbor_decode_value()
* must perform a Duktape value stack growth check. Avoid the check
* here for simple paths like primitive values.
*/
reread_initial_byte:
#if defined(DUK_CBOR_DPRINT)
fprintf(stderr, "cbor decode off=%ld len=%ld\n", (long) dec_ctx->off, (long) dec_ctx->len);
#endif
ib = duk__cbor_decode_readbyte(dec_ctx);
/* Full initial byte switch, footprint cost over baseline is ~+1kB. */
/* XXX: Force full switch with no range check. */
switch (ib) {
case 0x00U: case 0x01U: case 0x02U: case 0x03U: case 0x04U: case 0x05U: case 0x06U: case 0x07U:
case 0x08U: case 0x09U: case 0x0aU: case 0x0bU: case 0x0cU: case 0x0dU: case 0x0eU: case 0x0fU:
case 0x10U: case 0x11U: case 0x12U: case 0x13U: case 0x14U: case 0x15U: case 0x16U: case 0x17U:
duk_push_uint(dec_ctx->ctx, ib);
break;
case 0x18U: case 0x19U: case 0x1aU: case 0x1bU:
duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/);
break;
case 0x1cU: case 0x1dU: case 0x1eU: case 0x1fU:
goto format_error;
case 0x20U: case 0x21U: case 0x22U: case 0x23U: case 0x24U: case 0x25U: case 0x26U: case 0x27U:
case 0x28U: case 0x29U: case 0x2aU: case 0x2bU: case 0x2cU: case 0x2dU: case 0x2eU: case 0x2fU:
case 0x30U: case 0x31U: case 0x32U: case 0x33U: case 0x34U: case 0x35U: case 0x36U: case 0x37U:
duk_push_int(dec_ctx->ctx, -((duk_int_t) ((ib - 0x20U) + 1U)));
break;
case 0x38U: case 0x39U: case 0x3aU: case 0x3bU:
duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/);
break;
case 0x3cU: case 0x3dU: case 0x3eU: case 0x3fU:
goto format_error;
case 0x40U: case 0x41U: case 0x42U: case 0x43U: case 0x44U: case 0x45U: case 0x46U: case 0x47U:
case 0x48U: case 0x49U: case 0x4aU: case 0x4bU: case 0x4cU: case 0x4dU: case 0x4eU: case 0x4fU:
case 0x50U: case 0x51U: case 0x52U: case 0x53U: case 0x54U: case 0x55U: case 0x56U: case 0x57U:
/* XXX: Avoid rewind, we know the length already. */
DUK_CBOR_ASSERT(dec_ctx->off > 0U);
dec_ctx->off--;
duk__cbor_decode_buffer(dec_ctx, 0x40U);
break;
case 0x58U: case 0x59U: case 0x5aU: case 0x5bU:
/* XXX: Avoid rewind, decode length inline. */
DUK_CBOR_ASSERT(dec_ctx->off > 0U);
dec_ctx->off--;
duk__cbor_decode_buffer(dec_ctx, 0x40U);
break;
case 0x5cU: case 0x5dU: case 0x5eU:
goto format_error;
case 0x5fU:
duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U);
break;
case 0x60U: case 0x61U: case 0x62U: case 0x63U: case 0x64U: case 0x65U: case 0x66U: case 0x67U:
case 0x68U: case 0x69U: case 0x6aU: case 0x6bU: case 0x6cU: case 0x6dU: case 0x6eU: case 0x6fU:
case 0x70U: case 0x71U: case 0x72U: case 0x73U: case 0x74U: case 0x75U: case 0x76U: case 0x77U:
/* XXX: Avoid double decode of length. */
duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
break;
case 0x78U: case 0x79U: case 0x7aU: case 0x7bU:
/* XXX: Avoid double decode of length. */
duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
break;
case 0x7cU: case 0x7dU: case 0x7eU:
goto format_error;
case 0x7fU:
duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
break;
case 0x80U: case 0x81U: case 0x82U: case 0x83U: case 0x84U: case 0x85U: case 0x86U: case 0x87U:
case 0x88U: case 0x89U: case 0x8aU: case 0x8bU: case 0x8cU: case 0x8dU: case 0x8eU: case 0x8fU:
case 0x90U: case 0x91U: case 0x92U: case 0x93U: case 0x94U: case 0x95U: case 0x96U: case 0x97U:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0x98U: case 0x99U: case 0x9aU: case 0x9bU:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0x9cU: case 0x9dU: case 0x9eU:
goto format_error;
case 0x9fU:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0xa0U: case 0xa1U: case 0xa2U: case 0xa3U: case 0xa4U: case 0xa5U: case 0xa6U: case 0xa7U:
case 0xa8U: case 0xa9U: case 0xaaU: case 0xabU: case 0xacU: case 0xadU: case 0xaeU: case 0xafU:
case 0xb0U: case 0xb1U: case 0xb2U: case 0xb3U: case 0xb4U: case 0xb5U: case 0xb6U: case 0xb7U:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0xb8U: case 0xb9U: case 0xbaU: case 0xbbU:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0xbcU: case 0xbdU: case 0xbeU:
goto format_error;
case 0xbfU:
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
goto format_error;
}
break;
case 0xc0U: case 0xc1U: case 0xc2U: case 0xc3U: case 0xc4U: case 0xc5U: case 0xc6U: case 0xc7U:
case 0xc8U: case 0xc9U: case 0xcaU: case 0xcbU: case 0xccU: case 0xcdU: case 0xceU: case 0xcfU:
case 0xd0U: case 0xd1U: case 0xd2U: case 0xd3U: case 0xd4U: case 0xd5U: case 0xd6U: case 0xd7U:
/* Tag 0-23: drop. */
goto reread_initial_byte;
case 0xd8U: case 0xd9U: case 0xdaU: case 0xdbU:
duk__cbor_decode_skip_aival_int(dec_ctx, ib);
goto reread_initial_byte;
case 0xdcU: case 0xddU: case 0xdeU: case 0xdfU:
goto format_error;
case 0xe0U:
goto format_error;
case 0xe1U:
goto format_error;
case 0xe2U:
goto format_error;
case 0xe3U:
goto format_error;
case 0xe4U:
goto format_error;
case 0xe5U:
goto format_error;
case 0xe6U:
goto format_error;
case 0xe7U:
goto format_error;
case 0xe8U:
goto format_error;
case 0xe9U:
goto format_error;
case 0xeaU:
goto format_error;
case 0xebU:
goto format_error;
case 0xecU:
goto format_error;
case 0xedU:
goto format_error;
case 0xeeU:
goto format_error;
case 0xefU:
goto format_error;
case 0xf0U:
goto format_error;
case 0xf1U:
goto format_error;
case 0xf2U:
goto format_error;
case 0xf3U:
goto format_error;
case 0xf4U:
duk_push_false(dec_ctx->ctx);
break;
case 0xf5U:
duk_push_true(dec_ctx->ctx);
break;
case 0xf6U:
duk_push_null(dec_ctx->ctx);
break;
case 0xf7U:
duk_push_undefined(dec_ctx->ctx);
break;
case 0xf8U:
/* Simple value 32-255, nothing defined yet, so reject. */
goto format_error;
case 0xf9U: {
duk_double_t d;
d = duk__cbor_decode_half_float(dec_ctx);
duk_push_number(dec_ctx->ctx, d);
break;
}
case 0xfaU: {
duk_double_t d;
d = duk__cbor_decode_float(dec_ctx);
duk_push_number(dec_ctx->ctx, d);
break;
}
case 0xfbU: {
duk_double_t d;
d = duk__cbor_decode_double(dec_ctx);
duk_push_number(dec_ctx->ctx, d);
break;
}
case 0xfcU:
case 0xfdU:
case 0xfeU:
case 0xffU:
goto format_error;
} /* end switch */
return;
format_error:
duk__cbor_decode_error(dec_ctx);
}
#else /* DUK_CBOR_DECODE_FASTPATH */
static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
duk_uint8_t ib, mt, ai;
/* When working with deeply recursive structures, this is important
* to ensure there's no effective depth limit.
/* Any paths potentially recursing back to duk__cbor_decode_value()
* must perform a Duktape value stack growth check. Avoid the check
* here for simple paths like primitive values.
*/
duk_require_stack(dec_ctx->ctx, 4);
reread_initial_byte:
#if defined(DUK_CBOR_DPRINT)
@ -1538,12 +1881,11 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
switch (mt) {
case 0U: { /* unsigned integer */
duk_push_number(dec_ctx->ctx, duk__cbor_decode_aival_int(dec_ctx, ib, 0 /*negative*/));
duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/);
break;
}
case 1U: { /* negative integer */
/* XXX: precision, for -1.0 and 64-bit integers */
duk_push_number(dec_ctx->ctx, duk__cbor_decode_aival_int(dec_ctx, ib, 1 /*negative*/));
duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/);
break;
}
case 2U: { /* byte string */
@ -1560,13 +1902,13 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
break;
}
case 4U: { /* array of data items */
if (duk__cbor_decode_array(dec_ctx, ib, ai) == 0) {
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, ai) == 0)) {
goto format_error;
}
break;
}
case 5U: { /* map of pairs of data items */
if (duk__cbor_decode_map(dec_ctx, ib, ai) == 0) {
if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, ai) == 0)) {
goto format_error;
}
break;
@ -1576,7 +1918,7 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
* value may itself be tagged (an unlimited number of times)
* so keep on peeling away tags.
*/
(void) duk__cbor_decode_aival_int(dec_ctx, ib, 0 /*negative*/);
duk__cbor_decode_skip_aival_int(dec_ctx, ib);
goto reread_initial_byte;
}
case 7U: { /* floating point numbers, simple data types, break; other */
@ -1643,6 +1985,7 @@ static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
format_error:
duk__cbor_decode_error(dec_ctx);
}
#endif /* DUK_CBOR_DECODE_FASTPATH */
/*
* Public APIs
@ -1706,6 +2049,13 @@ void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags) {
(void) decode_flags;
/* Suppress compile warnings for functions only needed with e.g.
* asserts enabled.
*/
(void) duk__cbor_get_reserve;
(void) duk__cbor_isinf;
(void) duk__cbor_fpclassify;
idx = duk_require_normalize_index(ctx, idx);
dec_ctx.ctx = ctx;
@ -1713,6 +2063,7 @@ void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags) {
dec_ctx.off = 0;
/* dec_ctx.len: set above */
duk__cbor_req_stack(&dec_ctx);
duk__cbor_decode_value(&dec_ctx);
if (dec_ctx.off != dec_ctx.len) {
(void) duk_type_error(ctx, "trailing garbage");

154
tests/ecmascript/test-bi-cbor-dec-fastint.js

@ -0,0 +1,154 @@
/*
* Current CBOR decode fastint behavior.
*/
/*===
- top level integer
17 fastint
255 fastint
4275878552 fastint
4294967295 fastint
4294967295 fastint
4294967296 fastint
139486488458836 fastint
140737488355327 fastint
140737488355328
141685511714388
-18 fastint
-256 fastint
-2147483648 fastint
-2147483649 fastint
-4275878553 fastint
-139486488458837 fastint
-140737488355328 fastint
-2147483648 fastint
-2147483649 fastint
-140737488355329
-141685511714389
- array wrapped integer
17 fastint
255 fastint
4275878552 fastint
4294967295 fastint
4294967295
4294967296
139486488458836
140737488355327
140737488355328
141685511714388
-18 fastint
-256 fastint
-2147483648 fastint
-2147483649
-4275878553
-2147483648
-2147483649
-140737488355329
-141685511714389
- integer-compatible double
17 fastint
17
- integer-compatible float
123 fastint
123
- integer-compatible half-float
123 fastint
123
===*/
function test() {
var inp, dec;
function t(arr) {
var dec = CBOR.decode(new Uint8Array(arr));
var t = Array.isArray(dec) ? dec[0] : dec;
var info_t = Duktape.info(t);
var info_double = Duktape.info(123.4);
if (info_t.itag === info_double.itag) {
print(t);
} else {
print(t, 'fastint');
}
//print(Duktape.enc('jx', Duktape.info(t)));
}
// If top level value is an integer, it gets coerced into a fastint
// because of return value fastint check for CBOR.decode() itself.
// This applies to the entire fastint range.
print('- top level integer');
t([ 0x11 ]);
t([ 0x18, 0xff ]);
t([ 0x1a, 0xfe, 0xdc, 0xba, 0x98 ]);
t([ 0x1a, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x1b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 ]);
t([ 0x1b, 0x00, 0x00, 0x7e, 0xdc, 0xba, 0x98, 0x76, 0x54 ]);
t([ 0x1b, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x1b, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 ]); // no longer in fastint range
t([ 0x1b, 0x00, 0x00, 0x80, 0xdc, 0xba, 0x98, 0x76, 0x54 ]); // no longer in fastint range
t([ 0x31 ]);
t([ 0x38, 0xff ]);
t([ 0x3a, 0x7f, 0xff, 0xff, 0xff ]);
t([ 0x3a, 0x80, 0x00, 0x00, 0x00 ]);
t([ 0x3a, 0xfe, 0xdc, 0xba, 0x98 ]);
t([ 0x3b, 0x00, 0x00, 0x7e, 0xdc, 0xba, 0x98, 0x76, 0x54 ]);
t([ 0x3b, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff ]);
t([ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 ]);
t([ 0x3b, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 ]); // no longer in fastint range
t([ 0x3b, 0x00, 0x00, 0x80, 0xdc, 0xba, 0x98, 0x76, 0x54 ]); // no longer in fastint range
// An integer deeper inside a structure won't get automatic return
// value fastint check, so it is up to the CBOR binding to ensure
// fastints are used (e.g. use duk_push_int() rather than
// duk_push_number() where applicable).
//
// At present fastints are used for [-0x80000000,0xffffffff] when
// they are encoded in shortest form (this is likely to change).
print('- array wrapped integer');
t([ 0x81, 0x11 ]);
t([ 0x81, 0x18, 0xff ]);
t([ 0x81, 0x1a, 0xfe, 0xdc, 0xba, 0x98 ]);
t([ 0x81, 0x1a, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x81, 0x1b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff ]); // in range, non-shortest encoding
t([ 0x81, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 ]); // no longer in 32-bit range
t([ 0x81, 0x1b, 0x00, 0x00, 0x7e, 0xdc, 0xba, 0x98, 0x76, 0x54 ]);
t([ 0x81, 0x1b, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff ]);
t([ 0x81, 0x1b, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 ]); // no longer in fastint range
t([ 0x81, 0x1b, 0x00, 0x00, 0x80, 0xdc, 0xba, 0x98, 0x76, 0x54 ]); // no longer in fastint range
t([ 0x81, 0x31 ]);
t([ 0x81, 0x38, 0xff ]);
t([ 0x81, 0x3a, 0x7f, 0xff, 0xff, 0xff ]);
t([ 0x81, 0x3a, 0x80, 0x00, 0x00, 0x00 ]);
t([ 0x81, 0x3a, 0xfe, 0xdc, 0xba, 0x98 ]);
t([ 0x81, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff ]); // in range, non-shortest encoding
t([ 0x81, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 ]); // no longer in 32-bit range
t([ 0x81, 0x3b, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 ]);
t([ 0x81, 0x3b, 0x00, 0x00, 0x80, 0xdc, 0xba, 0x98, 0x76, 0x54 ]);
// Integer-compatible double.
//
// At present no fastint check (except for top level value).
//
// >>> cbor.dumps(17.0).encode('hex')
// 'fb4031000000000000'
print('- integer-compatible double');
t([ 0xfb, 0x40, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]);
t([ 0x81, 0xfb, 0x40, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]);
// Integer-compatible float or half-float.
//
// At present no fastint check (except for top level value).
print('- integer-compatible float');
t([ 0xfa, 0x42, 0xf6, 0x00, 0x00 ]); // 123 as float
t([ 0x81, 0xfa, 0x42, 0xf6, 0x00, 0x00 ]);
print('- integer-compatible half-float');
t([ 0xf9, 0x57, 0xb0 ]); // 123 as half-float
t([ 0x81, 0xf9, 0x57, 0xb0 ]);
}
try {
test();
} catch (e) {
print(e.stack || e);
}

2
tests/ecmascript/test-dev-fastint-basic.js

@ -86,7 +86,7 @@ function printFastint(v) {
}
// The internal type tag depends on the duk_tval unpacked/packed layout.
// XXX: add to util.
if (typeof Duktape !== 'object') {
isfast = ' NOT-DUKTAPE';
} else if (getValueInternalTag(true) >= 0xfff0) {

41
tests/perf/test-cbor-decode-fastints.js

@ -0,0 +1,41 @@
if (typeof print !== 'function') { print = console.log; }
function build() {
var obj = [];
for (var i = 0; i < 1000; i++) {
obj.push(123);
obj.push(12);
obj.push(123);
obj.push(12);
obj.push(123);
obj.push(12);
obj.push(-123);
obj.push(-12);
obj.push(-123);
obj.push(-12);
obj.push(-123);
obj.push(-12);
}
return obj;
}
function test() {
var obj;
var buf;
var i;
obj = build();
buf = CBOR.encode(obj);
for (i = 0; i < 1e3; i++) {
void CBOR.decode(buf);
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

30
tests/perf/test-cbor-decode-strings.js

@ -0,0 +1,30 @@
if (typeof print !== 'function') { print = console.log; }
function build() {
var obj = [];
for (var i = 0; i < 10000; i++) {
obj.push('foobar-' + i);
}
return obj;
}
function test() {
var obj;
var buf;
var i;
obj = build();
buf = CBOR.encode(obj);
for (i = 0; i < 1e3; i++) {
void CBOR.decode(buf);
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

21
tests/perf/test-cbor-encode-double.js

@ -0,0 +1,21 @@
if (typeof print !== 'function') { print = console.log; }
function test() {
var msg = [];
while (msg.length < 10000) {
msg[msg.length] = Math.PI;
}
// print(Duktape.enc('hex', CBOR.encode(msg)));
for (var i = 0; i < 1e3; i++) {
void CBOR.encode(msg);
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

21
tests/perf/test-cbor-encode-float.js

@ -0,0 +1,21 @@
if (typeof print !== 'function') { print = console.log; }
function test() {
var msg = [];
while (msg.length < 10000) {
msg[msg.length] = 100000.5;
}
// print(Duktape.enc('hex', CBOR.encode(msg)));
for (var i = 0; i < 1e3; i++) {
void CBOR.encode(msg);
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

21
tests/perf/test-cbor-encode-half-float.js

@ -0,0 +1,21 @@
if (typeof print !== 'function') { print = console.log; }
function test() {
var msg = [];
while (msg.length < 10000) {
msg[msg.length] = 1.5;
}
// print(Duktape.enc('hex', CBOR.encode(msg)));
for (var i = 0; i < 1e3; i++) {
void CBOR.encode(msg);
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}
Loading…
Cancel
Save