[gjs] Add new GBytes API and conversions
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] Add new GBytes API and conversions
- Date: Tue, 20 Nov 2012 01:48:35 +0000 (UTC)
commit 94cb4e9a2da33e83632f8641e02513b55571cea1
Author: Colin Walters <walters verbum org>
Date: Mon Nov 5 18:15:08 2012 -0500
Add new GBytes API and conversions
GLib's annotations for GBytes have been updated so one can now do:
GLib.Bytes.new([1,2,3])
This allows us to construct them natively, which is nice. But we
should go further.
Now, we should have the ability to convert them to a JS array. In
gjs, we have the byteArray class which "looks like" a JS array, but
actually points to a native array, since it's a lot more efficient.
So, change the internal byteArray class so that it supports holding
a GBytes *or* a GByteArray. It's used as a container as well as
a bridge from JS -> native conversions for the overrides code.
Add an override method "toArray" that allows converting a GBytes to a
"gjs byteArray", which in turn has automatic conversions to
e.g. guint8+len pair as used from C.
https://bugzilla.gnome.org/show_bug.cgi?id=685431
gi/arg.c | 44 ++++++---
gjs/byteArray.c | 213 ++++++++++++++++++++++++++++++++++++++----
gjs/byteArray.h | 15 +++
modules/overrides/GLib.js | 4 +
test/js/testGIMarshalling.js | 36 +++++++
5 files changed, 279 insertions(+), 33 deletions(-)
---
diff --git a/gi/arg.c b/gi/arg.c
index 75c1525..ef8ca54 100644
--- a/gi/arg.c
+++ b/gi/arg.c
@@ -1365,9 +1365,12 @@ gjs_value_to_g_argument(JSContext *context,
if ((interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) &&
/* We special case Closures later, so skip them here */
!g_type_is_a(gtype, G_TYPE_CLOSURE)) {
+ JSObject *obj = JSVAL_TO_OBJECT(value);
- /* special case GError too */
- if (g_type_is_a(gtype, G_TYPE_ERROR)) {
+ if (g_type_is_a(gtype, G_TYPE_BYTES)
+ && gjs_typecheck_bytearray(context, obj, FALSE)) {
+ arg->v_pointer = gjs_byte_array_get_bytes(context, obj);
+ } else if (g_type_is_a(gtype, G_TYPE_ERROR)) {
if (!gjs_typecheck_gerror(context, JSVAL_TO_OBJECT(value), JS_TRUE)) {
arg->v_pointer = NULL;
wrong = TRUE;
@@ -1607,21 +1610,36 @@ gjs_value_to_g_argument(JSContext *context,
gpointer data;
gsize length;
GIArrayType array_type = g_type_info_get_array_type(type_info);
+ GITypeTag element_type;
+ GITypeInfo *param_info;
+ gboolean bytearray_fastpath = FALSE;
- if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY && JSVAL_IS_OBJECT(value)) {
- GByteArray *byte_array;
+ param_info = g_type_info_get_param_type(type_info, 0);
+ element_type = g_type_info_get_tag(param_info);
- byte_array = gjs_byte_array_get_byte_array(context, JSVAL_TO_OBJECT(value));
- if (byte_array) {
- /* extra reference will be removed by gjs_g_argument_release */
- arg->v_pointer = g_byte_array_ref(byte_array);
- break;
+ /* First, let's handle the case where we're passed an instance
+ * of our own byteArray class.
+ */
+ if (JSVAL_IS_OBJECT(value) &&
+ gjs_typecheck_bytearray(context,
+ JSVAL_TO_OBJECT(value),
+ FALSE))
+ {
+ JSObject *bytearray_obj = JSVAL_TO_OBJECT(value);
+ if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
+ arg->v_pointer = gjs_byte_array_get_byte_array(context, bytearray_obj);
+ break;
+ } else if (array_type == GI_ARRAY_TYPE_C &&
+ (element_type == GI_TYPE_TAG_UINT8 || element_type == GI_TYPE_TAG_INT8)) {
+ gjs_byte_array_peek_data(context, bytearray_obj, data, &length);
+ bytearray_fastpath = TRUE;
+ } else {
+ /* Fall through, !handled */
+ }
}
- /* otherwise this is not a JS ByteArray, so fall through to extracting
- all elements from the String or the Array */
- }
- if (!gjs_array_to_explicit_array_internal(context,
+ if (!bytearray_fastpath &&
+ !gjs_array_to_explicit_array_internal(context,
value,
type_info,
arg_name,
diff --git a/gjs/byteArray.c b/gjs/byteArray.c
index 13c4fcf..e5eb7c4 100644
--- a/gjs/byteArray.c
+++ b/gjs/byteArray.c
@@ -25,12 +25,15 @@
#include <string.h>
#include <glib.h>
#include "byteArray.h"
+#include "../gi/boxed.h"
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include <girepository.h>
#include <util/log.h>
typedef struct {
GByteArray *array;
+ GBytes *bytes;
} ByteArrayInstance;
static struct JSClass gjs_byte_array_class;
@@ -75,6 +78,13 @@ static struct JSClass gjs_byte_array_class = {
NULL, NULL, NULL, NULL, NULL
};
+JSBool
+gjs_typecheck_bytearray(JSContext *context,
+ JSObject *object,
+ JSBool throw)
+{
+ return do_base_typecheck(context, object, throw);
+}
static JSBool
gjs_value_from_gsize(JSContext *context,
@@ -89,6 +99,28 @@ gjs_value_from_gsize(JSContext *context,
}
}
+static void
+byte_array_ensure_array (ByteArrayInstance *priv)
+{
+ if (priv->bytes) {
+ priv->array = g_bytes_unref_to_array(priv->bytes);
+ priv->bytes = NULL;
+ } else {
+ g_assert(priv->array);
+ }
+}
+
+static void
+byte_array_ensure_gbytes (ByteArrayInstance *priv)
+{
+ if (priv->array) {
+ priv->bytes = g_byte_array_free_to_bytes(priv->array);
+ priv->array = NULL;
+ } else {
+ g_assert(priv->bytes);
+ }
+}
+
static JSBool
gjs_value_to_gsize(JSContext *context,
jsval value,
@@ -151,18 +183,20 @@ byte_array_get_index(JSContext *context,
gsize idx,
jsval *value_p)
{
- guint8 v;
+ gsize len;
+ guint8 *data;
+
+ gjs_byte_array_peek_data(context, obj, &data, &len);
- if (idx >= priv->array->len) {
+ if (idx >= len) {
gjs_throw(context,
- "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %u",
+ "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %lu",
idx,
- priv->array->len);
+ (unsigned long)len);
return JS_FALSE;
}
- v = g_array_index(priv->array, guint8, idx);
- *value_p = INT_TO_JSVAL(v);
+ *value_p = INT_TO_JSVAL(data[idx]);
return JS_TRUE;
}
@@ -183,7 +217,7 @@ byte_array_get_prop(JSContext *context,
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
- if (priv->array == NULL)
+ if (priv->array == NULL && priv->bytes == NULL)
return JS_TRUE; /* prototype, not an instance. */
if (!JS_IdToValue(context, id, &id_value))
@@ -211,16 +245,20 @@ byte_array_length_getter(JSContext *context,
jsval *value_p)
{
ByteArrayInstance *priv;
+ gsize len;
priv = priv_from_js(context, obj);
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
- if (priv->array == NULL)
+ if (priv->array == NULL && priv->bytes == NULL)
return JS_TRUE; /* prototype, not an instance. */
- return gjs_value_from_gsize(context, priv->array->len,
- value_p);
+ if (priv->array != NULL)
+ len = priv->array->len;
+ else if (priv->bytes != NULL)
+ len = g_bytes_get_size (priv->bytes);
+ return gjs_value_from_gsize(context, len, value_p);
}
static JSBool
@@ -237,9 +275,11 @@ byte_array_length_setter(JSContext *context,
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
- if (priv->array == NULL)
+ if (priv->array == NULL && priv->bytes == NULL)
return JS_TRUE; /* prototype, not an instance. */
+ byte_array_ensure_array(priv);
+
if (!gjs_value_to_gsize(context, *value_p,
&len)) {
gjs_throw(context,
@@ -264,6 +304,8 @@ byte_array_set_index(JSContext *context,
return JS_FALSE;
}
+ byte_array_ensure_array(priv);
+
/* grow the array if necessary */
if (idx >= priv->array->len) {
g_byte_array_set_size(priv->array,
@@ -297,12 +339,14 @@ byte_array_set_prop(JSContext *context,
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
- if (priv->array == NULL)
+ if (priv->array == NULL && priv->bytes == NULL)
return JS_TRUE; /* prototype, not an instance. */
if (!JS_IdToValue(context, id, &id_value))
return JS_FALSE;
+ byte_array_ensure_array(priv);
+
/* First handle array indexing */
if (JSVAL_IS_NUMBER(id_value)) {
gsize idx;
@@ -352,12 +396,14 @@ byte_array_new_resolve(JSContext *context,
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
- if (priv->array == NULL)
+ if (priv->array == NULL && priv->bytes == NULL)
return JS_TRUE; /* prototype, not an instance. */
if (!JS_IdToValue(context, id, &id_val))
return JS_FALSE;
+ byte_array_ensure_array(priv);
+
if (JSVAL_IS_NUMBER(id_val)) {
gsize idx;
if (!gjs_value_to_gsize(context, id_val, &idx))
@@ -455,6 +501,8 @@ byte_array_finalize(JSContext *context,
if (priv->array) {
g_byte_array_free(priv->array, TRUE);
priv->array = NULL;
+ } else if (priv->bytes) {
+ g_clear_pointer(&priv->bytes, g_bytes_unref);
}
g_slice_free(ByteArrayInstance, priv);
@@ -478,6 +526,8 @@ to_string_func(JSContext *context,
if (priv == NULL)
return JS_FALSE; /* wrong class passed in */
+ byte_array_ensure_array(priv);
+
encoding_is_utf8 = TRUE;
if (argc >= 1 &&
JSVAL_IS_STRING(argv[0])) {
@@ -559,6 +609,30 @@ to_string_func(JSContext *context,
}
}
+static JSBool
+to_gbytes_func(JSContext *context,
+ uintN argc,
+ jsval *vp)
+{
+ JSObject *object = JS_THIS_OBJECT(context, vp);
+ ByteArrayInstance *priv;
+ JSObject *ret_bytes_obj;
+ GIBaseInfo *gbytes_info;
+
+ priv = priv_from_js(context, object);
+ if (priv == NULL)
+ return JS_FALSE; /* wrong class passed in */
+
+ byte_array_ensure_gbytes(priv);
+
+ gbytes_info = g_irepository_find_by_gtype(NULL, G_TYPE_BYTES);
+ ret_bytes_obj = gjs_boxed_from_c_struct(context, (GIStructInfo*)gbytes_info,
+ priv->bytes, GJS_BOXED_CREATION_NONE);
+
+ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(ret_bytes_obj));
+ return JS_TRUE;
+}
+
static JSObject*
byte_array_new(JSContext *context)
{
@@ -568,7 +642,6 @@ byte_array_new(JSContext *context)
array = JS_NewObject(context, &gjs_byte_array_class, gjs_byte_array_prototype, NULL);
priv = g_slice_new0(ByteArrayInstance);
- priv->array = gjs_g_byte_array_new(0);
g_assert(priv_from_js(context, array) == NULL);
JS_SetPrivate(context, array, priv);
@@ -600,6 +673,8 @@ from_string_func(JSContext *context,
g_assert(argc > 0); /* because we specified min args 1 */
+ priv->array = gjs_g_byte_array_new(0);
+
if (!JSVAL_IS_STRING(argv[0])) {
gjs_throw(context,
"byteArray.fromString() called with non-string as first arg");
@@ -708,6 +783,8 @@ from_array_func(JSContext *context,
g_assert(argc > 0); /* because we specified min args 1 */
+ priv->array = gjs_g_byte_array_new(0);
+
if (!JS_IsArrayObject(context, JSVAL_TO_OBJECT(argv[0]))) {
gjs_throw(context,
"byteArray.fromArray() called with non-array as first arg");
@@ -750,6 +827,40 @@ from_array_func(JSContext *context,
return ret;
}
+static JSBool
+from_gbytes_func(JSContext *context,
+ uintN argc,
+ jsval *vp)
+{
+ jsval *argv = JS_ARGV(context, vp);
+ JSObject *bytes_obj;
+ GBytes *gbytes;
+ ByteArrayInstance *priv;
+ JSObject *obj;
+ JSBool ret = JS_FALSE;
+
+ if (!gjs_parse_args(context, "overrides_gbytes_to_array", "o", argc, argv,
+ "bytes", &bytes_obj))
+ return JS_FALSE;
+
+ if (!gjs_typecheck_boxed(context, bytes_obj, NULL, G_TYPE_BYTES, TRUE))
+ return JS_FALSE;
+
+ gbytes = gjs_c_struct_from_boxed(context, bytes_obj);
+
+ obj = byte_array_new(context);
+ if (obj == NULL)
+ return JS_FALSE;
+ priv = priv_from_js(context, obj);
+ g_assert (priv != NULL);
+
+ priv->bytes = g_bytes_ref(gbytes);
+
+ ret = JS_TRUE;
+ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj));
+ return ret;
+}
+
/* Ensure that the module and class objects exists, and that in turn
* ensures that JS_InitClass has been called, causing
* gjs_byte_array_prototype to be valid for the later call to
@@ -798,17 +909,77 @@ gjs_byte_array_from_byte_array (JSContext *context,
return object;
}
-GByteArray*
-gjs_byte_array_get_byte_array (JSContext *context,
- JSObject *object)
+JSObject *
+gjs_byte_array_from_bytes (JSContext *context,
+ GBytes *bytes)
{
+ JSObject *object;
ByteArrayInstance *priv;
+ g_return_val_if_fail(context != NULL, NULL);
+ g_return_val_if_fail(bytes != NULL, NULL);
+
+ byte_array_ensure_initialized (context);
+
+ object = JS_NewObject(context, &gjs_byte_array_class,
+ gjs_byte_array_prototype, NULL);
+ if (!object) {
+ gjs_throw(context, "failed to create byte array");
+ return NULL;
+ }
+
+ priv = g_slice_new0(ByteArrayInstance);
+ g_assert(priv_from_js(context, object) == NULL);
+ JS_SetPrivate(context, object, priv);
+ priv->bytes = g_bytes_ref (bytes);
+
+ return object;
+}
+
+GBytes *
+gjs_byte_array_get_bytes (JSContext *context,
+ JSObject *object)
+{
+ ByteArrayInstance *priv;
priv = priv_from_js(context, object);
- if (priv == NULL)
- return NULL; /* wrong class passed in */
+ g_assert(priv != NULL);
+
+ byte_array_ensure_gbytes(priv);
+
+ return g_bytes_ref (priv->bytes);
+}
+
+GByteArray *
+gjs_byte_array_get_byte_array (JSContext *context,
+ JSObject *obj)
+{
+ ByteArrayInstance *priv;
+ priv = priv_from_js(context, obj);
+ g_assert(priv != NULL);
+
+ byte_array_ensure_array(priv);
+
+ return g_byte_array_ref (priv->array);
+}
- return priv->array;
+void
+gjs_byte_array_peek_data (JSContext *context,
+ JSObject *obj,
+ guint8 **out_data,
+ gsize *out_len)
+{
+ ByteArrayInstance *priv;
+ priv = priv_from_js(context, obj);
+ g_assert(priv != NULL);
+
+ if (priv->array != NULL) {
+ *out_data = (guint8*)priv->array->data;
+ *out_len = (gsize)priv->array->len;
+ } else if (priv->bytes != NULL) {
+ *out_data = (guint8*)g_bytes_get_data(priv->bytes, out_len);
+ } else {
+ g_assert_not_reached();
+ }
}
/* no idea what this is used for. examples in
@@ -829,12 +1000,14 @@ static JSPropertySpec gjs_byte_array_proto_props[] = {
static JSFunctionSpec gjs_byte_array_proto_funcs[] = {
{ "toString", (JSNative) to_string_func, 0, 0 },
+ { "toGBytes", (JSNative) to_gbytes_func, 0, 0 },
{ NULL }
};
static JSFunctionSpec gjs_byte_array_module_funcs[] = {
{ "fromString", (JSNative)from_string_func, 1, 0 },
{ "fromArray", (JSNative)from_array_func, 1, 0 },
+ { "fromGBytes", (JSNative)from_gbytes_func, 1, 0 },
{ NULL }
};
diff --git a/gjs/byteArray.h b/gjs/byteArray.h
index 12c05eb..2d0dadc 100644
--- a/gjs/byteArray.h
+++ b/gjs/byteArray.h
@@ -33,14 +33,29 @@
G_BEGIN_DECLS
+JSBool gjs_typecheck_bytearray (JSContext *context,
+ JSObject *obj,
+ JSBool throw);
+
JSBool gjs_define_byte_array_stuff (JSContext *context,
JSObject *in_object);
JSObject * gjs_byte_array_from_byte_array (JSContext *context,
GByteArray *array);
+JSObject * gjs_byte_array_from_bytes (JSContext *context,
+ GBytes *bytes);
+
GByteArray * gjs_byte_array_get_byte_array (JSContext *context,
JSObject *object);
+GBytes * gjs_byte_array_get_bytes (JSContext *context,
+ JSObject *object);
+
+void gjs_byte_array_peek_data (JSContext *context,
+ JSObject *object,
+ guint8 **out_data,
+ gsize *out_len);
+
G_END_DECLS
#endif /* __GJS_BYTE_ARRAY_H__ */
diff --git a/modules/overrides/GLib.js b/modules/overrides/GLib.js
index 7d69b76..ea3c30d 100644
--- a/modules/overrides/GLib.js
+++ b/modules/overrides/GLib.js
@@ -263,4 +263,8 @@ function _init() {
this.Variant.prototype.toString = function() {
return '[object variant of type "' + this.get_type_string() + '"]';
}
+
+ this.Bytes.prototype.toArray = function() {
+ return imports.byteArray.fromGBytes(this);
+ }
}
diff --git a/test/js/testGIMarshalling.js b/test/js/testGIMarshalling.js
index e046e6d..e127c20 100644
--- a/test/js/testGIMarshalling.js
+++ b/test/js/testGIMarshalling.js
@@ -172,6 +172,42 @@ function testByteArray() {
GIMarshallingTests.bytearray_none_in("\x00\x31\xFF\x33");
}
+function testGBytes() {
+ var i = 0;
+ var refByteArray = new imports.byteArray.ByteArray();
+ refByteArray[i++] = 0;
+ refByteArray[i++] = 49;
+ refByteArray[i++] = 0xFF;
+ refByteArray[i++] = 51;
+ GIMarshallingTests.gbytes_none_in(refByteArray);
+
+ var bytes = GIMarshallingTests.gbytes_full_return();
+ GIMarshallingTests.gbytes_none_in(bytes);
+
+ var array = bytes.toArray();
+ assertEquals(array[0], 0);
+ assertEquals(array[1], 49);
+ assertEquals(array[2], 0xFF);
+ assertEquals(array[3], 51);
+
+ bytes = GLib.Bytes.new([0, 49, 0xFF, 51]);
+ GIMarshallingTests.gbytes_none_in(bytes);
+
+ bytes = GLib.Bytes.new("\x00\x31\xFF\x33");
+ GIMarshallingTests.gbytes_none_in(bytes);
+
+ bytes = GIMarshallingTests.gbytes_full_return();
+ array = bytes.toArray(); // Array should just be holding a ref, not a copy
+ assertEquals(array[1], 49);
+ array[1] = 42; // Assignment should force to GByteArray
+ assertEquals(array[1], 42);
+ array[1] = 49; // Flip the value back
+ GIMarshallingTests.gbytes_none_in(array.toGBytes()); // Now convert back to GBytes
+
+ bytes = GLib.Bytes.new([97, 98, 99, 100])
+ GIMarshallingTests.array_uint8_in(bytes.toArray());
+}
+
function testPtrArray() {
var array;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]