Skip to content

Add ArrayBuffer with user specified buffer #2162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions docs/02.API-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,7 @@ jerry_value_is_arraybuffer (const jerry_value_t value)
**See also**

- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)


## jerry_value_is_boolean
Expand Down Expand Up @@ -2465,6 +2466,56 @@ jerry_create_arraybuffer (jerry_length_t size);
- [jerry_release_value](#jerry_release_value)


## jerry_create_arraybuffer_external

**Summary**

Creates a jerry_value_t representing an ArrayBuffer object with
user specified back-buffer.

User must pass a buffer pointer which is at least `size` big.
After the object is not needed the GC will call the `free_cb`
so the user can release the buffer which was provided.

**Prototype**

```c
jerry_value_t
jerry_create_arraybuffer_external (const jerry_length_t size
uint8_t *buffer_p,
jerry_object_native_free_callback_t free_cb);
```

- `size` - size of the buffer to use **in bytes** (should not be 0)
- `buffer_p` - the buffer used for the Array Buffer object (should not be a null pointer)
- `free_cb` - the callback function called when the object is released
- return value
- the new ArrayBuffer as a `jerry_value_t`
- if the `size` is zero or `buffer_p` is a null pointer will return RangeError

**Example**

```c
{
uint8_t buffer_p[15];
jerry_value_t buffer_value = jerry_create_arraybuffer_external (15, buffer_p, NULL);

... // use the array buffer

jerry_release_value (buffer_value);
}
```

**See also**

- [jerry_get_arraybuffer_pointer](#jerry_get_arraybuffer_pointer)
- [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_object_native_free_callback_t](#jerry_object_native_free_callback_t)


## jerry_create_boolean

**Summary**
Expand Down Expand Up @@ -4921,3 +4972,61 @@ jerry_arraybuffer_write (const jerry_value_t value,
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
- [jerry_get_arraybuffer_byte_length](#jerry_get_arraybuffer_byte_length)


## jerry_get_arraybuffer_pointer

**Summary**

The function allows access to the contents of the Array Buffer directly.
Only allowed for Array Buffers which were created with
[jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)
function calls. In any other case this function will return `NULL`.

After using the pointer the [jerry_release_value](#jerry_release_value)
function must be called.

**WARNING!** This operation is for expert use only! The programmer must
ensure that the returned memory area is used correctly. That is
there is no out of bounds reads or writes.

**Prototype**

```c
uint8_t *
jerry_get_arraybuffer_pointer (const jerry_value_t value);
```

- `value` - Array Buffer object.
- return value
- pointer to the Array Buffer's data area.
- NULL if the `value` is not an Array Buffer object with external memory.

**Example**

```c
{
jerry_value_t buffer;

// acquire buffer somewhere which was created by a jerry_create_array_buffer_external call.

uint8_t *const data = jerry_get_arraybuffer_pointer (buffer);

for (int i = 0; i < 22; i++)
{
data[i] = (uint8_t) (i + 4);
}

// required after jerry_get_arraybuffer_pointer call.
jerry_release_value (buffer);

// use the Array Buffer

// release buffer as it is not needed after this point
jerry_release_value (buffer);
}
```

**See also**

- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)
75 changes: 75 additions & 0 deletions jerry-core/api/jerry.c
Original file line number Diff line number Diff line change
Expand Up @@ -2670,6 +2670,42 @@ jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffe
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_create_arraybuffer */

/**
* Creates an ArrayBuffer object with user specified buffer.
*
* Notes:
* * the size is specified in bytes.
* * the buffer passed should be at least the specified bytes big.
* * if the typed arrays are disabled this will return a TypeError.
* * if the size is zero or the buffer_p is a null pointer this will return a RangeError.
*
* @return value of the construced ArrayBuffer object
*/
jerry_value_t
jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */
uint8_t *buffer_p, /**< buffer to use as the ArrayBuffer's backing */
jerry_object_native_free_callback_t free_cb) /**< buffer free callback */
{
jerry_assert_api_available ();

#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
if (size == 0 || buffer_p == NULL)
{
return jerry_throw (ecma_raise_range_error (ECMA_ERR_MSG ("invalid buffer size or storage reference")));
}

ecma_object_t *arraybuffer = ecma_arraybuffer_new_object_external (size,
buffer_p,
(ecma_object_native_free_callback_t) free_cb);
return jerry_return (ecma_make_object_value (arraybuffer));
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (size);
JERRY_UNUSED (buffer_p);
JERRY_UNUSED (free_cb);
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("ArrayBuffer not supported.")));
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
} /* jerry_create_arraybuffer_external */

/**
* Copy bytes into the ArrayBuffer from a buffer.
*
Expand Down Expand Up @@ -2798,6 +2834,45 @@ jerry_get_arraybuffer_byte_length (const jerry_value_t value) /**< ArrayBuffer *
return 0;
} /* jerry_get_arraybuffer_byte_length */

/**
* Get a pointer for the start of the ArrayBuffer.
*
* Note:
* * Only valid for ArrayBuffers created with jerry_create_arraybuffer_external.
* * This is a high-risk operation as the bounds are not checked
* when accessing the pointer elements.
* * jerry_release_value must be called on the ArrayBuffer when the pointer is no longer needed.
*
* @return pointer to the back-buffer of the ArrayBuffer.
* pointer is NULL if the parameter is not an ArrayBuffer with external memory
or it is not an ArrayBuffer at all.
*/
uint8_t *
jerry_get_arraybuffer_pointer (const jerry_value_t value) /**< Array Buffer to use */
{
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 NULL;
}

ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (buffer_p))
{
jerry_acquire_value (value);
lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p);
return (uint8_t *const) mem_buffer_p;
}
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
JERRY_UNUSED (value);
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */

