goto fail;
}
- ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen);
+ ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen, 0);
fail:
outlen += taglen;
}
- ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen);
+ ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen, 0);
fail:
done:
- ret = njs_vm_value_array_buffer_set(vm, retval, dst, len);
+ ret = njs_vm_value_array_buffer_set(vm, retval, dst, len, 0);
fail:
olen += olen2;
- ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen);
+ ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen, 0);
fail:
goto fail;
}
- ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen);
+ ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen, 0);
fail:
dkey, 0);
} else {
ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&lvalue), k,
- length);
+ length, 0);
}
if (njs_slow_path(ret != NJS_OK)) {
dkey, 0);
} else {
ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&lvalue), k,
- length);
+ length, 0);
}
if (njs_slow_path(ret != NJS_OK)) {
goto fail;
}
- ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst, olen);
+ ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst, olen, 0);
if (njs_slow_path(ret != NJS_OK)) {
goto fail;
}
return NJS_ERROR;
}
- return njs_vm_value_array_buffer_set(vm, retval, dst, size);
+ return njs_vm_value_array_buffer_set(vm, retval, dst, size, 0);
}
if (!verify) {
ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst,
- outlen);
+ outlen, 0);
if (njs_slow_path(ret != NJS_OK)) {
goto fail;
}
#endif
return njs_vm_value_array_buffer_set(vm, retval, key->u.s.raw.start,
- key->u.s.raw.length);
+ key->u.s.raw.length, 1);
}
return NJS_ERROR;
memcpy(dst, start, length);
- return njs_vm_value_array_buffer_set(vm, retval, dst, length);
+ return njs_vm_value_array_buffer_set(vm, retval, dst, length, 0);
}
case NGX_JS_BODY_ARRAY_BUFFER:
return njs_vm_value_array_buffer_set(vm, retval,
ctx->body_read_data,
- ctx->body_read_len);
+ ctx->body_read_len, 1);
case NGX_JS_BODY_JSON:
ret = njs_vm_value_string_create(vm, njs_value_arg(&arg),
case NGX_JS_BODY_ARRAY_BUFFER:
ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result),
request->body.data,
- request->body.len);
+ request->body.len, 1);
if (ret != NJS_OK) {
njs_vm_memory_error(vm);
return NJS_ERROR;
switch (type) {
case NGX_JS_BODY_ARRAY_BUFFER:
ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result),
- string.start, string.length);
+ string.start, string.length, 0);
if (ret != NJS_OK) {
njs_vm_memory_error(vm);
return NJS_ERROR;
NJS_EXPORT njs_int_t njs_vm_string_compare(njs_vm_t *vm, const njs_value_t *v1,
const njs_value_t *v2);
+/*
+ * Sets an ArrayBuffer value without copying data or taking ownership.
+ * Shared data is copied on the first write; otherwise it must be writable.
+ * The caller must keep the data alive while the buffer refers to it.
+ */
NJS_EXPORT njs_int_t njs_vm_value_array_buffer_set(njs_vm_t *vm,
- njs_value_t *value, const u_char *start, uint32_t size);
+ njs_value_t *value, const u_char *start, uint32_t size, njs_bool_t shared);
NJS_EXPORT njs_int_t njs_value_buffer_get(njs_vm_t *vm, njs_value_t *value,
njs_str_t *dst);
njs_int_t
njs_vm_value_array_buffer_set(njs_vm_t *vm, njs_value_t *value,
- const u_char *start, uint32_t size)
+ const u_char *start, uint32_t size, njs_bool_t shared)
{
njs_array_buffer_t *array;
return NJS_ERROR;
}
+ array->object.shared = shared;
array->u.data = (u_char *) start;
array->size = size;
}
+static njs_int_t
+njs_unit_test_r_array_buffer(njs_vm_t *vm, njs_object_prop_t *prop,
+ uint32_t unused, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval)
+{
+ return njs_vm_value_array_buffer_set(vm, retval,
+ (u_char *) "АБВГДЕЁЖЗИЙ", 22, 1);
+}
+
+
static njs_int_t
njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
}
},
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("arrayBuffer"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = njs_unit_test_r_array_buffer,
+ }
+ },
+
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("method"),
"[b[0], $r.buffer[0]]"),
njs_str("64,208") },
+ { njs_str("var b = new Uint8Array($r.arrayBuffer);"
+ "b[0] = '@'.codePointAt(0);"
+ "[b[0], new Uint8Array($r.arrayBuffer)[0]]"),
+ njs_str("64,208") },
+
{ njs_str("$r.buffer instanceof Buffer"),
njs_str("true") },
}
+static njs_int_t
+njs_vm_array_buffer_test(njs_vm_t *unused, njs_opts_t *opts, njs_stat_t *stat)
+{
+ u_char *start, *data[2];
+ njs_vm_t *vm;
+ njs_int_t ret;
+ njs_uint_t i;
+ njs_str_t bytes;
+ njs_vm_opt_t options;
+ njs_opaque_value_t buffers[2], retval;
+
+ static const njs_str_t names[] = {
+ njs_str("privateBuffer"), njs_str("sharedBuffer")
+ };
+
+ static const njs_str_t script = njs_str(
+ "new Uint8Array(privateBuffer)[0] = 88;"
+ "new DataView(sharedBuffer).setUint8(0, 88);");
+
+ njs_vm_opt_init(&options);
+ options.init = 1;
+
+ vm = njs_vm_create(&options);
+ if (vm == NULL) {
+ return NJS_ERROR;
+ }
+
+ ret = NJS_ERROR;
+
+ for (i = 0; i < njs_nitems(buffers); i++) {
+ data[i] = njs_mp_alloc(njs_vm_memory_pool(vm), 2);
+ if (data[i] == NULL) {
+ ret = NJS_ERROR;
+ goto done;
+ }
+
+ memcpy(data[i], "ab", 2);
+
+ ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&buffers[i]),
+ data[i], 2, i);
+ if (ret != NJS_OK) {
+ goto done;
+ }
+
+ ret = njs_vm_bind(vm, &names[i], njs_value_arg(&buffers[i]), 0);
+ if (ret != NJS_OK) {
+ goto done;
+ }
+ }
+
+ start = script.start;
+ ret = njs_vm_compile(vm, &start, start + script.length);
+ if (ret != NJS_OK) {
+ goto done;
+ }
+
+ ret = njs_vm_start(vm, njs_value_arg(&retval));
+ if (ret != NJS_OK) {
+ goto done;
+ }
+
+ for (i = 0; i < njs_nitems(buffers); i++) {
+ ret = njs_vm_value_to_bytes(vm, &bytes, njs_value_arg(&buffers[i]));
+ if (ret != NJS_OK) {
+ goto done;
+ }
+
+ if (bytes.length != 2 || bytes.start[0] != 88 || bytes.start[1] != 'b'
+ || (bytes.start != data[i]) != (i != 0)
+ || data[i][0] != ((i == 0) ? 88 : 'a'))
+ {
+ njs_printf("ArrayBuffer shared=%ui: unexpected backing data\n", i);
+ ret = NJS_ERROR;
+ goto done;
+ }
+ }
+
+done:
+
+ njs_vm_destroy(vm);
+
+ if (ret == NJS_OK) {
+ stat->passed++;
+
+ } else {
+ stat->failed++;
+ }
+
+ return NJS_OK;
+}
+
+
static njs_int_t
njs_chb_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat)
{
} tests[] = {
{ njs_vm_object_alloc_test,
njs_str("njs_vm_object_alloc_test") },
+ { njs_vm_array_buffer_test,
+ njs_str("njs_vm_array_buffer_test") },
{ njs_chb_test,
njs_str("njs_chb_test") },
{ njs_sort_test,