[gjs/ewlsh/text-encoding: 1/4] 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/4] modules: Support zero-terminated and fixed length encoding.
- Date: Thu, 12 Aug 2021 01:35:48 +0000 (UTC)
commit fc1cc49e60dbcd0da8bc8877ae66346a1c0e0d24
Author: Evan Welsh <contact evanwelsh com>
Date: Mon Jul 5 21:25:20 2021 -0700
modules: Support zero-terminated and fixed length encoding.
gjs/byteArray.cpp | 127 +++++--------------
gjs/jsapi-util-string.cpp | 39 +++++-
gjs/jsapi-util.h | 3 +
gjs/text-encoding.cpp | 313 +++++++++++++++++++++++++++++++++++++---------
gjs/text-encoding.h | 15 ++-
5 files changed, 337 insertions(+), 160 deletions(-)
---
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 63a2f17d..a5979df0 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));
+ 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));
+ 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..51ca5f9c 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,44 @@ JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value value) {
return JS_EncodeStringToUTF8(cx, str);
}
+/**
+ * gjs_string_to_utf8_n:
+ * @param cx: the current #JSContext
+ * @param str: a handle to a JSString
+ * @param output a pointer to a JS::UniqueChars
+ * @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 handles the boilerblate for unpacking a JSString, determining its
+ * length, and returning the appropriate JS::UniqueChars. This function should generally
+ * be preferred over using JS::DeflateStringToUTF8Buffer directly as it correctly
+ * handles allocation in a JS_Free compatible manner.
+ */
+bool gjs_string_to_utf8_n(JSContext* cx, JS::HandleString str, JS::UniqueChars* output,
+ size_t* output_len) {
+ JSLinearString* linear = JS_EnsureLinearString(cx, str);
+ if (!linear)
+ return false;
+
+ size_t length = JS::GetDeflatedUTF8StringLength(linear);
+ char* bytes = js_pod_arena_malloc<char>(js::StringBufferArena, 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 = JS::UniqueChars(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..ec648347 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, JS::UniqueChars* 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 a77bd19f..0ca6e46c 100644
--- a/gjs/text-encoding.cpp
+++ b/gjs/text-encoding.cpp
@@ -26,6 +26,7 @@
#include <js/Utility.h> // for UniqueChars
#include <jsapi.h> // for JS_DefineFunctionById, JS_DefineFun...
#include <jsfriendapi.h> // for JS_NewUint8ArrayWithBuffer, GetUint...
+#include <mozilla/Unused.h>
#include "gi/boxed.h"
#include "gjs/atoms.h"
@@ -35,6 +36,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
@@ -47,6 +62,29 @@ static const char* UTF16_CODESET = "UTF-16LE";
static const char* UTF16_CODESET = "UTF-16BE";
#endif
+GJS_JSAPI_RETURN_CONVENTION
+static JSString* gjs_decode_from_uint8array_slow(JSContext* cx, uint8_t* input,
+ uint32_t input_len,
+ const char* encoding) {
+ size_t bytes_written, bytes_read;
+ GError* error = nullptr;
+
+ GjsAutoChar bytes =
+ g_convert(reinterpret_cast<const char*>(input), input_len,
+ UTF16_CODESET, encoding, &bytes_read, &bytes_written, &error);
+
+ if (error)
+ return gjs_throw_type_error_from_gerror(cx, error);
+
+ // bytes_written should be bytes in a UTF-16 string so should be a
+ // multiple of 2
+ g_assert((bytes_written % 2) == 0);
+
+ // Cast g_convert's output to char16_t and copy the data.
+ const char16_t* unicode_bytes = reinterpret_cast<char16_t*>(bytes.get());
+ return JS_NewUCStringCopyN(cx, unicode_bytes, bytes_written / 2);
+}
+
[[nodiscard]] static bool is_utf8_label(const char* encoding) {
// We could be smarter about utf8 synonyms here.
// For now, we handle any casing and trailing/leading
@@ -63,71 +101,83 @@ static const char* UTF16_CODESET = "UTF-16BE";
g_ascii_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);
+// Finds the length of a given data array, stopping at the first 0 byte.
+template <class T, class L>
+[[nodiscard]] static L zero_terminated_length(const T* data, L len) {
+ if (!data || len == 0)
+ return 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;
+ const T* start = data;
+ auto* found = static_cast<const T*>(
+ std::memchr(start, '\0', static_cast<size_t>(len)));
- rval.setString(s);
- return true;
+ // If a null byte was not found, return the passed length.
+ if (!found)
+ return len;
+
+ return std::distance(start, found);
}
-// 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) {
+ g_assert(encoding && "encoding must be non-null");
+
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;
+ // len should be size_t but SpiderMonkey defines it differently in mozjs78
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 (len && string_termination == GjsStringTermination::ZERO_TERMINATED)
+ 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;
+ 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;
@@ -135,23 +185,168 @@ 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;
+
+ g_assert(current_len == len &&
+ "Garbage collection should not affect data length.");
// 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");
+}
+
+// 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) {
+ JS::UniqueChars utf8;
+ size_t utf8_len;
+
+ if (!gjs_string_to_utf8_n(cx, str, &utf8, &utf8_len))
+ return nullptr;
+
+ if (string_termination == GjsStringTermination::ZERO_TERMINATED) {
+ // strlen is safe because gjs_string_to_utf8_n returns
+ // a null-terminated string.
+ utf8_len = strlen(utf8.get());
+ }
+
+ array_buffer = JS::NewArrayBufferWithContents(cx, utf8_len, utf8.get());
+
+ // array_buffer only assumes ownership if the call succeeded,
+ // if array_buffer assumes ownership we must release our ownership
+ // without freeing the data.
+ if (array_buffer)
+ mozilla::Unused << utf8.release();
+ } else {
+ GError* error = nullptr;
+ GjsAutoChar encoded = nullptr;
+ size_t 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,
+ encoding, // to_encoding
+ "LATIN1", // from_encoding
+ nullptr, // bytes_read
+ &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 (string_termination == GjsStringTermination::ZERO_TERMINATED) {
+ bytes_written =
+ zero_terminated_length(encoded.get(), bytes_written);
+ }
+
+ if (bytes_written == 0)
+ return JS_NewUint8Array(cx, 0);
+
+ array_buffer =
+ JS::NewExternalArrayBuffer(cx, bytes_written, encoded.release(),
+ 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 v_read(cx, JS::NumberValue(read)),
+ v_written(cx, JS::NumberValue(written));
+
+ if (!JS_SetProperty(cx, result, "read", v_read) ||
+ !JS_SetProperty(cx, result, "written", v_written))
+ return false;
+
+ rval.setObject(*result);
+ return true;
}
static JSFunctionSpec gjs_text_encoding_module_funcs[] = {JS_FS_END};
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]