return NULL;
} /* jerry_get_arraybuffer_pointer */

/**
* @}
*/
22 changes: 20 additions & 2 deletions jerry-core/ecma/base/ecma-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,11 +570,29 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
case LIT_MAGIC_STRING_ARRAY_BUFFER_UL:
{
ecma_length_t arraybuffer_length = ext_object_p->u.class_prop.u.length;
size_t size = sizeof (ecma_extended_object_t) + arraybuffer_length;
size_t size;

if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p))
{
size = sizeof (ecma_arraybuffer_external_info);

/* Call external free callback if any. */
ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p;
JERRY_ASSERT (array_p != NULL);

if (array_p->free_cb != NULL)
{
(array_p->free_cb) (array_p->buffer_p);
}
}
else
{
size = sizeof (ecma_extended_object_t) + arraybuffer_length;
}

ecma_dealloc_extended_object (object_p, size);
return;
}

#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
case LIT_MAGIC_STRING_PROMISE_UL:
Expand Down
29 changes: 29 additions & 0 deletions jerry-core/ecma/base/ecma-globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@ typedef struct
struct
{
uint16_t class_id; /**< class id of the object */
uint16_t extra_info; /**< extra information for the object
e.g. array buffer type info (external/internal) */

/*
* Description of extra fields. These extra fields depends on the class_id.
Expand Down Expand Up @@ -1243,6 +1245,33 @@ typedef struct

#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN

/**
* Extra information for ArrayBuffers.
*/
typedef enum
{
ECMA_ARRAYBUFFER_INTERNAL_MEMORY = 0u, /* ArrayBuffer memory is handled internally. */
ECMA_ARRAYBUFFER_EXTERNAL_MEMORY = (1u << 0), /* ArrayBuffer created via jerry_create_arraybuffer_external. */
} ecma_arraybuffer_extra_flag_t;

#define ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY(object_p) \
((((ecma_extended_object_t *) object_p)->u.class_prop.extra_info & ECMA_ARRAYBUFFER_EXTERNAL_MEMORY) != 0)

/**
* Struct to store information for ArrayBuffers with external memory.
*
* The following elements are stored in Jerry memory.
*
* buffer_p - pointer to the external memory.
* free_cb - pointer to a callback function which is called when the ArrayBuffer is freed.
*/
typedef struct
{
ecma_extended_object_t extended_object; /**< extended object part */
void *buffer_p; /**< external buffer pointer */
ecma_object_native_free_callback_t free_cb; /**< the free callback for the above buffer pointer */
} ecma_arraybuffer_external_info;

/**
* Some internal properties of TypedArray object.
* It is only used when the offset is not 0, and
Expand Down
44 changes: 43 additions & 1 deletion jerry-core/ecma/operations/ecma-arraybuffer-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ ecma_arraybuffer_new_object (ecma_length_t length) /**< length of the arraybuffe
ECMA_OBJECT_TYPE_CLASS);
ecma_deref_object (prototype_obj_p);
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
ext_object_p->u.class_prop.extra_info = ECMA_ARRAYBUFFER_INTERNAL_MEMORY;
ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_ARRAY_BUFFER_UL;
ext_object_p->u.class_prop.u.length = length;

Expand All @@ -60,6 +61,38 @@ ecma_arraybuffer_new_object (ecma_length_t length) /**< length of the arraybuffe
return object_p;
} /* ecma_arraybuffer_new_object */

/**
* Helper function: create arraybuffer object with external buffer backing.
*
* The struct of external arraybuffer object:
* ecma_object_t
* extend_part
* arraybuffer external info part
*
* @return ecma_object_t *, pointer to the created ArrayBuffer object
*/
ecma_object_t *
ecma_arraybuffer_new_object_external (ecma_length_t length, /**< length of the buffer_p to use */
void *buffer_p, /**< pointer for ArrayBuffer's buffer backing */
ecma_object_native_free_callback_t free_cb) /**< buffer free callback */
{
ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE);
ecma_object_t *object_p = ecma_create_object (prototype_obj_p,
sizeof (ecma_arraybuffer_external_info),
ECMA_OBJECT_TYPE_CLASS);
ecma_deref_object (prototype_obj_p);
ecma_arraybuffer_external_info *array_object_p = (ecma_arraybuffer_external_info *) object_p;
array_object_p->extended_object.u.class_prop.extra_info = ECMA_ARRAYBUFFER_EXTERNAL_MEMORY;
array_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_ARRAY_BUFFER_UL;
array_object_p->extended_object.u.class_prop.u.length = length;

array_object_p->buffer_p = buffer_p;
array_object_p->free_cb = free_cb;

return object_p;
} /* ecma_arraybuffer_new_object_external */


