Skip to content

Commit b7415b6

Browse files
committed
Add ArrayBuffer with user specified buffer
New API functions: - jerry_create_arraybuffer_external - jerry_get_arraybuffer_pointer JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com
1 parent 680bafd commit b7415b6

File tree

8 files changed

+416
-5
lines changed

8 files changed

+416
-5
lines changed

docs/02.API-REFERENCE.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ jerry_value_is_arraybuffer (const jerry_value_t value)
10301030
**See also**
10311031

10321032
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
1033+
- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)
10331034

10341035

10351036
## jerry_value_is_boolean
@@ -2465,6 +2466,56 @@ jerry_create_arraybuffer (jerry_length_t size);
24652466
- [jerry_release_value](#jerry_release_value)
24662467

24672468

2469+
## jerry_create_arraybuffer_external
2470+
2471+
**Summary**
2472+
2473+
Create a jerry_value_t representing an ArrayBuffer object with
2474+
user specified back-buffer.
2475+
2476+
User must pass a buffer pointer which is at least `size` big.
2477+
After the object is not needed the GC will call the `free_cb`
2478+
so the user can release the buffer which was provided.
2479+
2480+
**Prototype**
2481+
2482+
```c
2483+
jerry_value_t
2484+
jerry_create_arraybuffer_external (const jerry_length_t size
2485+
uint8_t *buffer_p,
2486+
jerry_object_native_free_callback_t free_cb);
2487+
```
2488+
2489+
- `size` - size of the buffer to use **in bytes** (should not be 0)
2490+
- `buffer_p` - the buffer used for the Array Buffer object (should not be a null pointer)
2491+
- `free_cb` - the callback function called when the object is released
2492+
- return value
2493+
- the new ArrayBuffer as a `jerry_value_t`
2494+
- if the `size` is zero or `buffer_p` is a null pointer will return RangeError
2495+
2496+
**Example**
2497+
2498+
```c
2499+
{
2500+
uint8_t buffer_p[15];
2501+
jerry_value_t buffer_value = jerry_create_arraybuffer_external (15, buffer_p, NULL);
2502+
2503+
... // use the array buffer
2504+
2505+
jerry_release_value (buffer_value);
2506+
}
2507+
```
2508+
2509+
**See also**
2510+
2511+
- [jerry_get_arraybuffer_pointer](#jerry_get_arraybuffer_pointer)
2512+
- [jerry_arraybuffer_read](#jerry_arraybuffer_read)
2513+
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
2514+
- [jerry_value_is_arraybuffer](#jerry_value_is_arraybuffer)
2515+
- [jerry_release_value](#jerry_release_value)
2516+
- [jerry_object_native_free_callback_t](#jerry_object_native_free_callback_t)
2517+
2518+
24682519
## jerry_create_boolean
24692520

24702521
**Summary**
@@ -4921,3 +4972,61 @@ jerry_arraybuffer_write (const jerry_value_t value,
49214972
- [jerry_create_arraybuffer](#jerry_create_arraybuffer)
49224973
- [jerry_arraybuffer_write](#jerry_arraybuffer_write)
49234974
- [jerry_get_arraybuffer_byte_length](#jerry_get_arraybuffer_byte_length)
4975+
4976+
4977+
## jerry_get_arraybuffer_pointer
4978+
4979+
**Summary**
4980+
4981+
The function allows acces to the contents of the Array Buffer directly.
4982+
Only allowed for Array Buffers which were created with
4983+
[jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)
4984+
function calls. In any other case this function will return `NULL`.
4985+
4986+
After using the pointer the [jerry_release_value](#jerry_release_value)
4987+
function must be called.
4988+
4989+
WARNING! This operation is for expert use only! The programmer must
4990+
ensure that the returned memory area is used correctly. That is
4991+
there is no out of bounds reads or writes.
4992+
4993+
**Prototype**
4994+
4995+
```c
4996+
uint8_t *
4997+
jerry_get_arraybuffer_pointer (const jerry_value_t value);
4998+
```
4999+
5000+
- `value` - Array Buffer object.
5001+
- return value
5002+
- pointer to the Array Buffer's data area.
5003+
- NULL if the `value` is not an Array Buffer object with external memory.
5004+
5005+
**Example**
5006+
5007+
```c
5008+
{
5009+
jerry_value_t buffer;
5010+
5011+
// acquire buffer somewhere which was created by a jerry_create_array_buffer_external call.
5012+
5013+
uint8_t *const data = jerry_get_arraybuffer_pointer (buffer);
5014+
5015+
for (int i = 0; i < 22; i++)
5016+
{
5017+
data[i] = (uint8_t) (i + 4);
5018+
}
5019+
5020+
// required after jerry_get_arraybuffer_pointer call.
5021+
jerry_release_value (buffer);
5022+
5023+
// use the Array Buffer
5024+
5025+
// release buffer as it is not needed after this point
5026+
jerry_release_value (buffer);
5027+
}
5028+
```
5029+
5030+
**See also**
5031+
5032+
- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)

jerry-core/api/jerry.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,6 +2670,42 @@ jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffe
26702670
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
26712671
} /* jerry_create_arraybuffer */
26722672

2673+
/**
2674+
* Creates an ArrayBuffer object with user specified buffer.
2675+
*
2676+
* Notes:
2677+
* * the size is specified in bytes.
2678+
* * the buffer passed should be at least the specified bytes big.
2679+
* * if the typed arrays are disabled this will return a TypeError.
2680+
* * if the size is zero or the buffer_p is a null pointer this will return a RangeError.
2681+
*
2682+
* @return value of the construced ArrayBuffer object
2683+
*/
2684+
jerry_value_t
2685+
jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */
2686+
uint8_t *buffer_p, /**< buffer to use as the ArrayBuffer's backing */
2687+
jerry_object_native_free_callback_t free_cb) /**< buffer free callback */
2688+
{
2689+
jerry_assert_api_available ();
2690+
2691+
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
2692+
if (size == 0 || buffer_p == NULL)
2693+
{
2694+
return jerry_throw (ecma_raise_range_error (ECMA_ERR_MSG ("invalid buffer size or storage reference")));
2695+
}
2696+
2697+
ecma_object_t *arraybuffer = ecma_arraybuffer_new_object_external (size,
2698+
buffer_p,
2699+
(ecma_object_native_free_callback_t) free_cb);
2700+
return jerry_return (ecma_make_object_value (arraybuffer));
2701+
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
2702+
JERRY_UNUSED (size);
2703+
JERRY_UNUSED (buffer_p);
2704+
JERRY_UNUSED (free_cb);
2705+
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("ArrayBuffer not supported.")));
2706+
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
2707+
} /* jerry_create_arraybuffer_external */
2708+
26732709
/**
26742710
* Copy bytes into the ArrayBuffer from a buffer.
26752711
*
@@ -2798,6 +2834,45 @@ jerry_get_arraybuffer_byte_length (const jerry_value_t value) /**< ArrayBuffer *
27982834
return 0;
27992835
} /* jerry_get_arraybuffer_byte_length */
28002836

2837+
/**
2838+
* Get a pointer for the start of the ArrayBuffer.
2839+
*
2840+
* Note:
2841+
* * Only valid for ArrayBuffers created with jerry_create_arraybuffer_external.
2842+
* * This is a high-risk operation as the bounds are not checked
2843+
* when accessing the pointer elements.
2844+
* * jerry_release_value must be called on the ArrayBuffer when the pointer is no longer needed.
2845+
*
2846+
* @return pointer to the back-buffer of the ArrayBuffer.
2847+
* pointer is NULL if the parameter is not an ArrayBuffer with external memory
2848+
or it is not an ArrayBuffer at all.
2849+
*/
2850+
uint8_t *
2851+
jerry_get_arraybuffer_pointer (const jerry_value_t value) /**< Array Buffer to use */
2852+
{
2853+
jerry_assert_api_available ();
2854+
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
2855+
jerry_value_t buffer = jerry_get_arg_value (value);
2856+
2857+
if (!ecma_is_arraybuffer (buffer))
2858+
{
2859+
return NULL;
2860+
}
2861+
2862+
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
2863+
if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (buffer_p))
2864+
{
2865+
jerry_acquire_value (value);
2866+
lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p);
2867+
return (uint8_t *const) mem_buffer_p;
2868+
}
2869+
#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
2870+
JERRY_UNUSED (value);
2871+
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
2872+
2873+
return NULL;
2874+
} /* jerry_get_arraybuffer_pointer */
2875+
28012876
/**
28022877
* @}
28032878
*/

jerry-core/ecma/base/ecma-gc.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,29 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
570570
case LIT_MAGIC_STRING_ARRAY_BUFFER_UL:
571571
{
572572
ecma_length_t arraybuffer_length = ext_object_p->u.class_prop.u.length;
573-
size_t size = sizeof (ecma_extended_object_t) + arraybuffer_length;
573+
size_t size;
574+
575+
if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p))
576+
{
577+
size = sizeof (ecma_arraybuffer_external_info);
578+
579+
/* Call external free callback if any. */
580+
ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p;
581+
JERRY_ASSERT (array_p != NULL);
582+
583+
if (array_p->free_cb != NULL)
584+
{
585+
(array_p->free_cb) (array_p->buffer_p);
586+
}
587+
}
588+
else
589+
{
590+
size = sizeof (ecma_extended_object_t) + arraybuffer_length;
591+
}
592+
574593
ecma_dealloc_extended_object (object_p, size);
575594
return;
576595
}
577-
578596
#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */
579597
#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
580598
case LIT_MAGIC_STRING_PROMISE_UL:

jerry-core/ecma/base/ecma-globals.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,8 @@ typedef struct
712712
struct
713713
{
714714
uint16_t class_id; /**< class id of the object */
715+
uint16_t extra_info; /**< extra information for the object
716+
e.g. array buffer type info (external/internal) */
715717

716718
/*
717719
* Description of extra fields. These extra fields depends on the class_id.
@@ -1243,6 +1245,33 @@ typedef struct
12431245

12441246
#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
12451247

1248+
/**
1249+
* Extra information for ArrayBuffers.
1250+
*/
1251+
typedef enum
1252+
{
1253+
ECMA_ARRAYBUFFER_INTERNAL_MEMORY = 0u, /* ArrayBuffer memory is handled internally. */
1254+
ECMA_ARRAYBUFFER_EXTERNAL_MEMORY = (1u << 0), /* ArrayBuffer created via jerry_create_arraybuffer_external. */
1255+
} ecma_arraybuffer_extra_flag_t;
1256+
1257+
#define ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY(object_p) \
1258+
((((ecma_extended_object_t *) object_p)->u.class_prop.extra_info & ECMA_ARRAYBUFFER_EXTERNAL_MEMORY) != 0)
1259+
1260+
/**
1261+
* Struct to store information for ArrayBuffers with external memory.
1262+
*
1263+
* The following elements are stored in Jerry memory.
1264+
*
1265+
* buffer_p - pointer to the external memory.
1266+
* free_cb - pointer to a callback function which is called when the ArrayBuffer is freed.
1267+
*/
1268+
typedef struct
1269+
{
1270+
ecma_extended_object_t extended_object; /**< extended object part */
1271+
void *buffer_p; /**< external buffer pointer */
1272+
ecma_object_native_free_callback_t free_cb; /**< the free callback for the above buffer pointer */
1273+
} ecma_arraybuffer_external_info;
1274+
12461275
/**
12471276
* Some internal properties of TypedArray object.
12481277
* It is only used when the offset is not 0, and

jerry-core/ecma/operations/ecma-arraybuffer-object.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ecma_arraybuffer_new_object (ecma_length_t length) /**< length of the arraybuffe
5151
ECMA_OBJECT_TYPE_CLASS);
5252
ecma_deref_object (prototype_obj_p);
5353
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
54+
ext_object_p->u.class_prop.extra_info = ECMA_ARRAYBUFFER_INTERNAL_MEMORY;
5455
ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_ARRAY_BUFFER_UL;
5556
ext_object_p->u.class_prop.u.length = length;
5657

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

64+
/**
65+
* Helper function: create arraybuffer object with external buffer backing.
66+
*
67+
* The struct of external arraybuffer object:
68+
* ecma_object_t
69+
* extend_part
70+
* arraybuffer external info part
71+
*
72+
* @return ecma_object_t *
73+
*/
74+
ecma_object_t *
75+
ecma_arraybuffer_new_object_external (ecma_length_t length, /**< length of the buffer_p to use */
76+
void *buffer_p, /**< pointer for ArrayBuffer's buffer backing */
77+
ecma_object_native_free_callback_t free_cb) /**< buffer free callback */
78+
{
79+
ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE);
80+
ecma_object_t *object_p = ecma_create_object (prototype_obj_p,
81+
sizeof (ecma_arraybuffer_external_info),
82+
ECMA_OBJECT_TYPE_CLASS);
83+
ecma_deref_object (prototype_obj_p);
84+
ecma_arraybuffer_external_info *array_object_p = (ecma_arraybuffer_external_info *) object_p;
85+
array_object_p->extended_object.u.class_prop.extra_info = ECMA_ARRAYBUFFER_EXTERNAL_MEMORY;
86+
array_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_ARRAY_BUFFER_UL;
87+
array_object_p->extended_object.u.class_prop.u.length = length;
88+
89+
array_object_p->buffer_p = buffer_p;
90+
array_object_p->free_cb = free_cb;
91+
92+
return object_p;
93+
} /* ecma_arraybuffer_new_object_external */
94+
95+
6396
/**
6497
* ArrayBuffer object creation operation.
6598
*
@@ -158,7 +191,16 @@ ecma_arraybuffer_get_buffer (ecma_object_t *object_p) /**< pointer to the ArrayB
158191
JERRY_ASSERT (ecma_object_class_is (object_p, LIT_MAGIC_STRING_ARRAY_BUFFER_UL));
159192

160193
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
161-
return (lit_utf8_byte_t *) (ext_object_p + 1);
194+
195+
if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p))
196+
{
197+
ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p;
198+
return (lit_utf8_byte_t *) array_p->buffer_p;
199+
}
200+
else
201+
{
202+
return (lit_utf8_byte_t *) (ext_object_p + 1);
203+
}
162204
} /* ecma_arraybuffer_get_buffer */
163205

164206
/**

jerry-core/ecma/operations/ecma-arraybuffer-object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ ecma_op_create_arraybuffer_object (const ecma_value_t *, ecma_length_t);
3434
*/
3535
ecma_object_t *
3636
ecma_arraybuffer_new_object (ecma_length_t lengh);
37+
ecma_object_t *
38+
ecma_arraybuffer_new_object_external (ecma_length_t length,
39+
void *buffer_p,
40+
ecma_object_native_free_callback_t free_cb);
3741
lit_utf8_byte_t *
3842
ecma_arraybuffer_get_buffer (ecma_object_t *obj_p) __attr_pure___;
3943
ecma_length_t

jerry-core/include/jerryscript-core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, voi
446446
*/
447447
bool jerry_value_is_arraybuffer (const jerry_value_t value);
448448
jerry_value_t jerry_create_arraybuffer (const jerry_length_t size);
449+
jerry_value_t jerry_create_arraybuffer_external (const jerry_length_t size,
450+
uint8_t *buffer_p,
451+
jerry_object_native_free_callback_t free_cb);
449452
jerry_length_t jerry_arraybuffer_write (const jerry_value_t value,
450453
jerry_length_t offset,
451454
const uint8_t *buf_p,
@@ -455,6 +458,7 @@ jerry_length_t jerry_arraybuffer_read (const jerry_value_t value,
455458
uint8_t *buf_p,
456459
jerry_length_t buf_size);
457460
jerry_length_t jerry_get_arraybuffer_byte_length (const jerry_value_t value);
461+
uint8_t *jerry_get_arraybuffer_pointer (const jerry_value_t value);
458462

459463
/**
460464
* @}

0 commit comments

Comments
 (0)