mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
10 years ago
5 changed files with 604 additions and 20 deletions
@ -0,0 +1,151 @@ |
|||
/*
|
|||
* Examples of working with buffer objects from C code. |
|||
*/ |
|||
|
|||
/*
|
|||
* Example 1: Buffer objects are created by Ecmascript code and then handed |
|||
* over to a Duktape/C function which accesses the raw bytes from C code. |
|||
*/ |
|||
|
|||
/*===
|
|||
*** test_1 (duk_safe_call) |
|||
image size: 12x13, width_bytes: 2 |
|||
...***...... |
|||
...***...... |
|||
...***...... |
|||
..........*. |
|||
*****.**..*. |
|||
*****.***.*. |
|||
*****.*..*** |
|||
.***.*....*. |
|||
..*.**...... |
|||
...*.**..... |
|||
.**...**.... |
|||
.**...**.... |
|||
***...***... |
|||
final top: 0 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
static duk_ret_t draw_pixels(duk_context *ctx) { |
|||
unsigned char *ptr; |
|||
duk_size_t sz; |
|||
unsigned char *p; |
|||
long width, height; |
|||
long width_bytes; |
|||
long i, j; |
|||
|
|||
/* Get data area of buffer argument (plain buffer or any
|
|||
* duk_hbufferobject). |
|||
* |
|||
* The returned pointer is stable if the underlying buffer is a |
|||
* fixed buffer (this is always the case when a buffer object is |
|||
* created from Ecmascript code e.g. as "new ArrayBuffer()"). |
|||
* For dynamic and external buffers the pointer is stable unless |
|||
* the buffer is resized or reconfigured. Caller is responsible |
|||
* for avoiding the use of stale pointers in such cases. When in |
|||
* doubt, relookup the pointer / length right before accessing. |
|||
* |
|||
* The duk_{get,require}_buffer_data() calls take into account |
|||
* "slices" so that the returned ptr/size is always to the active |
|||
* slice as one would expect compared to how buffers behave in |
|||
* Ecmascript code. |
|||
*/ |
|||
ptr = duk_require_buffer_data(ctx, 0, &sz); |
|||
|
|||
/* Get width and height. Buffer contains a 1-bit pixel image,
|
|||
* with rows starting from top represented with ceil(width / 8) |
|||
* bytes. |
|||
*/ |
|||
width = (long) duk_require_int(ctx, 1); |
|||
height = (long) duk_require_int(ctx, 2); |
|||
width_bytes = (width + 7) / 8; |
|||
printf("image size: %ldx%ld, width_bytes: %ld\n", |
|||
(long) width, (long) height, (long) width_bytes); |
|||
|
|||
/* Caller must ensure it never accesses beyond the allowed buffer
|
|||
* range which is [0, sz[. This is critical for memory safety. |
|||
*/ |
|||
if (sz < width_bytes * height) { |
|||
return DUK_RET_RANGE_ERROR; |
|||
} |
|||
|
|||
/* Print pixels. Actual user code could draw pixels to screen here. */ |
|||
p = ptr - 1; /* dec by one, inner loop advances on first round */ |
|||
for (i = 0; i < height; i++) { |
|||
for (j = 0; j < width; j++) { |
|||
unsigned char mask = 1 << (7 - (j & 0x07)); |
|||
if ((j & 0x07) == 0) { |
|||
p++; |
|||
} |
|||
if (*p & mask) { |
|||
printf("*"); |
|||
} else { |
|||
printf("."); |
|||
} |
|||
} |
|||
printf("\n"); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t test_1(duk_context *ctx) { |
|||
duk_push_c_function(ctx, draw_pixels, 3 /*nargs*/); |
|||
duk_put_global_string(ctx, "drawPixels"); |
|||
|
|||
/*
|
|||
...***.. .... |
|||
...***.. .... 13 rows |
|||
...***.. .... 12 pixels/row (2 bytes) |
|||
........ ..*. |
|||
*****.** ..*. |
|||
*****.** *.*. |
|||
*****.*. .*** |
|||
.***.*.. ..*. |
|||
..*.**.. .... |
|||
...*.**. .... |
|||
.**...** .... |
|||
.**...** .... |
|||
***...** *... |
|||
*/ |
|||
|
|||
duk_eval_string_noresult(ctx, |
|||
"(function () {\n" |
|||
" var buf = new Buffer(13 * 2);\n" |
|||
" buf[0] = 0x1c; buf[1] = 0x00;\n" |
|||
" buf[2] = 0x1c; buf[3] = 0x00;\n" |
|||
" buf[4] = 0x1c; buf[5] = 0x00;\n" |
|||
" buf[6] = 0x00; buf[7] = 0x20;\n" |
|||
" buf[8] = 0xfb; buf[9] = 0x20;\n" |
|||
" buf[10] = 0xfb; buf[11] = 0xa0;\n" |
|||
" buf[12] = 0xfa; buf[13] = 0x70;\n" |
|||
" buf[14] = 0x74; buf[15] = 0x20;\n" |
|||
" buf[16] = 0x2c; buf[17] = 0x00;\n" |
|||
" buf[18] = 0x16; buf[19] = 0x00;\n" |
|||
" buf[20] = 0x63; buf[21] = 0x00;\n" |
|||
" buf[22] = 0x63; buf[23] = 0x00;\n" |
|||
" buf[24] = 0xe3; buf[25] = 0x80;\n" |
|||
" drawPixels(buf, 12 /*width*/, 13 /*height*/);\n" |
|||
"\n" |
|||
"})()\n" |
|||
); |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
/*
|
|||
* Example 2: buffer object is created from C code and used in |
|||
* Ecmascript code. |
|||
*/ |
|||
|
|||
/* XXX: no API yet */ |
|||
|
|||
/*
|
|||
* Main |
|||
*/ |
|||
|
|||
void test(duk_context *ctx) { |
|||
TEST_SAFE_CALL(test_1); |
|||
} |
@ -0,0 +1,231 @@ |
|||
/*===
|
|||
*** test_basic (duk_safe_call) |
|||
top: 18 |
|||
index 0: length 0 |
|||
index 1: length 0 |
|||
index 2: length 0 |
|||
index 3: length 0 |
|||
index 4: length 0 |
|||
index 5: length 0 |
|||
index 6: length 0 |
|||
index 7: length 0 |
|||
index 8: length 0 |
|||
index 9: length 1024, ptr-is-NULL 0 |
|||
index 10: length 0 |
|||
index 11: length 2048, ptr-is-NULL 0 |
|||
index 12: length 16, ptr-is-NULL 0 |
|||
index 13: length 64, ptr-is-NULL 0 |
|||
index 14: length 16, ptr-is-NULL 0 |
|||
index 15: length 12, ptr-is-NULL 0 |
|||
index 16: length 8, ptr-is-NULL 0 |
|||
index 17: length 3, ptr-is-NULL 0 |
|||
final top: 18 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
static duk_ret_t test_basic(duk_context *ctx) { |
|||
duk_idx_t i, n; |
|||
|
|||
duk_push_undefined(ctx); |
|||
duk_push_null(ctx); |
|||
duk_push_true(ctx); |
|||
duk_push_false(ctx); |
|||
duk_push_string(ctx, ""); |
|||
duk_push_string(ctx, "foo"); |
|||
duk_push_int(ctx, 123); |
|||
duk_push_object(ctx); |
|||
duk_push_fixed_buffer(ctx, 0); |
|||
duk_push_fixed_buffer(ctx, 1024); |
|||
duk_push_dynamic_buffer(ctx, 0); |
|||
duk_push_dynamic_buffer(ctx, 2048); |
|||
duk_eval_string(ctx, "(function () { return new ArrayBuffer(16); })()"); |
|||
duk_eval_string(ctx, "(function () { return new Uint32Array(16); })()"); |
|||
duk_eval_string(ctx, "(function () { return new DataView(new ArrayBuffer(16)); })()"); |
|||
duk_eval_string(ctx, "(function () { return new Uint32Array(16).subarray(3, 6); })()"); |
|||
duk_eval_string(ctx, "(function () { return new Buffer('ABCDEFGH'); })()"); |
|||
duk_eval_string(ctx, "(function () { return new Buffer('ABCDEFGH').slice(3, 6); })()"); |
|||
|
|||
n = duk_get_top(ctx); |
|||
printf("top: %ld\n", (long) n); |
|||
for (i = 0; i < n; i++) { |
|||
void *buf; |
|||
duk_size_t len; |
|||
|
|||
len = (duk_size_t) 0xdeadbeefUL; |
|||
buf = duk_get_buffer_data(ctx, i, &len); |
|||
if (len == 0) { |
|||
/* avoid printing 'buf' if len is zero, as it is not predictable */ |
|||
printf("index %ld: length 0\n", (long) i); |
|||
} else { |
|||
printf("index %ld: length %lu, ptr-is-NULL %d\n", |
|||
(long) i, (unsigned long) len, (buf == NULL ? 1 : 0)); |
|||
} |
|||
} |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
/*===
|
|||
*** test_null_ptr (duk_safe_call) |
|||
p is not NULL |
|||
final top: 1 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
static duk_ret_t test_null_ptr(duk_context *ctx) { |
|||
void *p; |
|||
|
|||
duk_eval_string(ctx, |
|||
"(function () {\n" |
|||
" var b = new ArrayBuffer(16);\n" |
|||
" var v = new Uint32Array(b);\n" |
|||
" v.set([ 0x11111111, 0x22222222, 0x33333333, 0x44444444 ]);\n" |
|||
" return v.subarray(1, 3);\n" |
|||
"})()"); |
|||
|
|||
p = duk_get_buffer_data(ctx, -1, NULL); |
|||
if (p) { |
|||
printf("p is not NULL\n"); |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
/*===
|
|||
*** test_slice (duk_safe_call) |
|||
p is not NULL, sz=8 |
|||
p[0] = 0x22 |
|||
p[1] = 0x22 |
|||
p[2] = 0x22 |
|||
p[3] = 0x22 |
|||
p[4] = 0x33 |
|||
p[5] = 0x33 |
|||
p[6] = 0x33 |
|||
p[7] = 0x33 |
|||
final top: 1 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
/* Test that slice views work, i.e. returned buffer pointer
|
|||
* is correctly offsetted. Avoid endianness by using initializers |
|||
* which have the same memory representation in either case. |
|||
*/ |
|||
static duk_ret_t test_slice(duk_context *ctx) { |
|||
unsigned char *p; |
|||
duk_size_t sz; |
|||
duk_size_t i; |
|||
|
|||
duk_eval_string(ctx, |
|||
"(function () {\n" |
|||
" var b = new ArrayBuffer(16);\n" |
|||
" var v = new Uint32Array(b);\n" |
|||
" v.set([ 0x11111111, 0x22222222, 0x33333333, 0x44444444 ]);\n" |
|||
" return v.subarray(1, 3);\n" |
|||
"})()"); |
|||
|
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
p = duk_get_buffer_data(ctx, -1, &sz); |
|||
if (p) { |
|||
printf("p is not NULL, sz=%ld\n", (long) sz); |
|||
for (i = 0; i < sz; i++) { |
|||
printf("p[%ld] = 0x%02x\n", (long) i, (unsigned int) p[i]); |
|||
} |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
/*===
|
|||
*** test_uncovered_buffer (duk_safe_call) |
|||
p is not NULL, sz=16 |
|||
p is not NULL, sz=16 |
|||
p is NULL |
|||
final top: 2 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
/* Underlying buffer doesn't cover logical slice. */ |
|||
static duk_ret_t test_uncovered_buffer(duk_context *ctx) { |
|||
unsigned char *p; |
|||
duk_size_t sz; |
|||
|
|||
duk_push_dynamic_buffer(ctx, 64); /* 16x4 elements */ |
|||
|
|||
duk_eval_string(ctx, |
|||
"(function (plain_buffer) {\n" |
|||
" var b = new ArrayBuffer(plain_buffer);\n" |
|||
" return new Uint32Array(b).subarray(1,5);\n" |
|||
"})"); |
|||
duk_dup(ctx, 0); |
|||
duk_call(ctx, 1); /* -> [ plain_buffer Uint32Array ] */ |
|||
|
|||
/* Initially OK. */ |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
p = duk_get_buffer_data(ctx, -1, &sz); |
|||
if (p) { |
|||
printf("p is not NULL, sz=%ld\n", (long) sz); |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
/* Resize; slice still covered. */ |
|||
duk_resize_buffer(ctx, 0, 20); /* 5x4 = 20, subarray is [1*4, 5*4[ */ |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
p = duk_get_buffer_data(ctx, -1, &sz); |
|||
if (p) { |
|||
printf("p is not NULL, sz=%ld\n", (long) sz); |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
/* Resize; no longer covered. */ |
|||
duk_resize_buffer(ctx, 0, 19); |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
p = duk_get_buffer_data(ctx, -1, &sz); |
|||
if (p) { |
|||
printf("p is not NULL, sz=%ld\n", (long) sz); |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
/*===
|
|||
*** test_invalid_index (duk_safe_call) |
|||
p is NULL |
|||
final top: 0 |
|||
==> rc=0, result='undefined' |
|||
===*/ |
|||
|
|||
static duk_ret_t test_invalid_index(duk_context *ctx) { |
|||
unsigned char *p; |
|||
duk_size_t sz; |
|||
|
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
p = duk_get_buffer_data(ctx, -1, &sz); |
|||
if (p) { |
|||
printf("p is not NULL, sz=%ld\n", (long) sz); |
|||
} else { |
|||
printf("p is NULL\n"); |
|||
} |
|||
|
|||
printf("final top: %ld\n", (long) duk_get_top(ctx)); |
|||
return 0; |
|||
} |
|||
|
|||
void test(duk_context *ctx) { |
|||
TEST_SAFE_CALL(test_basic); |
|||
TEST_SAFE_CALL(test_null_ptr); |
|||
TEST_SAFE_CALL(test_slice); |
|||
TEST_SAFE_CALL(test_uncovered_buffer); |
|||
TEST_SAFE_CALL(test_invalid_index); |
|||
} |
@ -0,0 +1,88 @@ |
|||
/*===
|
|||
*** test_1 (duk_safe_call) |
|||
buffer: ptr-is-NULL=0, sz=1024 |
|||
buffer |
|||
buffer: ptr-is-NULL=-1, sz=0 |
|||
buffer |
|||
buffer: ptr-is-NULL=0, sz=1024 |
|||
buffer |
|||
buffer: ptr-is-NULL=-1, sz=0 |
|||
buffer |
|||
buffer: ptr-is-NULL=0, sz=12 |
|||
buffer |
|||
==> rc=0, result='undefined' |
|||
*** test_2 (duk_safe_call) |
|||
==> rc=1, result='TypeError: not buffer' |
|||
*** test_3 (duk_safe_call) |
|||
==> rc=1, result='TypeError: not buffer' |
|||
*** test_4 (duk_safe_call) |
|||
==> rc=1, result='TypeError: not buffer' |
|||
===*/ |
|||
|
|||
static duk_ret_t test_1(duk_context *ctx) { |
|||
void *ptr; |
|||
duk_size_t sz; |
|||
int i; |
|||
|
|||
duk_set_top(ctx, 0); |
|||
duk_push_fixed_buffer(ctx, 1024); |
|||
duk_push_fixed_buffer(ctx, 0); |
|||
duk_push_dynamic_buffer(ctx, 1024); |
|||
duk_push_dynamic_buffer(ctx, 0); |
|||
duk_eval_string(ctx, "(function () { return new Uint32Array(16).subarray(3, 6); })()"); |
|||
|
|||
for (i = 0; i < 5; i++) { |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
ptr = duk_require_buffer_data(ctx, i, &sz); |
|||
printf("buffer: ptr-is-NULL=%d, sz=%ld\n", |
|||
(sz == 0 ? -1 : (ptr == NULL ? 1 : 0)), (long) sz); |
|||
|
|||
/* NULL pointer */ |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
ptr = duk_require_buffer_data(ctx, i, NULL); |
|||
printf("buffer\n"); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t test_2(duk_context *ctx) { |
|||
void *ptr; |
|||
duk_size_t sz; |
|||
|
|||
duk_set_top(ctx, 0); |
|||
duk_push_null(ctx); |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
ptr = duk_require_buffer_data(ctx, 0, &sz); |
|||
printf("buffer ok: %p\n", ptr); /* ok to print, should not be reached */ |
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t test_3(duk_context *ctx) { |
|||
void *ptr; |
|||
duk_size_t sz; |
|||
|
|||
duk_set_top(ctx, 0); |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
ptr = duk_require_buffer_data(ctx, 0, &sz); |
|||
printf("buffer ok: %p\n", ptr); /* ok to print, should not be reached */ |
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t test_4(duk_context *ctx) { |
|||
void *ptr; |
|||
duk_size_t sz; |
|||
|
|||
duk_set_top(ctx, 0); |
|||
sz = (duk_size_t) 0xdeadbeefUL; |
|||
ptr = duk_require_buffer_data(ctx, DUK_INVALID_INDEX, &sz); |
|||
printf("buffer ok: %p\n", ptr); /* ok to print, should not be reached */ |
|||
return 0; |
|||
} |
|||
|
|||
void test(duk_context *ctx) { |
|||
TEST_SAFE_CALL(test_1); |
|||
TEST_SAFE_CALL(test_2); |
|||
TEST_SAFE_CALL(test_3); |
|||
TEST_SAFE_CALL(test_4); |
|||
} |
Loading…
Reference in new issue