/**
* ArrayBuffer object creation operation.
*
Expand Down Expand Up @@ -158,7 +191,16 @@ ecma_arraybuffer_get_buffer (ecma_object_t *object_p) /**< pointer to the ArrayB
JERRY_ASSERT (ecma_object_class_is (object_p, LIT_MAGIC_STRING_ARRAY_BUFFER_UL));

ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
return (lit_utf8_byte_t *) (ext_object_p + 1);

if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p))
{
ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p;
return (lit_utf8_byte_t *) array_p->buffer_p;
}
else
{
return (lit_utf8_byte_t *) (ext_object_p + 1);
}
} /* ecma_arraybuffer_get_buffer */

/**
Expand Down
4 changes: 4 additions & 0 deletions jerry-core/ecma/operations/ecma-arraybuffer-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ ecma_op_create_arraybuffer_object (const ecma_value_t *, ecma_length_t);
*/
ecma_object_t *
ecma_arraybuffer_new_object (ecma_length_t lengh);
ecma_object_t *
ecma_arraybuffer_new_object_external (ecma_length_t length,
void *buffer_p,
ecma_object_native_free_callback_t free_cb);
lit_utf8_byte_t *
ecma_arraybuffer_get_buffer (ecma_object_t *obj_p) __attr_pure___;
ecma_length_t
Expand Down
4 changes: 4 additions & 0 deletions jerry-core/include/jerryscript-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, voi
*/
bool jerry_value_is_arraybuffer (const jerry_value_t value);
jerry_value_t jerry_create_arraybuffer (const jerry_length_t size);
jerry_value_t jerry_create_arraybuffer_external (const jerry_length_t size,
uint8_t *buffer_p,
jerry_object_native_free_callback_t free_cb);
jerry_length_t jerry_arraybuffer_write (const jerry_value_t value,
jerry_length_t offset,
const uint8_t *buf_p,
Expand All @@ -455,6 +458,7 @@ jerry_length_t jerry_arraybuffer_read (const jerry_value_t value,
uint8_t *buf_p,
jerry_length_t buf_size);
jerry_length_t jerry_get_arraybuffer_byte_length (const jerry_value_t value);
uint8_t *jerry_get_arraybuffer_pointer (const jerry_value_t value);

/**
* @}
Expand Down
Loading