158 lines
4.8 KiB
C
158 lines
4.8 KiB
C
|
/*
|
||
|
* Bitstream decoder.
|
||
|
*/
|
||
|
|
||
|
#include "duk_internal.h"
|
||
|
|
||
|
/* Decode 'bits' bits from the input stream (bits must be 1...24).
|
||
|
* When reading past bitstream end, zeroes are shifted in. The result
|
||
|
* is signed to match duk_bd_decode_flagged.
|
||
|
*/
|
||
|
DUK_INTERNAL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits) {
|
||
|
duk_small_int_t shift;
|
||
|
duk_uint32_t mask;
|
||
|
duk_uint32_t tmp;
|
||
|
|
||
|
/* Note: cannot read more than 24 bits without possibly shifting top bits out.
|
||
|
* Fixable, but adds complexity.
|
||
|
*/
|
||
|
DUK_ASSERT(bits >= 1 && bits <= 24);
|
||
|
|
||
|
while (ctx->currbits < bits) {
|
||
|
#if 0
|
||
|
DUK_DDD(DUK_DDDPRINT("decode_bits: shift more data (bits=%ld, currbits=%ld)",
|
||
|
(long) bits, (long) ctx->currbits));
|
||
|
#endif
|
||
|
ctx->currval <<= 8;
|
||
|
if (ctx->offset < ctx->length) {
|
||
|
/* If ctx->offset >= ctx->length, we "shift zeroes in"
|
||
|
* instead of croaking.
|
||
|
*/
|
||
|
ctx->currval |= ctx->data[ctx->offset++];
|
||
|
}
|
||
|
ctx->currbits += 8;
|
||
|
}
|
||
|
#if 0
|
||
|
DUK_DDD(DUK_DDDPRINT("decode_bits: bits=%ld, currbits=%ld, currval=0x%08lx",
|
||
|
(long) bits, (long) ctx->currbits, (unsigned long) ctx->currval));
|
||
|
#endif
|
||
|
|
||
|
/* Extract 'top' bits of currval; note that the extracted bits do not need
|
||
|
* to be cleared, we just ignore them on next round.
|
||
|
*/
|
||
|
shift = ctx->currbits - bits;
|
||
|
mask = (((duk_uint32_t) 1U) << bits) - 1U;
|
||
|
tmp = (ctx->currval >> shift) & mask;
|
||
|
ctx->currbits = shift; /* remaining */
|
||
|
|
||
|
#if 0
|
||
|
DUK_DDD(DUK_DDDPRINT("decode_bits: %ld bits -> 0x%08lx (%ld), currbits=%ld, currval=0x%08lx",
|
||
|
(long) bits, (unsigned long) tmp, (long) tmp, (long) ctx->currbits, (unsigned long) ctx->currval));
|
||
|
#endif
|
||
|
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
DUK_INTERNAL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx) {
|
||
|
return (duk_small_uint_t) duk_bd_decode(ctx, 1);
|
||
|
}
|
||
|
|
||
|
/* Decode a one-bit flag, and if set, decode a value of 'bits', otherwise return
|
||
|
* default value.
|
||
|
*/
|
||
|
DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value) {
|
||
|
if (duk_bd_decode_flag(ctx)) {
|
||
|
return duk_bd_decode(ctx, bits);
|
||
|
} else {
|
||
|
return def_value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Signed variant, allows negative marker value. */
|
||
|
DUK_INTERNAL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value) {
|
||
|
return (duk_int32_t) duk_bd_decode_flagged(ctx, bits, (duk_uint32_t) def_value);
|
||
|
}
|
||
|
|
||
|
/* Shared varint encoding. Match dukutil.py BitEncode.varuint(). */
|
||
|
DUK_INTERNAL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx) {
|
||
|
duk_small_uint_t t;
|
||
|
|
||
|
/* The bit encoding choices here are based on manual testing against
|
||
|
* the actual varuints generated by genbuiltins.py.
|
||
|
*/
|
||
|
switch (duk_bd_decode(ctx, 2)) {
|
||
|
case 0:
|
||
|
return 0; /* [0,0] */
|
||
|
case 1:
|
||
|
return duk_bd_decode(ctx, 2) + 1; /* [1,4] */
|
||
|
case 2:
|
||
|
return duk_bd_decode(ctx, 5) + 5; /* [5,36] */
|
||
|
default:
|
||
|
t = duk_bd_decode(ctx, 7);
|
||
|
if (t == 0) {
|
||
|
return duk_bd_decode(ctx, 20);
|
||
|
}
|
||
|
return (t - 1) + 37; /* [37,163] */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Decode a bit packed string from a custom format used by genbuiltins.py.
|
||
|
* This function is here because it's used for both heap and thread inits.
|
||
|
* Caller must supply the output buffer whose size is NOT checked!
|
||
|
*/
|
||
|
|
||
|
#define DUK__BITPACK_LETTER_LIMIT 26
|
||
|
#define DUK__BITPACK_LOOKUP1 26
|
||
|
#define DUK__BITPACK_LOOKUP2 27
|
||
|
#define DUK__BITPACK_SWITCH1 28
|
||
|
#define DUK__BITPACK_SWITCH 29
|
||
|
#define DUK__BITPACK_UNUSED1 30
|
||
|
#define DUK__BITPACK_EIGHTBIT 31
|
||
|
|
||
|
DUK_LOCAL const duk_uint8_t duk__bitpacked_lookup[16] = {
|
||
|
DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3,
|
||
|
DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7,
|
||
|
DUK_ASC_8, DUK_ASC_9, DUK_ASC_UNDERSCORE, DUK_ASC_SPACE,
|
||
|
0x82, 0x80, DUK_ASC_DOUBLEQUOTE, DUK_ASC_LCURLY
|
||
|
};
|
||
|
|
||
|
DUK_INTERNAL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out) {
|
||
|
duk_small_uint_t len;
|
||
|
duk_small_uint_t mode;
|
||
|
duk_small_uint_t t;
|
||
|
duk_small_uint_t i;
|
||
|
|
||
|
len = duk_bd_decode(bd, 5);
|
||
|
if (len == 31) {
|
||
|
len = duk_bd_decode(bd, 8); /* Support up to 256 bytes; rare. */
|
||
|
}
|
||
|
|
||
|
mode = 32; /* 0 = uppercase, 32 = lowercase (= 'a' - 'A') */
|
||
|
for (i = 0; i < len; i++) {
|
||
|
t = duk_bd_decode(bd, 5);
|
||
|
if (t < DUK__BITPACK_LETTER_LIMIT) {
|
||
|
t = t + DUK_ASC_UC_A + mode;
|
||
|
} else if (t == DUK__BITPACK_LOOKUP1) {
|
||
|
t = duk__bitpacked_lookup[duk_bd_decode(bd, 3)];
|
||
|
} else if (t == DUK__BITPACK_LOOKUP2) {
|
||
|
t = duk__bitpacked_lookup[8 + duk_bd_decode(bd, 3)];
|
||
|
} else if (t == DUK__BITPACK_SWITCH1) {
|
||
|
t = duk_bd_decode(bd, 5);
|
||
|
DUK_ASSERT_DISABLE(t >= 0); /* unsigned */
|
||
|
DUK_ASSERT(t <= 25);
|
||
|
t = t + DUK_ASC_UC_A + (mode ^ 32);
|
||
|
} else if (t == DUK__BITPACK_SWITCH) {
|
||
|
mode = mode ^ 32;
|
||
|
t = duk_bd_decode(bd, 5);
|
||
|
DUK_ASSERT_DISABLE(t >= 0);
|
||
|
DUK_ASSERT(t <= 25);
|
||
|
t = t + DUK_ASC_UC_A + mode;
|
||
|
} else if (t == DUK__BITPACK_EIGHTBIT) {
|
||
|
t = duk_bd_decode(bd, 8);
|
||
|
}
|
||
|
out[i] = (duk_uint8_t) t;
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|