Introduce the Array Buffer C API (#2161)

Add C API to work with Array Buffers.
The following methods are added:
- jerry_value_is_arraybuffer
- jerry_create_arraybuffer
- jerry_arraybuffer_write
- jerry_arraybuffer_read
- jerry_get_arraybuffer_byte_length

JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com
This commit is contained in:
Péter Gál
2018-01-11 20:36:08 +01:00
committed by László Langó
parent b9560b7c70
commit ded0d5a846
4 changed files with 693 additions and 0 deletions
+242
View File
@@ -993,6 +993,44 @@ jerry_value_is_array (const jerry_value_t value)
- [jerry_release_value](#jerry_release_value)
## jerry_value_is_arraybuffer
**Summary**
Returns whether the given `jerry_value_t` is an ArrayBuffer object.
**Prototype**
```c
bool
jerry_value_is_arraybuffer (const jerry_value_t value)
```
- `value` - api value
- return value
- true, if the given `jerry_value_t` is an ArrayBuffer object.
- false, otherwise
**Example**
```c
{
jerry_value_t value;
... // create or acquire value
if (jerry_value_is_arraybuffer (value))
{
...
}
jerry_release_value (value);
}
```
**See also**
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
## jerry_value_is_boolean
@@ -2391,6 +2429,42 @@ jerry_create_array (uint32_t size);
- [jerry_get_property_by_index](#jerry_get_property_by_index)
## jerry_create_arraybuffer
**Summary**
Create a jerry_value_t representing an ArrayBuffer object.
**Prototype**
```c
jerry_value_t
jerry_create_arraybuffer (jerry_length_t size);
```
- `size` - size of the ArrayBuffer to create **in bytes**
- return value - the new ArrayBuffer as a `jerry_value_t`
**Example**
```c
{
jerry_value_t buffer_value = jerry_create_arraybuffer (15);
... // use the ArrayBuffer
jerry_release_value (buffer_value);
}
```
**See also**
- [jerry_arraybuffer_read](#jerry_arraybuffer_read)
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
- [jerry_value_is_arraybuffer](#jerry_value_is_arraybuffer)
- [jerry_release_value](#jerry_release_value)
## jerry_create_boolean
**Summary**
@@ -4679,3 +4753,171 @@ main (void)
- [jerry_parse](#jerry_parse)
- [jerry_run](#jerry_run)
- [jerry_vm_exec_stop_callback_t](#jerry_vm_exec_stop_callback_t)
# ArrayBuffer and TypedArray functions
## jerry_get_arraybuffer_byte_length
**Summary**
Get the byte length property of the ArrayBuffer. This is the
same value which was passed to the ArrayBuffer constructor call.
**Prototype**
```c
jerry_length_t
jerry_get_arraybuffer_byte_length (const jerry_value_t value);
```
- `value` - ArrayBuffer object
- return value
- size of the ArrayBuffer in bytes
- 0 if the `value` parameter is not an ArrayBuffer
**Example**
```c
{
jerry_value_t buffer = jerry_create_arraybuffer (15);
jerry_length_t length = jerry_get_arraybuffer_byte_length (buffer);
// length should be 15
jerry_release_value (buffer);
}
```
**See also**
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
## jerry_arraybuffer_read
**Summary**
Copy the portion of the ArrayBuffer into a user provided buffer.
The start offset of the read operation can be specified.
The number bytes to be read can be specified via the `buf_size`
parameter. It is not possible to read more than the length of
the ArrayBuffer.
Function returns the number of bytes read from the ArrayBuffer
(and written to the buffer parameter). This value is
calculated in the following way: `min(array buffer length - offset, buf_size)`.
**Prototype**
```c
jerry_length_t
jerry_arraybuffer_read (const jerry_value_t value,
jerry_length_t offset,
uint8_t *buf_p,
jerry_length_t buf_size);
```
- `value` - ArrayBuffer to read from
- `offset` - start offset of the read operation
- `buf_p` - buffer to read the data to
- `buf_size` - maximum number of bytes to read into the buffer
- return value
- number of bytes written into the buffer (read from the ArrayBuffer)
- 0 if the `value` is not an ArrayBuffer object
- 0 if the `buf_size` is zero or there is nothing to read
**Example**
```c
{
uint8_t data[20];
jerry_value_t buffer;
// ... create the ArrayBuffer or acuiqre it from somewhere.
jerry_value_t bytes_read;
// read 10 bytes from the start of the ArrayBuffer.
bytes_read = jerry_arraybuffer_read (buffer, 0, data, 10);
// read the next 10 bytes
bytes_read += jerry_arraybuffer_read (buffer, bytes_read, data + bytes_read, 10);
// process the data variable
jerry_release_value (buffer);
}
```
**See also**
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
- [jerry_get_arraybuffer_byte_length](#jerry_get_arraybuffer_byte_length)
## jerry_arraybuffer_write
**Summary**
Copy the contents of a buffer into the ArrayBuffer.
The start offset of the write operation can be specified.
The number bytes to be written can be specified via the `buf_size`
parameter. It is not possible to write more than the length of
the ArrayBuffer.
Function returns the number of bytes written into the ArrayBuffer
(and read from the buffer parameter). This value is
calculated in the following way: `min(array buffer length - offset, buf_size)`.
**Prototype**
```c
jerry_length_t
jerry_arraybuffer_write (const jerry_value_t value,
jerry_length_t offset,
const uint8_t *buf_p,
jerry_length_t buf_size);
```
- `value` - ArrayBuffer to write to
- `offset` - start offset of the write operation
- `buf_p` - buffer to read the data from
- `buf_size` - maximum number of bytes to write into the ArrayBuffer
- return value
- number of bytes written into the ArrayBuffer (read from the buffer parameter)
- 0 if the `value` is not an ArrayBuffer object
- 0 if the `buf_size` is zero or there is nothing to write
**Example**
```c
{
uint8_t data[20];
// fill the data with values
for (int i = 0; i < 20; i++)
{
data[i] = (uint8_t) (i * 2);
}
jerry_value_t buffer;
// ... create the ArrayBuffer or acquire it from somewhere.
jerry_value_t bytes_written;
// write 10 bytes from to the start of the ArrayBuffer.
bytes_written = jerry_arraybuffer_write (buffer, 0, data, 10);
// read the next 10 bytes
bytes_written += jerry_arraybuffer_write (buffer, bytes_written, data + bytes_written, 10);
// use the ArrayBuffer
jerry_release_value (buffer);
}
```
**See also**
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
- [jerry_get_arraybuffer_byte_length](#jerry_get_arraybuffer_byte_length)
+173
View File
@@ -18,6 +18,7 @@
#include "debugger.h"
#include "ecma-alloc.h"
#include "ecma-array-object.h"
#include "ecma-arraybuffer-object.h"
#include "ecma-builtin-helpers.h"
#include "ecma-builtins.h"
#include "ecma-exceptions.h"
@@ -2625,6 +2626,178 @@ jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, /**< per
#endif /* JERRY_VM_EXEC_STOP */
} /* jerry_set_vm_exec_stop_callback */
/**
* Check if the given value is an ArrayBuffer object.
*
* @return true - if it is an ArrayBuffer object
* false - otherwise
*/
bool
jerry_value_is_arraybuffer (const jerry_value_t value) /**< value to check if it is an ArrayBuffer */
{
jerry_assert_api_available ();
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
jerry_value_t buffer = jerry_get_arg_value (value);
return ecma_is_arraybuffer (buffer);
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (value);
return false;
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_value_is_arraybuffer */
/**
* Creates an ArrayBuffer object with the given length (size).
*
* Notes:
* * the length is specified in bytes.
* * returned value must be freed with jerry_release_value, when it is no longer needed.
* * if the typed arrays are disabled this will return a TypeError.
*
* @return value of the constructed ArrayBuffer object
*/
jerry_value_t
jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffer to create */
{
jerry_assert_api_available ();
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
return jerry_return (ecma_make_object_value (ecma_arraybuffer_new_object (size)));
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (size);
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("ArrayBuffer not supported.")));
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_create_arraybuffer */
/**
* Copy bytes into the ArrayBuffer from a buffer.
*
* Note:
* * if the object passed is not an ArrayBuffer will return 0.
*
* @return number of bytes copied into the ArrayBuffer.
*/
jerry_length_t
jerry_arraybuffer_write (const jerry_value_t value, /**< target ArrayBuffer */
jerry_length_t offset, /**< start offset of the ArrayBuffer */
const uint8_t *buf_p, /**< buffer to copy from */
jerry_length_t buf_size) /**< number of bytes to copy from the buffer */
{
jerry_assert_api_available ();
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
jerry_value_t buffer = jerry_get_arg_value (value);
if (!ecma_is_arraybuffer (buffer))
{
return 0;
}
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
jerry_length_t length = ecma_arraybuffer_get_length (buffer_p);
if (offset >= length)
{
return 0;
}
jerry_length_t copy_count = JERRY_MIN (length - offset, buf_size);
if (copy_count > 0)
{
lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p);
memcpy ((void *) (mem_buffer_p + offset), (void *) buf_p, copy_count);
}
return copy_count;
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (value);
JERRY_UNUSED (offset);
JERRY_UNUSED (buf_p);
JERRY_UNUSED (buf_size);
return 0;
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_arraybuffer_write */
/**
* Copy bytes from a buffer into an ArrayBuffer.
*
* Note:
* * if the object passed is not an ArrayBuffer will return 0.
*
* @return number of bytes read from the ArrayBuffer.
*/
jerry_length_t
jerry_arraybuffer_read (const jerry_value_t value, /**< ArrayBuffer to read from */
jerry_length_t offset, /**< start offset of the ArrayBuffer */
uint8_t *buf_p, /**< destination buffer to copy to */
jerry_length_t buf_size) /**< number of bytes to copy into the buffer */
{
jerry_assert_api_available ();
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
jerry_value_t buffer = jerry_get_arg_value (value);
if (!ecma_is_arraybuffer (buffer))
{
return 0;
}
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
jerry_length_t length = ecma_arraybuffer_get_length (buffer_p);
if (offset >= length)
{
return 0;
}
jerry_length_t copy_count = JERRY_MIN (length - offset, buf_size);
if (copy_count > 0)
{
lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p);
memcpy ((void *) buf_p, (void *) (mem_buffer_p + offset), copy_count);
}
return copy_count;
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (value);
JERRY_UNUSED (offset);
JERRY_UNUSED (buf_p);
JERRY_UNUSED (buf_size);
return 0;
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_arraybuffer_read */
/**
* Get the length (size) of the ArrayBuffer in bytes.
*
* Note:
* This is the 'byteLength' property of an ArrayBuffer.
*
* @return the length of the ArrayBuffer in bytes.
*/
jerry_length_t
jerry_get_arraybuffer_byte_length (const jerry_value_t value) /**< ArrayBuffer */
{
jerry_assert_api_available ();
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
jerry_value_t buffer = jerry_get_arg_value (value);
if (ecma_is_arraybuffer (buffer))
{
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
return ecma_arraybuffer_get_length (buffer_p);
}
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (value);
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
return 0;
} /* jerry_get_arraybuffer_byte_length */
/**
* @}
*/
+15
View File
@@ -441,6 +441,21 @@ jerry_instance_t *jerry_create_instance (uint32_t heap_size, jerry_instance_allo
*/
void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency);
/**
* Array buffer components.
*/
bool jerry_value_is_arraybuffer (const jerry_value_t value);
jerry_value_t jerry_create_arraybuffer (const jerry_length_t size);
jerry_length_t jerry_arraybuffer_write (const jerry_value_t value,
jerry_length_t offset,
const uint8_t *buf_p,
jerry_length_t buf_size);
jerry_length_t jerry_arraybuffer_read (const jerry_value_t value,
jerry_length_t offset,
uint8_t *buf_p,
jerry_length_t buf_size);
jerry_length_t jerry_get_arraybuffer_byte_length (const jerry_value_t value);
/**
* @}
*/
+263
View File
@@ -0,0 +1,263 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jerryscript.h"
#include "jerryscript-port.h"
#include "jerryscript-port-default.h"
#include "test-common.h"
/**
* Register a JavaScript value in the global object.
*/
static void
register_js_value (const char *name_p, /**< name of the function */
jerry_value_t value) /**< JS value */
{
jerry_value_t global_obj_val = jerry_get_global_object ();
jerry_value_t name_val = jerry_create_string ((const jerry_char_t *) name_p);
jerry_value_t result_val = jerry_set_property (global_obj_val, name_val, value);
TEST_ASSERT (jerry_value_is_boolean (result_val));
jerry_release_value (name_val);
jerry_release_value (global_obj_val);
jerry_release_value (result_val);
} /* register_js_value */
static jerry_value_t
assert_handler (const jerry_value_t func_obj_val, /**< function object */
const jerry_value_t this_val, /**< this arg */
const jerry_value_t args_p[], /**< function arguments */
const jerry_length_t args_cnt) /**< number of function arguments */
{
JERRY_UNUSED (func_obj_val);
JERRY_UNUSED (this_val);
if (args_cnt > 0
&& jerry_value_is_boolean (args_p[0])
&& jerry_get_boolean_value (args_p[0]))
{
return jerry_create_boolean (true);
}
if (args_cnt > 1
&& jerry_value_is_string (args_p[1]))
{
jerry_length_t utf8_sz = jerry_get_string_size (args_p[1]);
char string_from_utf8[utf8_sz];
string_from_utf8[utf8_sz] = 0;
jerry_string_to_char_buffer (args_p[1], (jerry_char_t *) string_from_utf8, utf8_sz);
printf ("JS assert: %s\n", string_from_utf8);
}
TEST_ASSERT (false);
} /* assert_handler */
/**
* Checks whether global object has arraybuffer.
*/
static bool
arraybuffer_is_available (void)
{
jerry_value_t global_obj_val = jerry_get_global_object ();
jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "ArrayBuffer");
jerry_value_t prop_value = jerry_has_property (global_obj_val, prop_name);
bool has_prop = jerry_get_boolean_value (prop_value);
jerry_release_value (global_obj_val);
jerry_release_value (prop_name);
jerry_release_value (prop_value);
return has_prop;
} /* arraybuffer_is_available */
/**
* Test ArrayBuffer 'read' api call with various offset values.
*/
static void
test_read_with_offset (uint8_t offset) /**< offset for buffer read. */
{
const char *eval_arraybuffer_src_p = ("var array = new Uint8Array (15);"
"for (var i = 0; i < array.length; i++) { array[i] = i * 2; };"
"array.buffer");
jerry_value_t arraybuffer = jerry_eval ((jerry_char_t *) eval_arraybuffer_src_p,
strlen (eval_arraybuffer_src_p),
true);
TEST_ASSERT (!jerry_value_has_error_flag (arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == 15);
uint8_t buffer[20];
memset (buffer, 120, 20);
/* Try to copy more than the target buffer size. */
jerry_length_t copied = jerry_arraybuffer_read (arraybuffer, offset, buffer, 20);
TEST_ASSERT (copied == (jerry_length_t)(15 - offset));
for (uint8_t i = 0; i < copied; i++)
{
TEST_ASSERT (buffer[i] == (i + offset) * 2);
}
TEST_ASSERT (buffer[15 - offset] == 120);
jerry_release_value (arraybuffer);
} /* test_read_with_offset */
/**
* Test ArrayBuffer 'write' api call with various offset values.
*/
static void test_write_with_offset (uint8_t offset) /**< offset for buffer write. */
{
{
jerry_value_t offset_val = jerry_create_number (offset);
register_js_value ("offset", offset_val);
jerry_release_value (offset_val);
}
const char *eval_arraybuffer_src_p = "var array = new Uint8Array (15); array.buffer";
jerry_value_t arraybuffer = jerry_eval ((jerry_char_t *) eval_arraybuffer_src_p,
strlen (eval_arraybuffer_src_p),
true);
TEST_ASSERT (!jerry_value_has_error_flag (arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == 15);
uint8_t buffer[20];
for (uint8_t i = 0; i < 20; i++)
{
buffer[i] = (uint8_t)(i * 3);
}
/* Intentionally copy more than the allowed space. */
jerry_length_t copied = jerry_arraybuffer_write (arraybuffer, offset, buffer, 20);
TEST_ASSERT (copied == (jerry_length_t)(15 - offset));
const char *eval_test_arraybuffer_p = (
"for (var i = 0; i < offset; i++)"
"{"
" assert (array[i] == 0, 'offset check for: ' + i + ' was: ' + array[i] + ' should be: 0');"
"};"
"for (var i = offset; i < array.length; i++)"
"{"
" var expected = (i - offset) * 3;"
" assert (array[i] == expected, 'calc check for: ' + i + ' was: ' + array[i] + ' should be: ' + expected);"
"};"
"assert (array[15] === undefined, 'ArrayBuffer out of bounds index should return undefined value');");
jerry_value_t res = jerry_eval ((jerry_char_t *) eval_test_arraybuffer_p,
strlen (eval_test_arraybuffer_p),
true);
jerry_release_value (res);
jerry_release_value (arraybuffer);
} /* test_write_with_offset */
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
if (!arraybuffer_is_available ())
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "ArrayBuffer is disabled!\n");
jerry_cleanup ();
return 0;
}
jerry_value_t function_val = jerry_create_external_function (assert_handler);
register_js_value ("assert", function_val);
jerry_release_value (function_val);
/* Test array buffer queries */
{
const char *eval_arraybuffer_src_p = "new ArrayBuffer (10)";
jerry_value_t eval_arraybuffer = jerry_eval ((jerry_char_t *) eval_arraybuffer_src_p,
strlen (eval_arraybuffer_src_p),
true);
TEST_ASSERT (!jerry_value_has_error_flag (eval_arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (eval_arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (eval_arraybuffer) == 10);
jerry_release_value (eval_arraybuffer);
}
/* Test array buffer creation */
{
const uint32_t length = 15;
jerry_value_t arraybuffer = jerry_create_arraybuffer (length);
TEST_ASSERT (!jerry_value_has_error_flag (arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length);
jerry_release_value (arraybuffer);
}
/* Test array buffer read operations */
for (uint8_t i = 0; i < 15; i++)
{
test_read_with_offset (i);
}
/* Test zero length ArrayBuffer read */
{
const uint32_t length = 0;
jerry_value_t arraybuffer = jerry_create_arraybuffer (length);
TEST_ASSERT (!jerry_value_has_error_flag (arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length);
uint8_t data[20];
memset (data, 11, 20);
jerry_length_t bytes_read = jerry_arraybuffer_read (arraybuffer, 0, data, 20);
TEST_ASSERT (bytes_read == 0);
for (int i = 0; i < 20; i++)
{
TEST_ASSERT (data[i] == 11);
}
jerry_release_value (arraybuffer);
}
/* Test array buffer write operations */
for (uint8_t i = 0; i < 15; i++)
{
test_write_with_offset (i);
}
/* Test zero length ArrayBuffer write */
{
const uint32_t length = 0;
jerry_value_t arraybuffer = jerry_create_arraybuffer (length);
TEST_ASSERT (!jerry_value_has_error_flag (arraybuffer));
TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer));
TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length);
uint8_t data[20];
memset (data, 11, 20);
jerry_length_t bytes_written = jerry_arraybuffer_write (arraybuffer, 0, data, 20);
TEST_ASSERT (bytes_written == 0);
jerry_release_value (arraybuffer);
}
jerry_cleanup ();
} /* main */