| /* |
| * Example debug transport with a local debug message encoder/decoder. |
| * |
| * Provides a "received dvalue" callback for a fully parsed dvalue (user |
| * code frees dvalue) and a "cooperate" callback for e.g. UI integration. |
| * There are a few other callbacks. See test.c for usage examples. |
| * |
| * This transport implementation is not multithreaded which means that: |
| * |
| * - Callbacks to "received dvalue" callback come from the Duktape thread, |
| * either during normal execution or from duk_debugger_cooperate(). |
| * |
| * - Calls into duk_trans_dvalue_send() must be made from the callbacks |
| * provided (e.g. "received dvalue" or "cooperate") which use the active |
| * Duktape thread. |
| * |
| * - The only exception to this is when Duktape is idle: you can then call |
| * duk_trans_dvalue_send() from any thread (only one thread at a time). |
| * When you next call into Duktape or call duk_debugger_cooperate(), the |
| * queued data will be read and processed by Duktape. |
| * |
| * There are functions for creating and freeing values; internally they use |
| * malloc() and free() for memory management. Duktape heap alloc functions |
| * are not used to minimize disturbances to the Duktape heap under debugging. |
| * |
| * Doesn't depend on C99 types; assumes "int" is at least 32 bits, and makes |
| * a few assumptions about format specifiers. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "duktape.h" |
| #include "duk_trans_dvalue.h" |
| |
| /* Define to enable debug prints to stderr. */ |
| #if 0 |
| #define DEBUG_PRINTS |
| #endif |
| |
| /* Define to enable error prints to stderr. */ |
| #if 1 |
| #define ERROR_PRINTS |
| #endif |
| |
| /* |
| * Dvalue handling |
| */ |
| |
| duk_dvalue *duk_dvalue_alloc(void) { |
| duk_dvalue *dv = (duk_dvalue *) malloc(sizeof(duk_dvalue)); |
| if (dv) { |
| memset((void *) dv, 0, sizeof(duk_dvalue)); |
| dv->buf = NULL; |
| } |
| return dv; |
| } |
| |
| void duk_dvalue_free(duk_dvalue *dv) { |
| if (dv) { |
| free(dv->buf); /* tolerates NULL */ |
| dv->buf = NULL; |
| free(dv); |
| } |
| } |
| |
| static void duk__dvalue_bufesc(duk_dvalue *dv, char *buf, size_t maxbytes, int stresc) { |
| size_t i, limit; |
| |
| *buf = (char) 0; |
| limit = dv->len > maxbytes ? maxbytes : dv->len; |
| for (i = 0; i < limit; i++) { |
| unsigned char c = dv->buf[i]; |
| if (stresc) { |
| if (c >= 0x20 && c <= 0x7e && c != (char) '"' && c != (char) '\'') { |
| sprintf(buf, "%c", c); |
| buf++; |
| } else { |
| sprintf(buf, "\\x%02x", (unsigned int) c); |
| buf += 4; |
| } |
| } else { |
| sprintf(buf, "%02x", (unsigned int) c); |
| buf += 2; |
| } |
| } |
| if (dv->len > maxbytes) { |
| sprintf(buf, "..."); |
| buf += 3; |
| } |
| } |
| |
| /* Caller must provide a buffer at least DUK_DVALUE_TOSTRING_BUFLEN in size. */ |
| void duk_dvalue_to_string(duk_dvalue *dv, char *buf) { |
| char hexbuf[32 * 4 + 4]; /* 32 hex encoded or \xXX escaped bytes, possible "...", NUL */ |
| |
| if (!dv) { |
| sprintf(buf, "NULL"); |
| return; |
| } |
| |
| switch (dv->tag) { |
| case DUK_DVALUE_EOM: |
| sprintf(buf, "EOM"); |
| break; |
| case DUK_DVALUE_REQ: |
| sprintf(buf, "REQ"); |
| break; |
| case DUK_DVALUE_REP: |
| sprintf(buf, "REP"); |
| break; |
| case DUK_DVALUE_ERR: |
| sprintf(buf, "ERR"); |
| break; |
| case DUK_DVALUE_NFY: |
| sprintf(buf, "NFY"); |
| break; |
| case DUK_DVALUE_INTEGER: |
| sprintf(buf, "%d", dv->i); |
| break; |
| case DUK_DVALUE_STRING: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 1); |
| sprintf(buf, "str:%ld:\"%s\"", (long) dv->len, hexbuf); |
| break; |
| case DUK_DVALUE_BUFFER: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 0); |
| sprintf(buf, "buf:%ld:%s", (long) dv->len, hexbuf); |
| break; |
| case DUK_DVALUE_UNUSED: |
| sprintf(buf, "undefined"); |
| break; |
| case DUK_DVALUE_UNDEFINED: |
| sprintf(buf, "undefined"); |
| break; |
| case DUK_DVALUE_NULL: |
| sprintf(buf, "null"); |
| break; |
| case DUK_DVALUE_TRUE: |
| sprintf(buf, "true"); |
| break; |
| case DUK_DVALUE_FALSE: |
| sprintf(buf, "false"); |
| break; |
| case DUK_DVALUE_NUMBER: |
| if (fpclassify(dv->d) == FP_ZERO) { |
| if (signbit(dv->d)) { |
| sprintf(buf, "-0"); |
| } else { |
| sprintf(buf, "0"); |
| } |
| } else { |
| sprintf(buf, "%lg", dv->d); |
| } |
| break; |
| case DUK_DVALUE_OBJECT: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 0); |
| sprintf(buf, "obj:%d:%s", (int) dv->i, hexbuf); |
| break; |
| case DUK_DVALUE_POINTER: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 0); |
| sprintf(buf, "ptr:%s", hexbuf); |
| break; |
| case DUK_DVALUE_LIGHTFUNC: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 0); |
| sprintf(buf, "lfunc:%04x:%s", (unsigned int) dv->i, hexbuf); |
| break; |
| case DUK_DVALUE_HEAPPTR: |
| duk__dvalue_bufesc(dv, hexbuf, 32, 0); |
| sprintf(buf, "heapptr:%s", hexbuf); |
| break; |
| default: |
| sprintf(buf, "unknown:%d", (int) dv->tag); |
| } |
| } |
| |
| duk_dvalue *duk_dvalue_make_tag(int tag) { |
| duk_dvalue *dv = duk_dvalue_alloc(); |
| if (!dv) { return NULL; } |
| dv->tag = tag; |
| return dv; |
| } |
| |
| duk_dvalue *duk_dvalue_make_tag_int(int tag, int intval) { |
| duk_dvalue *dv = duk_dvalue_alloc(); |
| if (!dv) { return NULL; } |
| dv->tag = tag; |
| dv->i = intval; |
| return dv; |
| } |
| |
| duk_dvalue *duk_dvalue_make_tag_double(int tag, double dblval) { |
| duk_dvalue *dv = duk_dvalue_alloc(); |
| if (!dv) { return NULL; } |
| dv->tag = tag; |
| dv->d = dblval; |
| return dv; |
| } |
| |
| duk_dvalue *duk_dvalue_make_tag_data(int tag, const char *buf, size_t len) { |
| unsigned char *p; |
| duk_dvalue *dv = duk_dvalue_alloc(); |
| if (!dv) { return NULL; } |
| /* Alloc size is len + 1 so that a NUL terminator is always |
| * guaranteed which is convenient, e.g. you can printf() the |
| * value safely. |
| */ |
| p = (unsigned char *) malloc(len + 1); |
| if (!p) { |
| free(dv); |
| return NULL; |
| } |
| memcpy((void *) p, (const void *) buf, len); |
| p[len] = (unsigned char) 0; |
| dv->tag = tag; |
| dv->buf = p; |
| dv->len = len; |
| return dv; |
| } |
| |
| duk_dvalue *duk_dvalue_make_tag_int_data(int tag, int intval, const char *buf, size_t len) { |
| duk_dvalue *dv = duk_dvalue_make_tag_data(tag, buf, len); |
| if (!dv) { return NULL; } |
| dv->i = intval; |
| return dv; |
| } |
| |
| /* |
| * Dvalue transport handling |
| */ |
| |
| static void duk__trans_dvalue_double_byteswap(duk_trans_dvalue_ctx *ctx, volatile unsigned char *p) { |
| unsigned char t; |
| |
| /* Portable IEEE double byteswap. Relies on runtime detection of |
| * host endianness. |
| */ |
| |
| if (ctx->double_byteorder == 0) { |
| /* little endian */ |
| t = p[0]; p[0] = p[7]; p[7] = t; |
| t = p[1]; p[1] = p[6]; p[6] = t; |
| t = p[2]; p[2] = p[5]; p[5] = t; |
| t = p[3]; p[3] = p[4]; p[4] = t; |
| } else if (ctx->double_byteorder == 1) { |
| /* big endian: ok as is */ |
| ; |
| } else { |
| /* mixed endian */ |
| t = p[0]; p[0] = p[3]; p[3] = t; |
| t = p[1]; p[1] = p[2]; p[2] = t; |
| t = p[4]; p[4] = p[7]; p[7] = t; |
| t = p[5]; p[5] = p[6]; p[6] = t; |
| } |
| } |
| |
| static unsigned int duk__trans_dvalue_parse_u32(duk_trans_dvalue_ctx *ctx, unsigned char *p) { |
| /* Integers are network endian, read back into host format in |
| * a portable manner. |
| */ |
| (void) ctx; |
| return (((unsigned int) p[0]) << 24) + |
| (((unsigned int) p[1]) << 16) + |
| (((unsigned int) p[2]) << 8) + |
| (((unsigned int) p[3]) << 0); |
| } |
| |
| static int duk__trans_dvalue_parse_i32(duk_trans_dvalue_ctx *ctx, unsigned char *p) { |
| /* Portable sign handling, doesn't assume 'int' is exactly 32 bits |
| * like a direct cast would. |
| */ |
| unsigned int tmp = duk__trans_dvalue_parse_u32(ctx, p); |
| if (tmp & 0x80000000UL) { |
| return -((int) ((tmp ^ 0xffffffffUL) + 1UL)); |
| } else { |
| return tmp; |
| } |
| } |
| |
| static unsigned int duk__trans_dvalue_parse_u16(duk_trans_dvalue_ctx *ctx, unsigned char *p) { |
| /* Integers are network endian, read back into host format. */ |
| (void) ctx; |
| return (((unsigned int) p[0]) << 8) + |
| (((unsigned int) p[1]) << 0); |
| } |
| |
| static double duk__trans_dvalue_parse_double(duk_trans_dvalue_ctx *ctx, unsigned char *p) { |
| /* IEEE doubles are network endian, read back into host format. */ |
| volatile union { |
| double d; |
| unsigned char b[8]; |
| } u; |
| memcpy((void *) u.b, (const void *) p, 8); |
| duk__trans_dvalue_double_byteswap(ctx, u.b); |
| return u.d; |
| } |
| |
| static unsigned char *duk__trans_dvalue_encode_u32(duk_trans_dvalue_ctx *ctx, unsigned char *p, unsigned int val) { |
| /* Integers are written in network endian format. */ |
| (void) ctx; |
| *p++ = (unsigned char) ((val >> 24) & 0xff); |
| *p++ = (unsigned char) ((val >> 16) & 0xff); |
| *p++ = (unsigned char) ((val >> 8) & 0xff); |
| *p++ = (unsigned char) (val & 0xff); |
| return p; |
| } |
| |
| static unsigned char *duk__trans_dvalue_encode_i32(duk_trans_dvalue_ctx *ctx, unsigned char *p, int val) { |
| return duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) val & 0xffffffffUL); |
| } |
| |
| static unsigned char *duk__trans_dvalue_encode_u16(duk_trans_dvalue_ctx *ctx, unsigned char *p, unsigned int val) { |
| /* Integers are written in network endian format. */ |
| (void) ctx; |
| *p++ = (unsigned char) ((val >> 8) & 0xff); |
| *p++ = (unsigned char) (val & 0xff); |
| return p; |
| } |
| |
| static unsigned char *duk__trans_dvalue_encode_double(duk_trans_dvalue_ctx *ctx, unsigned char *p, double val) { |
| /* IEEE doubles are written in network endian format. */ |
| volatile union { |
| double d; |
| unsigned char b[8]; |
| } u; |
| u.d = val; |
| duk__trans_dvalue_double_byteswap(ctx, u.b); |
| memcpy((void *) p, (const void *) u.b, 8); |
| p += 8; |
| return p; |
| } |
| |
| static unsigned char *duk__trans_buffer_ensure(duk_trans_buffer *dbuf, size_t space) { |
| size_t avail; |
| size_t used; |
| size_t new_size; |
| void *new_alloc; |
| |
| used = dbuf->write_offset; |
| avail = dbuf->alloc_size - dbuf->write_offset; |
| |
| if (avail >= space) { |
| if (avail - space > 256) { |
| /* Too big, resize so that we reclaim memory if we have just |
| * received a large string/buffer value. |
| */ |
| goto do_realloc; |
| } |
| } else { |
| /* Too small, resize. */ |
| goto do_realloc; |
| } |
| |
| return dbuf->base + dbuf->write_offset; |
| |
| do_realloc: |
| new_size = used + space + 256; /* some extra to reduce resizes */ |
| new_alloc = realloc(dbuf->base, new_size); |
| if (new_alloc) { |
| dbuf->base = (unsigned char *) new_alloc; |
| dbuf->alloc_size = new_size; |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: resized buffer %p to %ld bytes, read_offset=%ld, write_offset=%ld\n", |
| __func__, (void *) dbuf, (long) new_size, (long) dbuf->read_offset, (long) dbuf->write_offset); |
| fflush(stderr); |
| #endif |
| return dbuf->base + dbuf->write_offset; |
| } else { |
| return NULL; |
| } |
| } |
| |
| /* When read_offset is large enough, "rebase" buffer by deleting already |
| * read data and updating offsets. |
| */ |
| static void duk__trans_buffer_rebase(duk_trans_buffer *dbuf) { |
| if (dbuf->read_offset > 64) { |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: rebasing buffer %p, read_offset=%ld, write_offset=%ld\n", |
| __func__, (void *) dbuf, (long) dbuf->read_offset, (long) dbuf->write_offset); |
| fflush(stderr); |
| #endif |
| if (dbuf->write_offset > dbuf->read_offset) { |
| memmove((void *) dbuf->base, (const void *) (dbuf->base + dbuf->read_offset), dbuf->write_offset - dbuf->read_offset); |
| } |
| dbuf->write_offset -= dbuf->read_offset; |
| dbuf->read_offset = 0; |
| } |
| } |
| |
| duk_trans_dvalue_ctx *duk_trans_dvalue_init(void) { |
| volatile union { |
| double d; |
| unsigned char b[8]; |
| } u; |
| duk_trans_dvalue_ctx *ctx = NULL; |
| |
| ctx = (duk_trans_dvalue_ctx *) malloc(sizeof(duk_trans_dvalue_ctx)); |
| if (!ctx) { goto fail; } |
| memset((void *) ctx, 0, sizeof(duk_trans_dvalue_ctx)); |
| ctx->received = NULL; |
| ctx->cooperate = NULL; |
| ctx->handshake = NULL; |
| ctx->detached = NULL; |
| ctx->send_buf.base = NULL; |
| ctx->recv_buf.base = NULL; |
| |
| ctx->send_buf.base = malloc(256); |
| if (!ctx->send_buf.base) { goto fail; } |
| ctx->send_buf.alloc_size = 256; |
| |
| ctx->recv_buf.base = malloc(256); |
| if (!ctx->recv_buf.base) { goto fail; } |
| ctx->recv_buf.alloc_size = 256; |
| |
| /* IEEE double byte order, detect at run time (could also use |
| * preprocessor defines but that's verbose to make portable). |
| * |
| * >>> struct.unpack('>d', '1122334455667788'.decode('hex')) |
| * (3.841412024471731e-226,) |
| * >>> struct.unpack('>d', '8877665544332211'.decode('hex')) |
| * (-7.086876636573014e-268,) |
| * >>> struct.unpack('>d', '4433221188776655'.decode('hex')) |
| * (3.5294303071877444e+20,) |
| */ |
| u.b[0] = 0x11; u.b[1] = 0x22; u.b[2] = 0x33; u.b[3] = 0x44; |
| u.b[4] = 0x55; u.b[5] = 0x66; u.b[6] = 0x77; u.b[7] = 0x88; |
| if (u.d < 0.0) { |
| ctx->double_byteorder = 0; /* little endian */ |
| } else if (u.d < 1.0) { |
| ctx->double_byteorder = 1; /* big endian */ |
| } else { |
| ctx->double_byteorder = 2; /* mixed endian (arm) */ |
| } |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "double endianness test value is %lg -> byteorder %d\n", |
| u.d, ctx->double_byteorder); |
| fflush(stderr); |
| #endif |
| |
| return ctx; |
| |
| fail: |
| if (ctx) { |
| free(ctx->recv_buf.base); /* tolerates NULL */ |
| free(ctx->send_buf.base); /* tolerates NULL */ |
| free(ctx); |
| } |
| return NULL; |
| } |
| |
| void duk_trans_dvalue_free(duk_trans_dvalue_ctx *ctx) { |
| if (ctx) { |
| free(ctx->send_buf.base); /* tolerates NULL */ |
| free(ctx->recv_buf.base); /* tolerates NULL */ |
| free(ctx); |
| } |
| } |
| |
| void duk_trans_dvalue_send(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { |
| unsigned char *p; |
| |
| /* Convert argument dvalue into Duktape debug protocol format. |
| * Literal constants are used here for the debug protocol, |
| * e.g. initial byte 0x02 is REP, see doc/debugger.rst. |
| */ |
| |
| #if defined(DEBUG_PRINTS) |
| { |
| char buf[DUK_DVALUE_TOSTRING_BUFLEN]; |
| duk_dvalue_to_string(dv, buf); |
| fprintf(stderr, "%s: sending dvalue: %s\n", __func__, buf); |
| fflush(stderr); |
| } |
| #endif |
| |
| switch (dv->tag) { |
| case DUK_DVALUE_EOM: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x00; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_REQ: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x01; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_REP: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x02; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_ERR: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x03; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_NFY: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x04; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_INTEGER: { |
| int i = dv->i; |
| if (i >= 0 && i <= 63) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = (unsigned char) (0x80 + i); |
| ctx->send_buf.write_offset += 1; |
| } else if (i >= 0 && i <= 16383L) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 2); |
| if (!p) { goto alloc_error; } |
| *p++ = (unsigned char) (0xc0 + (i >> 8)); |
| *p++ = (unsigned char) (i & 0xff); |
| ctx->send_buf.write_offset += 2; |
| } else if (i >= -0x80000000L && i <= 0x7fffffffL) { /* Harmless warning on some platforms (re: range) */ |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 5); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x10; |
| p = duk__trans_dvalue_encode_i32(ctx, p, i); |
| ctx->send_buf.write_offset += 5; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_STRING: { |
| size_t i = dv->len; |
| if (i <= 0x1fUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = (unsigned char) (0x60 + i); |
| memcpy((void *) p, (const void *) dv->buf, i); |
| p += i; |
| ctx->send_buf.write_offset += 1 + i; |
| } else if (i <= 0xffffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x12; |
| p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) i); |
| memcpy((void *) p, (const void *) dv->buf, i); |
| p += i; |
| ctx->send_buf.write_offset += 3 + i; |
| } else if (i <= 0xffffffffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 5 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x11; |
| p = duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) i); |
| memcpy((void *) p, (const void *) dv->buf, i); |
| p += i; |
| ctx->send_buf.write_offset += 5 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_BUFFER: { |
| size_t i = dv->len; |
| if (i <= 0xffffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x14; |
| p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) i); |
| memcpy((void *) p, (const void *) dv->buf, i); |
| p += i; |
| ctx->send_buf.write_offset += 3 + i; |
| } else if (i <= 0xffffffffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 5 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x13; |
| p = duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) i); |
| memcpy((void *) p, (const void *) dv->buf, i); |
| p += i; |
| ctx->send_buf.write_offset += 5 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_UNUSED: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x15; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_UNDEFINED: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x16; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_NULL: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x17; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_TRUE: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x18; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_FALSE: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 1); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x19; |
| ctx->send_buf.write_offset += 1; |
| break; |
| } |
| case DUK_DVALUE_NUMBER: { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 9); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x1a; |
| p = duk__trans_dvalue_encode_double(ctx, p, dv->d); |
| ctx->send_buf.write_offset += 9; |
| break; |
| } |
| case DUK_DVALUE_OBJECT: { |
| size_t i = dv->len; |
| if (i <= 0xffUL && dv->i >= 0 && dv->i <= 0xffL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x1b; |
| *p++ = (unsigned char) dv->i; |
| *p++ = (unsigned char) i; |
| memcpy((void *) p, (const void *) dv->buf, i); |
| ctx->send_buf.write_offset += 3 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_POINTER: { |
| size_t i = dv->len; |
| if (i <= 0xffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 2 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x1c; |
| *p++ = (unsigned char) i; |
| memcpy((void *) p, (const void *) dv->buf, i); |
| ctx->send_buf.write_offset += 2 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_LIGHTFUNC: { |
| size_t i = dv->len; |
| if (i <= 0xffUL && dv->i >= 0 && dv->i <= 0xffffL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 4 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x1d; |
| p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) dv->i); |
| *p++ = (unsigned char) i; |
| memcpy((void *) p, (const void *) dv->buf, i); |
| ctx->send_buf.write_offset += 4 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| case DUK_DVALUE_HEAPPTR: { |
| size_t i = dv->len; |
| if (i <= 0xffUL) { |
| p = duk__trans_buffer_ensure(&ctx->send_buf, 2 + i); |
| if (!p) { goto alloc_error; } |
| *p++ = 0x1e; |
| *p++ = (unsigned char) i; |
| memcpy((void *) p, (const void *) dv->buf, i); |
| ctx->send_buf.write_offset += 2 + i; |
| } else { |
| goto dvalue_error; |
| } |
| break; |
| } |
| default: { |
| goto dvalue_error; |
| } |
| } /* end switch */ |
| |
| return; |
| |
| dvalue_error: |
| #if defined(ERROR_PRINTS) |
| fprintf(stderr, "%s: internal error, argument dvalue is invalid\n", __func__); |
| fflush(stdout); |
| #endif |
| return; |
| |
| alloc_error: |
| #if defined(ERROR_PRINTS) |
| fprintf(stderr, "%s: internal error, failed to allocate space for write\n", __func__); |
| fflush(stdout); |
| #endif |
| return; |
| } |
| |
| static void duk__trans_dvalue_send_and_free(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { |
| if (!dv) { return; } |
| duk_trans_dvalue_send(ctx, dv); |
| duk_dvalue_free(dv); |
| } |
| |
| void duk_trans_dvalue_send_eom(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_EOM)); |
| } |
| |
| void duk_trans_dvalue_send_req(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_REQ)); |
| } |
| |
| void duk_trans_dvalue_send_rep(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_REP)); |
| } |
| |
| void duk_trans_dvalue_send_err(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_ERR)); |
| } |
| |
| void duk_trans_dvalue_send_nfy(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_NFY)); |
| } |
| |
| void duk_trans_dvalue_send_integer(duk_trans_dvalue_ctx *ctx, int val) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, val)); |
| } |
| |
| void duk_trans_dvalue_send_string(duk_trans_dvalue_ctx *ctx, const char *str) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_STRING, str, strlen(str))); |
| } |
| |
| void duk_trans_dvalue_send_lstring(duk_trans_dvalue_ctx *ctx, const char *str, size_t len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_STRING, str, len)); |
| } |
| |
| void duk_trans_dvalue_send_buffer(duk_trans_dvalue_ctx *ctx, const char *buf, size_t len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, buf, len)); |
| } |
| |
| void duk_trans_dvalue_send_unused(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_UNUSED)); |
| } |
| |
| void duk_trans_dvalue_send_undefined(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_UNDEFINED)); |
| } |
| |
| void duk_trans_dvalue_send_null(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_NULL)); |
| } |
| |
| void duk_trans_dvalue_send_true(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_TRUE)); |
| } |
| |
| void duk_trans_dvalue_send_false(duk_trans_dvalue_ctx *ctx) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_FALSE)); |
| } |
| |
| void duk_trans_dvalue_send_number(duk_trans_dvalue_ctx *ctx, double val) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_double(DUK_DVALUE_NUMBER, val)); |
| } |
| |
| void duk_trans_dvalue_send_object(duk_trans_dvalue_ctx *ctx, int classnum, const char *ptr_data, size_t ptr_len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int_data(DUK_DVALUE_OBJECT, classnum, ptr_data, ptr_len)); |
| } |
| |
| void duk_trans_dvalue_send_pointer(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_POINTER, ptr_data, ptr_len)); |
| } |
| |
| void duk_trans_dvalue_send_lightfunc(duk_trans_dvalue_ctx *ctx, int lf_flags, const char *ptr_data, size_t ptr_len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int_data(DUK_DVALUE_LIGHTFUNC, lf_flags, ptr_data, ptr_len)); |
| } |
| |
| void duk_trans_dvalue_send_heapptr(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len) { |
| duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_HEAPPTR, ptr_data, ptr_len)); |
| } |
| |
| void duk_trans_dvalue_send_req_cmd(duk_trans_dvalue_ctx *ctx, int cmd) { |
| duk_trans_dvalue_send_req(ctx); |
| duk_trans_dvalue_send_integer(ctx, cmd); |
| } |
| |
| static duk_dvalue *duk__trans_trial_parse_dvalue(duk_trans_dvalue_ctx *ctx) { |
| unsigned char *p; |
| size_t len; |
| unsigned char ib; |
| duk_dvalue *dv; |
| size_t datalen; |
| |
| p = ctx->recv_buf.base + ctx->recv_buf.read_offset; |
| len = ctx->recv_buf.write_offset - ctx->recv_buf.read_offset; |
| |
| if (len == 0) { |
| return NULL; |
| } |
| ib = p[0]; |
| |
| #if defined(DEBUG_PRINTS) |
| { |
| size_t i; |
| fprintf(stderr, "%s: parsing dvalue, window:", __func__); |
| for (i = 0; i < 16; i++) { |
| if (i < len) { |
| fprintf(stderr, " %02x", (unsigned int) p[i]); |
| } else { |
| fprintf(stderr, " ??"); |
| } |
| } |
| fprintf(stderr, " (length %ld, read_offset %ld, write_offset %ld, alloc_size %ld)\n", |
| (long) len, (long) ctx->recv_buf.read_offset, (long) ctx->recv_buf.write_offset, |
| (long) ctx->recv_buf.alloc_size); |
| fflush(stderr); |
| } |
| #endif |
| |
| if (ib <= 0x1fU) { |
| /* 0x00 ... 0x1f */ |
| switch (ib) { |
| case 0x00: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_EOM); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x01: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_REQ); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x02: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_REP); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x03: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_ERR); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x04: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_NFY); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x10: { |
| int intval; |
| if (len < 5) { goto partial; } |
| intval = duk__trans_dvalue_parse_i32(ctx, p + 1); |
| ctx->recv_buf.read_offset += 5; |
| dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x11: { |
| if (len < 5) { goto partial; } |
| datalen = (size_t) duk__trans_dvalue_parse_u32(ctx, p + 1); |
| if (len < 5 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 5 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 5), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x12: { |
| if (len < 3) { goto partial; } |
| datalen = (size_t) duk__trans_dvalue_parse_u16(ctx, p + 1); |
| if (len < 3 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 3 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 3), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x13: { |
| if (len < 5) { goto partial; } |
| datalen = (size_t) duk__trans_dvalue_parse_u32(ctx, p + 1); |
| if (len < 5 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 5 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, (const char *) (p + 5), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x14: { |
| if (len < 3) { goto partial; } |
| datalen = (size_t) duk__trans_dvalue_parse_u16(ctx, p + 1); |
| if (len < 3 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 3 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, (const char *) (p + 3), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x15: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_UNUSED); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x16: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_UNDEFINED); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x17: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_NULL); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x18: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_TRUE); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x19: { |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag(DUK_DVALUE_FALSE); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x1a: { |
| double dblval; |
| if (len < 9) { goto partial; } |
| dblval = duk__trans_dvalue_parse_double(ctx, p + 1); |
| ctx->recv_buf.read_offset += 9; |
| dv = duk_dvalue_make_tag_double(DUK_DVALUE_NUMBER, dblval); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x1b: { |
| int classnum; |
| if (len < 3) { goto partial; } |
| datalen = (size_t) p[2]; |
| if (len < 3 + datalen) { goto partial; } |
| classnum = (int) p[1]; |
| ctx->recv_buf.read_offset += 3 + datalen; |
| dv = duk_dvalue_make_tag_int_data(DUK_DVALUE_OBJECT, classnum, (const char *) (p + 3), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x1c: { |
| if (len < 2) { goto partial; } |
| datalen = (size_t) p[1]; |
| if (len < 2 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 2 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_POINTER, (const char *) (p + 2), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x1d: { |
| int lf_flags; |
| if (len < 4) { goto partial; } |
| datalen = (size_t) p[3]; |
| if (len < 4 + datalen) { goto partial; } |
| lf_flags = (int) duk__trans_dvalue_parse_u16(ctx, p + 1); |
| ctx->recv_buf.read_offset += 4 + datalen; |
| dv = duk_dvalue_make_tag_int_data(DUK_DVALUE_LIGHTFUNC, lf_flags, (const char *) (p + 4), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| case 0x1e: { |
| if (len < 2) { goto partial; } |
| datalen = (size_t) p[1]; |
| if (len < 2 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 2 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_HEAPPTR, (const char *) (p + 2), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| default: { |
| goto format_error; |
| } |
| } /* end switch */ |
| } else if (ib <= 0x5fU) { |
| /* 0x20 ... 0x5f */ |
| goto format_error; |
| } else if (ib <= 0x7fU) { |
| /* 0x60 ... 0x7f */ |
| datalen = (size_t) (ib - 0x60U); |
| if (len < 1 + datalen) { goto partial; } |
| ctx->recv_buf.read_offset += 1 + datalen; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 1), datalen); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } else if (ib <= 0xbfU) { |
| /* 0x80 ... 0xbf */ |
| int intval; |
| intval = (int) (ib - 0x80U); |
| ctx->recv_buf.read_offset += 1; |
| dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } else { |
| /* 0xc0 ... 0xff */ |
| int intval; |
| if (len < 2) { goto partial; } |
| intval = (((int) (ib - 0xc0U)) << 8) + (int) p[1]; |
| ctx->recv_buf.read_offset += 2; |
| dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| |
| /* never here */ |
| |
| partial: |
| return NULL; |
| |
| alloc_error: |
| #if defined(ERROR_PRINTS) |
| fprintf(stderr, "%s: internal error, cannot allocate space for dvalue\n", __func__); |
| fflush(stdout); |
| #endif |
| return NULL; |
| |
| format_error: |
| #if defined(ERROR_PRINTS) |
| fprintf(stderr, "%s: internal error, dvalue format error\n", __func__); |
| fflush(stdout); |
| #endif |
| return NULL; |
| } |
| |
| static duk_dvalue *duk__trans_trial_parse_handshake(duk_trans_dvalue_ctx *ctx) { |
| unsigned char *p; |
| size_t len; |
| duk_dvalue *dv; |
| size_t i; |
| |
| p = ctx->recv_buf.base + ctx->recv_buf.read_offset; |
| len = ctx->recv_buf.write_offset - ctx->recv_buf.read_offset; |
| |
| for (i = 0; i < len; i++) { |
| if (p[i] == 0x0a) { |
| /* Handshake line is returned as a dvalue for convenience; it's |
| * not actually a part of the dvalue phase of the protocol. |
| */ |
| ctx->recv_buf.read_offset += i + 1; |
| dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) p, i); |
| if (!dv) { goto alloc_error; } |
| return dv; |
| } |
| } |
| |
| return NULL; |
| |
| alloc_error: |
| #if defined(ERROR_PRINTS) |
| fprintf(stderr, "%s: internal error, cannot allocate space for handshake line\n", __func__); |
| fflush(stdout); |
| #endif |
| return NULL; |
| } |
| |
| static void duk__trans_call_cooperate(duk_trans_dvalue_ctx *ctx, int block) { |
| if (ctx->cooperate) { |
| ctx->cooperate(ctx, block); |
| } |
| } |
| |
| static void duk__trans_call_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { |
| if (ctx->received) { |
| ctx->received(ctx, dv); |
| } |
| } |
| |
| static void duk__trans_call_handshake(duk_trans_dvalue_ctx *ctx, const char *line) { |
| if (ctx->handshake) { |
| ctx->handshake(ctx, line); |
| } |
| } |
| |
| static void duk__trans_call_detached(duk_trans_dvalue_ctx *ctx) { |
| if (ctx->detached) { |
| ctx->detached(ctx); |
| } |
| } |
| |
| /* |
| * Duktape callbacks |
| */ |
| |
| duk_size_t duk_trans_dvalue_read_cb(void *udata, char *buffer, duk_size_t length) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p %p %ld\n", __func__, udata, (void *) buffer, (long) length); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_cooperate(ctx, 0); |
| |
| for (;;) { |
| size_t avail, now; |
| |
| avail = (size_t) (ctx->send_buf.write_offset - ctx->send_buf.read_offset); |
| if (avail == 0) { |
| /* Must cooperate until user callback provides data. From |
| * Duktape's perspective we MUST block until data is received. |
| */ |
| duk__trans_call_cooperate(ctx, 1); |
| } else { |
| now = avail; |
| if (now > length) { |
| now = length; |
| } |
| memcpy((void *) buffer, (const void *) (ctx->send_buf.base + ctx->send_buf.read_offset), now); |
| duk__trans_buffer_rebase(&ctx->send_buf); |
| ctx->send_buf.read_offset += now; |
| return now; |
| } |
| } |
| } |
| |
| duk_size_t duk_trans_dvalue_write_cb(void *udata, const char *buffer, duk_size_t length) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| unsigned char *p; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p %p %ld\n", __func__, udata, (void *) buffer, (long) length); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_cooperate(ctx, 0); |
| |
| /* Append data. */ |
| duk__trans_buffer_rebase(&ctx->recv_buf); |
| p = duk__trans_buffer_ensure(&ctx->recv_buf, length); |
| memcpy((void *) p, (const void *) buffer, (size_t) length); |
| ctx->recv_buf.write_offset += length; |
| |
| /* Trial parse handshake line or dvalue(s). */ |
| if (!ctx->handshake_done) { |
| duk_dvalue *dv = duk__trans_trial_parse_handshake(ctx); |
| if (dv) { |
| /* Handshake line is available for caller for the |
| * duration of the callback, and must not be freed |
| * by the caller. |
| */ |
| duk__trans_call_handshake(ctx, (const char *) dv->buf); |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: handshake ok\n", __func__); |
| fflush(stderr); |
| #endif |
| duk_dvalue_free(dv); |
| ctx->handshake_done = 1; |
| } |
| } |
| if (ctx->handshake_done) { |
| for (;;) { |
| duk_dvalue *dv = duk__trans_trial_parse_dvalue(ctx); |
| if (dv) { |
| #if defined(DEBUG_PRINTS) |
| { |
| char buf[DUK_DVALUE_TOSTRING_BUFLEN]; |
| duk_dvalue_to_string(dv, buf); |
| fprintf(stderr, "%s: received dvalue: %s\n", __func__, buf); |
| fflush(stderr); |
| } |
| #endif |
| |
| duk__trans_call_received(ctx, dv); |
| } else { |
| break; |
| } |
| } |
| } |
| |
| duk__trans_call_cooperate(ctx, 0); /* just in case, if dvalues changed something */ |
| |
| return length; |
| } |
| |
| duk_size_t duk_trans_dvalue_peek_cb(void *udata) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| size_t avail; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p\n", __func__, udata); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_cooperate(ctx, 0); |
| avail = (size_t) (ctx->send_buf.write_offset - ctx->send_buf.read_offset); |
| return (duk_size_t) avail; |
| } |
| |
| void duk_trans_dvalue_read_flush_cb(void *udata) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p\n", __func__, udata); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_cooperate(ctx, 0); |
| } |
| |
| void duk_trans_dvalue_write_flush_cb(void *udata) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p\n", __func__, udata); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_cooperate(ctx, 0); |
| } |
| |
| void duk_trans_dvalue_detached_cb(void *udata) { |
| duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; |
| |
| #if defined(DEBUG_PRINTS) |
| fprintf(stderr, "%s: %p\n", __func__, udata); |
| fflush(stderr); |
| #endif |
| |
| duk__trans_call_detached(ctx); |
| } |