[gjs/ewlsh/text-encoding: 1/5] modules: Support zero-terminated and fixed length encoding.
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/text-encoding: 1/5] modules: Support zero-terminated and fixed length encoding.
- Date: Mon, 5 Jul 2021 05:20:13 +0000 (UTC)
commit e297f19e5d0e5fa44ea98094a3e0cc48735b63fc
Author: Evan Welsh <contact evanwelsh com>
Date: Sun Jul 4 22:04:39 2021 -0700
modules: Support zero-terminated and fixed length encoding.
gjs/byteArray.cpp | 127 ++++-------------
gjs/jsapi-util-string.cpp | 37 ++++-
gjs/jsapi-util.h | 3 +
gjs/text-encoding.cpp | 344 ++++++++++++++++++++++++++++++++++++++--------
gjs/text-encoding.h | 15 +-
5 files changed, 364 insertions(+), 162 deletions(-)
---
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 63a2f17d..b210e77b 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -5,7 +5,6 @@
#include <config.h>
#include <stdint.h>
-#include <string.h> // for strcmp, memchr, strlen
#include <girepository.h>
#include <glib-object.h>
@@ -13,7 +12,6 @@
#include <js/ArrayBuffer.h>
#include <js/CallArgs.h>
-#include <js/GCAPI.h> // for AutoCheckCannotGC
#include <js/PropertySpec.h>
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
@@ -31,11 +29,7 @@
#include "gjs/text-encoding.h"
#include "util/misc.h" // for _gjs_memdup2
-/* Callbacks to use with JS::NewExternalArrayBuffer() */
-
-static void gfree_arraybuffer_contents(void* contents, void*) {
- g_free(contents);
-}
+// Callback to use with JS::NewExternalArrayBuffer()
static void bytes_unref_arraybuffer(void* contents [[maybe_unused]],
void* user_data) {
@@ -53,7 +47,15 @@ static bool to_string_func(JSContext* cx, unsigned argc, JS::Value* vp) {
&byte_array, "encoding", &encoding))
return false;
- return bytearray_to_string(cx, byte_array, encoding.get(), args.rval());
+ const char* actual_encoding = encoding ? encoding.get() : "utf-8";
+ JS::RootedString str(cx, gjs_decode_from_uint8array(
+ cx, byte_array, actual_encoding,
+ GjsStringTermination::ZERO_TERMINATED, true));
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
}
/* Workaround to keep existing code compatible. This function is tacked onto
@@ -71,7 +73,15 @@ static bool instance_to_string_func(JSContext* cx, unsigned argc,
if (!gjs_parse_call_args(cx, "toString", args, "|s", "encoding", &encoding))
return false;
- return bytearray_to_string(cx, this_obj, encoding.get(), args.rval());
+ const char* actual_encoding = encoding ? encoding.get() : "utf-8";
+ JS::RootedString str(cx, gjs_decode_from_uint8array(
+ cx, this_obj, actual_encoding,
+ GjsStringTermination::ZERO_TERMINATED, true));
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
}
GJS_JSAPI_RETURN_CONVENTION
@@ -83,101 +93,22 @@ static bool define_legacy_tostring(JSContext* cx, JS::HandleObject array) {
/* fromString() function implementation */
GJS_JSAPI_RETURN_CONVENTION
-static bool
-from_string_func(JSContext *context,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
+static bool from_string_func(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedString str(cx);
JS::UniqueChars encoding;
- JS::UniqueChars utf8;
- bool encoding_is_utf8;
- JS::RootedObject obj(context), array_buffer(context);
-
- if (!gjs_parse_call_args(context, "fromString", argv, "s|s",
- "string", &utf8,
+ if (!gjs_parse_call_args(cx, "fromString", args, "S|s", "string", &str,
"encoding", &encoding))
return false;
- if (argc > 1) {
- /* maybe we should be smarter about utf8 synonyms here.
- * doesn't matter much though. encoding_is_utf8 is
- * just an optimization anyway.
- */
- encoding_is_utf8 = (strcmp(encoding.get(), "UTF-8") == 0);
- } else {
- encoding_is_utf8 = true;
- }
-
- if (encoding_is_utf8) {
- /* optimization? avoids iconv overhead and runs
- * libmozjs hardwired utf16-to-utf8.
- */
- size_t len = strlen(utf8.get());
- array_buffer =
- JS::NewArrayBufferWithContents(context, len, utf8.release());
- } else {
- JSString *str = argv[0].toString(); /* Rooted by argv */
- GError *error = NULL;
- char *encoded = NULL;
- gsize bytes_written;
-
- /* Scope for AutoCheckCannotGC, will crash if a GC is triggered
- * while we are using the string's chars */
- {
- JS::AutoCheckCannotGC nogc;
- size_t len;
-
- if (JS_StringHasLatin1Chars(str)) {
- const JS::Latin1Char *chars =
- JS_GetLatin1StringCharsAndLength(context, nogc, str, &len);
- if (chars == NULL)
- return false;
-
- encoded = g_convert((char *) chars, len,
- encoding.get(), // to_encoding
- "LATIN1", /* from_encoding */
- NULL, /* bytes read */
- &bytes_written, &error);
- } else {
- const char16_t *chars =
- JS_GetTwoByteStringCharsAndLength(context, nogc, str, &len);
- if (chars == NULL)
- return false;
-
- encoded = g_convert((char *) chars, len * 2,
- encoding.get(), // to_encoding
- "UTF-16", /* from_encoding */
- NULL, /* bytes read */
- &bytes_written, &error);
- }
- }
-
- if (!encoded)
- return gjs_throw_gerror_message(context, error); // frees GError
-
- if (bytes_written == 0) {
- g_free(encoded);
- JS::RootedObject empty_array(context, JS_NewUint8Array(context, 0));
- if (!empty_array || !define_legacy_tostring(context, empty_array))
- return false;
-
- argv.rval().setObject(*empty_array);
- return true;
- }
-
- array_buffer =
- JS::NewExternalArrayBuffer(context, bytes_written, encoded,
- gfree_arraybuffer_contents, nullptr);
- }
-
- if (!array_buffer)
- return false;
- obj = JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1);
- if (!obj || !define_legacy_tostring(context, obj))
+ const char* actual_encoding = encoding ? encoding.get() : "utf-8";
+ JS::RootedObject uint8array(
+ cx, gjs_encode_to_uint8array(cx, str, actual_encoding,
+ GjsStringTermination::ZERO_TERMINATED));
+ if (!uint8array || !define_legacy_tostring(cx, uint8array))
return false;
- argv.rval().setObject(*obj);
+ args.rval().setObject(*uint8array);
return true;
}
diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp
index e5de20c2..49226d53 100644
--- a/gjs/jsapi-util-string.cpp
+++ b/gjs/jsapi-util-string.cpp
@@ -19,7 +19,6 @@
#include <js/BigInt.h>
#include <js/CharacterEncoding.h>
#include <js/Class.h>
-#include <js/ComparisonOperators.h>
#include <js/GCAPI.h> // for AutoCheckCannotGC
#include <js/Id.h> // for JSID_IS_STRING...
#include <js/Promise.h>
@@ -92,6 +91,42 @@ JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value value) {
return JS_EncodeStringToUTF8(cx, str);
}
+/**
+ * gjs_string_to_utf8:
+ * @param cx: the current #JSContext
+ * @param str: a handle to a JSString
+ * @param output a pointer for the output char array
+ * @param output_len a pointer for the length of output
+ *
+ * @brief Converts a JSString to UTF-8 and puts the char array in #output and
+ * its length in #output_len.
+ *
+ * This function is a wrapper around the JSAPI for deflating UTF-8, it should
+ * be used instead of using JS::DeflateStringToUTF8Buffer directly.
+ */
+bool gjs_string_to_utf8_n(JSContext* cx, JS::HandleString str, char** output,
+ size_t* output_len) {
+ JSLinearString* linear = JS_EnsureLinearString(cx, str);
+ if (!linear)
+ return false;
+
+ size_t length = JS::GetDeflatedUTF8StringLength(linear);
+ char* bytes = static_cast<char*>(g_malloc(length + 1));
+ if (!bytes)
+ return false;
+
+ // Append a zero-terminator to the string.
+ bytes[length] = '\0';
+
+ size_t deflated_length [[maybe_unused]] =
+ JS::DeflateStringToUTF8Buffer(linear, mozilla::Span(bytes, length));
+ g_assert(deflated_length == length);
+
+ *output_len = length;
+ *output = bytes;
+ return true;
+}
+
bool
gjs_string_from_utf8(JSContext *context,
const char *utf8_string,
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index e1f41e5a..c43fd860 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -451,6 +451,9 @@ void gjs_warning_reporter(JSContext*, JSErrorReport* report);
GJS_JSAPI_RETURN_CONVENTION
JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value string_val);
GJS_JSAPI_RETURN_CONVENTION
+bool gjs_string_to_utf8_n(JSContext* cx, JS::HandleString str, char** output,
+ size_t* output_len);
+GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_from_utf8(JSContext *context,
const char *utf8_string,
JS::MutableHandleValue value_p);
diff --git a/gjs/text-encoding.cpp b/gjs/text-encoding.cpp
index a3559fc8..daa43da2 100644
--- a/gjs/text-encoding.cpp
+++ b/gjs/text-encoding.cpp
@@ -35,6 +35,20 @@
#include "gjs/jsapi-util.h"
#include "gjs/text-encoding.h"
+// Callback to use with JS::NewExternalArrayBuffer()
+
+static void gfree_arraybuffer_contents(void* contents, void*) {
+ g_free(contents);
+}
+
+static std::nullptr_t gjs_throw_type_error_from_gerror(JSContext* cx,
+ GError* error) {
+ g_return_val_if_fail(error, nullptr);
+ gjs_throw_custom(cx, JSProto_TypeError, nullptr, "%s", error->message);
+ g_error_free(error);
+ return nullptr;
+}
+
// UTF16_CODESET is used to encode and decode UTF-16 buffers with
// iconv. To ensure the output of iconv is laid out in memory correctly
// we have to use UTF-16LE on little endian systems and UTF-16BE on big
@@ -62,71 +76,78 @@ static const char* UTF16_CODESET = "UTF-16BE";
strcasecmp(stripped, "utf8") == 0;
}
-GJS_JSAPI_RETURN_CONVENTION
-static bool to_string_impl_slow(JSContext* cx, uint8_t* data, uint32_t len,
- const char* encoding,
- JS::MutableHandleValue rval) {
- size_t bytes_written;
- GError* error = nullptr;
- GjsAutoChar u16_str =
- g_convert(reinterpret_cast<char*>(data), len, UTF16_CODESET, encoding,
- /* bytes_read = */ nullptr, &bytes_written, &error);
- if (!u16_str)
- return gjs_throw_gerror_message(cx, error); // frees GError
-
- // bytes_written should be bytes in a UTF-16 string so should be a multiple
- // of 2
- g_assert((bytes_written % 2) == 0);
-
- // g_convert 0-terminates the string, although the 0 isn't included in
- // bytes_written
- JSString* s =
- JS_NewUCStringCopyZ(cx, reinterpret_cast<char16_t*>(u16_str.get()));
- if (!s)
- return false;
+// Finds the length of a given data array, stopping at the first 0 byte.
+[[nodiscard]] static uint32_t zero_terminated_length(uint8_t* data,
+ uint32_t len) {
+ uint8_t *start = data, *end = data + (len * sizeof(uint8_t));
+ uint8_t* found = std::find(start, end, 0);
+ if (found != end)
+ return std::distance(data, found) / sizeof(uint8_t);
- rval.setString(s);
- return true;
+ return len;
}
-// implement ByteArray.toString() with an optional encoding arg
-bool bytearray_to_string(JSContext* context, JS::HandleObject byte_array,
- const char* encoding, JS::MutableHandleValue rval) {
+// decode() function implementation
+JSString* gjs_decode_from_uint8array(JSContext* cx, JS::HandleObject byte_array,
+ const char* encoding,
+ GjsStringTermination string_termination) {
if (!JS_IsUint8Array(byte_array)) {
- gjs_throw(context,
- "Argument to ByteArray.toString() must be a Uint8Array");
- return false;
+ gjs_throw(cx, "Argument to decode() must be a Uint8Array");
+ return nullptr;
}
- bool encoding_is_utf8 = true;
- if (encoding)
- encoding_is_utf8 = is_utf8_label(encoding);
-
uint8_t* data;
uint32_t len;
bool is_shared_memory;
js::GetUint8ArrayLengthAndData(byte_array, &len, &is_shared_memory, &data);
- if (len == 0) {
- rval.setString(JS_GetEmptyString(context));
- return true;
- }
+ // If the desired behavior is zero-terminated, calculate the
+ // zero-terminated length of the given data. If the original
+ // length, len, is smaller than the zero-terminated length,
+ // use it.
+ if (len && string_termination == GjsStringTermination::ZERO_TERMINATED)
+ len = std::min(len, zero_terminated_length(data, len));
+
+ // If the calculated length is 0 we can just return an empty string.
+ if (len == 0)
+ return JS_GetEmptyString(cx);
+ // Optimization, only use glib's iconv-based converters if we're dealing
+ // with a non-UTF8 encoding. SpiderMonkey has highly optimized UTF-8 decoder
+ // and encoders.
+ bool encoding_is_utf8 = is_utf8_label(encoding);
if (!encoding_is_utf8)
- return to_string_impl_slow(context, data, len, encoding, rval);
+ return gjs_decode_from_uint8array_slow(cx, data, len, encoding);
- // optimization, avoids iconv overhead and runs libmozjs hardwired
- // utf8-to-utf16
+ JS::RootedString decoded(cx);
+ JS::UTF8Chars chars(reinterpret_cast<char*>(data), len);
+ JS::RootedString str(cx, JS_NewStringCopyUTF8N(cx, chars));
- // If there are any 0 bytes, including the terminating byte, stop at the
- // first one
- if (data[len - 1] == 0 || memchr(data, 0, len)) {
- if (!gjs_string_from_utf8(context, reinterpret_cast<char*>(data), rval))
- return false;
+ // If an exception occurred, we need to check if the
+ // exception was an InternalError. Unfortunately,
+ // SpiderMonkey's decoder can throw InternalError for some
+ // invalid UTF-8 sources, we have to convert this into a
+ // TypeError to match the Encoding specification.
+ if (str) {
+ decoded.set(str);
} else {
- if (!gjs_string_from_utf8_n(context, reinterpret_cast<char*>(data), len,
- rval))
- return false;
+ if (!JS_IsExceptionPending(cx))
+ return nullptr;
+ JS::RootedValue exc(cx);
+ if (!JS_GetPendingException(cx, &exc) || !exc.isObject())
+ return nullptr;
+
+ JS::RootedObject exc_obj(cx, &exc.toObject());
+ const JSClass* internal_error =
+ js::ProtoKeyToClass(JSProto_InternalError);
+ if (JS_InstanceOf(cx, exc_obj, internal_error, nullptr)) {
+ // Clear the existing exception.
+ JS_ClearPendingException(cx);
+ gjs_throw_custom(cx, JSProto_TypeError, nullptr,
+ "The provided encoded data was not valid UTF-8");
+ }
+
+ return nullptr;
}
uint8_t* current_data;
@@ -134,26 +155,227 @@ bool bytearray_to_string(JSContext* context, JS::HandleObject byte_array,
bool ignore_val;
// If a garbage collection occurs between when we call
- // js::GetUint8ArrayLengthAndData and return from gjs_string_from_utf8, a
- // use-after-free corruption can occur if the garbage collector shifts the
- // location of the Uint8Array's private data. To mitigate this we call
- // js::GetUint8ArrayLengthAndData again and then compare if the length and
- // pointer are still the same. If the pointers differ, we use the slow path
- // to ensure no data corruption occurred. The shared-ness of an array cannot
- // change between calls, so we ignore it.
+ // js::GetUint8ArrayLengthAndData and return from
+ // gjs_decode_from_uint8array, a use-after-free corruption can occur if the
+ // garbage collector shifts the location of the Uint8Array's private data.
+ // To mitigate this we call js::GetUint8ArrayLengthAndData again and then
+ // compare if the length and pointer are still the same. If the pointers
+ // differ, we use the slow path to ensure no data corruption occurred. The
+ // shared-ness of an array cannot change between calls, so we ignore it.
js::GetUint8ArrayLengthAndData(byte_array, ¤t_len, &ignore_val,
¤t_data);
// Ensure the private data hasn't changed
- if (current_len == len && current_data == data)
- return true;
+ if (current_data == data)
+ return decoded;
+
+ // Length shouldn't change across calls but recalculate
+ // based on the moved data to be sure.
+ if (current_len &&
+ string_termination == GjsStringTermination::ZERO_TERMINATED) {
+ current_len = std::min(
+ current_len, zero_terminated_length(current_data, current_len));
+ }
// This was the UTF-8 optimized path, so we explicitly pass the encoding
- return to_string_impl_slow(context, current_data, current_len, "UTF-8",
- rval);
+ return gjs_decode_from_uint8array_slow(cx, current_data, current_len,
+ "UTF-8", fatal);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_decode(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ JS::RootedObject byte_array(cx);
+ JS::UniqueChars encoding;
+ bool fatal = false;
+ if (!gjs_parse_call_args(cx, "decode", args, "os|b", "byteArray",
+ &byte_array, "encoding", &encoding, "fatal",
+ &fatal))
+ return false;
+
+ JS::RootedString decoded(
+ cx, gjs_decode_from_uint8array(cx, byte_array, encoding.get(),
+ GjsStringTermination::EXPLICIT_LENGTH,
+ fatal));
+ if (!decoded)
+ return false;
+
+ args.rval().setString(decoded);
+ return true;
+}
+
+// encode() function implementation
+JSObject* gjs_encode_to_uint8array(JSContext* cx, JS::HandleString str,
+ const char* encoding,
+ GjsStringTermination string_termination) {
+ JS::RootedObject array_buffer(cx);
+
+ bool encoding_is_utf8 = is_utf8_label(encoding);
+ if (encoding_is_utf8) {
+ char* utf8;
+ size_t utf8_len;
+
+ if (!gjs_string_to_utf8_n(cx, str, &utf8, &utf8_len))
+ return nullptr;
+
+ if (string_termination == GjsStringTermination::ZERO_TERMINATED) {
+ utf8_len = std::min(utf8_len, strlen(utf8));
+ }
+
+ array_buffer = JS::NewArrayBufferWithContents(cx, utf8_len, utf8);
+ } else {
+ GError* error = nullptr;
+ char* encoded = nullptr;
+ gsize bytes_written;
+
+ /* Scope for AutoCheckCannotGC, will crash if a GC is triggered
+ * while we are using the string's chars */
+ {
+ JS::AutoCheckCannotGC nogc;
+ size_t len;
+
+ if (JS_StringHasLatin1Chars(str)) {
+ const JS::Latin1Char* chars =
+ JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len);
+ if (!chars)
+ return nullptr;
+
+ encoded =
+ g_convert(reinterpret_cast<const char*>(chars), len,
+ /* to_encoding */ encoding,
+ /* from_encoding */ "LATIN1",
+ /* bytes read */ nullptr, &bytes_written, &error);
+ } else {
+ const char16_t* chars =
+ JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len);
+ if (!chars)
+ return nullptr;
+
+ encoded =
+ g_convert(reinterpret_cast<const char*>(chars), len * 2,
+ encoding, // to_encoding
+ "UTF-16", /* from_encoding */
+ nullptr, /* bytes read */
+ &bytes_written, &error);
+ }
+ }
+
+ if (!encoded)
+ return gjs_throw_type_error_from_gerror(cx, error); // frees GError
+
+ if (bytes_written == 0) {
+ g_free(encoded);
+ JS::RootedObject empty_array(cx, JS_NewUint8Array(cx, 0));
+ if (!empty_array)
+ return nullptr;
+
+ return empty_array;
+ }
+
+ array_buffer = JS::NewExternalArrayBuffer(
+ cx, bytes_written, encoded, gfree_arraybuffer_contents, nullptr);
+ }
+
+ if (!array_buffer)
+ return nullptr;
+
+ return JS_NewUint8ArrayWithBuffer(cx, array_buffer, 0, -1);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_encode_into_uint8array(JSContext* cx, JS::HandleString str,
+ JS::HandleObject uint8array,
+ JS::MutableHandleValue rval) {
+ if (!JS_IsUint8Array(uint8array)) {
+ gjs_throw_custom(cx, JSProto_TypeError, nullptr,
+ "Argument to encodeInto() must be a Uint8Array");
+ return false;
+ }
+
+ uint32_t len = JS_GetTypedArrayByteLength(uint8array);
+ bool shared = JS_GetTypedArraySharedness(uint8array);
+
+ if (shared) {
+ gjs_throw(cx, "Cannot encode data into shared memory.");
+ return false;
+ }
+
+ mozilla::Maybe<mozilla::Tuple<size_t, size_t>> results;
+
+ {
+ JS::AutoCheckCannotGC nogc(cx);
+ uint8_t* data = JS_GetUint8ArrayData(uint8array, &shared, nogc);
+
+ // We already checked for sharedness with JS_GetTypedArraySharedness
+ g_assert(!shared);
+
+ results = JS_EncodeStringToUTF8BufferPartial(
+ cx, str, mozilla::AsWritableChars(mozilla::Span(data, len)));
+ }
+
+ if (!results) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ size_t read, written;
+
+ mozilla::Tie(read, written) = *results;
+
+ g_assert(written <= len);
+
+ JS::RootedObject result(cx, JS_NewPlainObject(cx));
+ if (!result)
+ return false;
+
+ JS::RootedValue readv(cx, JS::NumberValue(read)),
+ writtenv(cx, JS::NumberValue(written));
+
+ if (!JS_SetProperty(cx, result, "read", readv) ||
+ !JS_SetProperty(cx, result, "written", writtenv)) {
+ return false;
+ }
+
+ rval.setObject(*result);
+ return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_encode(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedString str(cx);
+ JS::UniqueChars encoding;
+ if (!gjs_parse_call_args(cx, "encode", args, "Ss", "string", &str,
+ "encoding", &encoding))
+ return false;
+
+ JS::RootedObject uint8array(
+ cx, gjs_encode_to_uint8array(cx, str, encoding.get(),
+ GjsStringTermination::EXPLICIT_LENGTH));
+ if (!uint8array)
+ return false;
+
+ args.rval().setObject(*uint8array);
+ return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_encode_into(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedString str(cx);
+ JS::RootedObject uint8array(cx);
+ if (!gjs_parse_call_args(cx, "encodeInto", args, "So", "string", &str,
+ "byteArray", &uint8array))
+ return false;
+
+ return gjs_encode_into_uint8array(cx, str, uint8array, args.rval());
}
-static JSFunctionSpec gjs_text_encoding_module_funcs[] = {JS_FS_END};
+static JSFunctionSpec gjs_text_encoding_module_funcs[] = {
+ JS_FN("decode", gjs_decode, 3, 0),
+ JS_FN("encodeInto", gjs_encode_into, 2, 0),
+ JS_FN("encode", gjs_encode, 2, 0), JS_FS_END};
bool gjs_define_text_encoding_stuff(JSContext* cx,
JS::MutableHandleObject module) {
diff --git a/gjs/text-encoding.h b/gjs/text-encoding.h
index e9425392..eee174bb 100644
--- a/gjs/text-encoding.h
+++ b/gjs/text-encoding.h
@@ -14,9 +14,20 @@
#include "gjs/macros.h"
+enum class GjsStringTermination {
+ ZERO_TERMINATED,
+ EXPLICIT_LENGTH,
+};
+
+GJS_JSAPI_RETURN_CONVENTION
+JSString* gjs_decode_from_uint8array(JSContext* cx, JS::HandleObject uint8array,
+ const char* encoding,
+ GjsStringTermination string_termination);
+
GJS_JSAPI_RETURN_CONVENTION
-bool bytearray_to_string(JSContext* cx, JS::HandleObject uint8array,
- const char* encoding, JS::MutableHandleValue rval);
+JSObject* gjs_encode_to_uint8array(JSContext* cx, JS::HandleString str,
+ const char* encoding,
+ GjsStringTermination string_termination);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_define_text_encoding_stuff(JSContext* cx,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]