Browse Source

Buffer API testcase additions and fixes

pull/222/head
Sami Vaarala 10 years ago
parent
commit
21b538782b
  1. 151
      api-testcases/test-bufferobject-example-1.c
  2. 231
      api-testcases/test-get-buffer-data.c
  3. 102
      api-testcases/test-get-buffer.c
  4. 88
      api-testcases/test-require-buffer-data.c
  5. 52
      api-testcases/test-require-buffer.c

151
api-testcases/test-bufferobject-example-1.c

@ -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);
}

231
api-testcases/test-get-buffer-data.c

@ -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);
}

102
api-testcases/test-get-buffer.c

@ -1,5 +1,6 @@
/*===
top: 12
*** test_basic (duk_safe_call)
top: 18
index 0: length 0
index 1: length 0
index 2: length 0
@ -12,9 +13,17 @@ 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 0
index 13: length 0
index 14: length 0
index 15: length 0
index 16: length 0
index 17: length 0
final top: 18
==> rc=0, result='undefined'
===*/
void test(duk_context *ctx) {
static duk_ret_t test_basic(duk_context *ctx) {
duk_idx_t i, n;
duk_push_undefined(ctx);
@ -29,6 +38,12 @@ void test(duk_context *ctx) {
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);
@ -36,7 +51,7 @@ void test(duk_context *ctx) {
void *buf;
duk_size_t len;
len = (duk_size_t) 0xdeadbeef;
len = (duk_size_t) 0xdeadbeefUL;
buf = duk_get_buffer(ctx, i, &len);
if (len == 0) {
/* avoid printing 'buf' if len is zero, as it is not predictable */
@ -46,4 +61,85 @@ void test(duk_context *ctx) {
(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_push_fixed_buffer(ctx, 1024);
p = duk_get_buffer(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_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) {
void *p;
duk_size_t sz;
sz = (duk_size_t) 0xdeadbeefUL;
p = duk_get_buffer(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_buffer_object (duk_safe_call)
p is NULL
==> rc=0, result='undefined'
===*/
static duk_ret_t test_buffer_object(duk_context *ctx) {
void *p;
duk_size_t sz;
/* duk_get_buffer_data() doesn't accept a buffer object */
duk_set_top(ctx, 0);
duk_eval_string(ctx, "new ArrayBuffer(16)");
sz = (duk_size_t) 0xdeadbeefUL;
p = duk_get_buffer(ctx, -1, &sz);
if (p) {
printf("p is not NULL, sz=%ld\n", (long) sz);
} else {
printf("p is NULL\n");
}
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_basic);
TEST_SAFE_CALL(test_null_ptr);
TEST_SAFE_CALL(test_invalid_index);
TEST_SAFE_CALL(test_buffer_object);
}

88
api-testcases/test-require-buffer-data.c

@ -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);
}

52
api-testcases/test-require-buffer.c

@ -1,5 +1,5 @@
/*===
*** test_1 (duk_safe_call)
*** test_basic (duk_safe_call)
buffer: ptr-is-NULL=0, sz=1024
buffer
buffer: ptr-is-NULL=-1, sz=0
@ -9,15 +9,17 @@ buffer
buffer: ptr-is-NULL=-1, sz=0
buffer
==> rc=0, result='undefined'
*** test_2 (duk_safe_call)
*** test_invalid_type (duk_safe_call)
==> rc=1, result='TypeError: not buffer'
*** test_3 (duk_safe_call)
*** test_invalid_index1 (duk_safe_call)
==> rc=1, result='TypeError: not buffer'
*** test_4 (duk_safe_call)
*** test_invalid_index2 (duk_safe_call)
==> rc=1, result='TypeError: not buffer'
*** test_buffer_object (duk_safe_call)
==> rc=1, result='TypeError: not buffer'
===*/
static duk_ret_t test_1(duk_context *ctx) {
static duk_ret_t test_basic(duk_context *ctx) {
void *ptr;
duk_size_t sz;
int i;
@ -29,12 +31,13 @@ static duk_ret_t test_1(duk_context *ctx) {
duk_push_dynamic_buffer(ctx, 0);
for (i = 0; i < 4; i++) {
sz = (duk_size_t) 0xdeadbeef;
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(ctx, i, &sz);
printf("buffer: ptr-is-NULL=%d, sz=%ld\n",
(sz == 0 ? -1 : (ptr == NULL ? 1 : 0)), (long) sz);
sz = (duk_size_t) 0xdeadbeef;
/* NULL pointer */
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(ctx, i, NULL);
printf("buffer\n");
}
@ -42,43 +45,58 @@ static duk_ret_t test_1(duk_context *ctx) {
return 0;
}
static duk_ret_t test_2(duk_context *ctx) {
static duk_ret_t test_invalid_type(duk_context *ctx) {
void *ptr;
duk_size_t sz;
duk_set_top(ctx, 0);
duk_push_null(ctx);
sz = (duk_size_t) 0xdeadbeef;
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(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) {
static duk_ret_t test_invalid_index1(duk_context *ctx) {
void *ptr;
duk_size_t sz;
duk_set_top(ctx, 0);
sz = (duk_size_t) 0xdeadbeef;
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(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) {
static duk_ret_t test_invalid_index2(duk_context *ctx) {
void *ptr;
duk_size_t sz;
duk_set_top(ctx, 0);
sz = (duk_size_t) 0xdeadbeef;
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(ctx, DUK_INVALID_INDEX, &sz);
printf("buffer ok: %p\n", ptr); /* ok to print, should not be reached */
return 0;
}
static duk_ret_t test_buffer_object(duk_context *ctx) {
void *ptr;
duk_size_t sz;
/* duk_require_buffer() doesn't accept a buffer object */
duk_set_top(ctx, 0);
duk_eval_string(ctx, "new ArrayBuffer(16)");
sz = (duk_size_t) 0xdeadbeefUL;
ptr = duk_require_buffer(ctx, -1, &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);
TEST_SAFE_CALL(test_basic);
TEST_SAFE_CALL(test_invalid_type);
TEST_SAFE_CALL(test_invalid_index1);
TEST_SAFE_CALL(test_invalid_index2);
TEST_SAFE_CALL(test_buffer_object);
}

Loading…
Cancel
Save