Gjs: ByteArray vs GLib.Bytes

I encountered a problem trying to use Gio.InputStream.read_bytes_finish() from Typescript, with bindings generated by ts-for-gjs. The underlying C function returns a GBytes* but it seems gjs is expected to convert it to ByteArray. ts-for-gjs seems to agree with this and has some sort of mapping to replace GLib.Bytes with ByteArray in the .d.ts files it outputs. However, this caused a run-time error in my code, because the returned object didn’t match ByteArray’s interface. Instead it seems to be a GLib.Bytes after all, and I can use it in Typescript by casting it as such.

So are ts-for-gjs and the above reference making a false assumption, or is there a bug in gjs which is preventing it from performing the conversion in just a few cases?

Functions that return GBytes* will return a GLib.Bytes instance in GJS. Functions that return a GByteArray* will return a Uint8Array instance. ByteArray is a wrapper around Uint8Array that is a holdover from the days when Uint8Array wasn’t part of JavaScript. It’s quite possible that there is incorrect information out there.

I believe these would be the correct TypeScript types for introspected functions with GBytes or GByteArray parameters:

GBytes* in: GLib.Bytes | Uint8Array | ByteArray
GBytes* out: GLib.Bytes
GByteArray* in: Uint8Array | ByteArray
GByteArray* out: Uint8Array

Thanks, that’s very helpful information.

Although you say ByteArray is a holdover, I think it’s still more useful in some circumstances than Uint8Array because it’s easier to convert it to and from strings.

Does this also mean GLib.ByteArray shouldn’t be used in gjs nor defined in GLib.d.ts?

Indeed, it’s not even possible to obtain one in GJS, the static constructor GLib.ByteArray.new() returns a Uint8Array, and constructing one directly doesn’t work:

gjs> new imports.gi.GLib.ByteArray()
typein:2:1 Error: Unable to construct struct type ByteArray since it has no default constructor and cannot be allocated directly

It should be just as easy to convert between Uint8Array and string by calling the static functions ByteArray.toString() and ByteArray.fromString(), without actually using the ByteArray.ByteArray type (which has bad performance compared to Uint8Array). Additionally, starting in GNOME 40, GJS will support TextEncoder and TextDecoder, should you find that more convenient.

Right. I’ve done a little experiment to confirm that byteArray.fromString() does indeed return a Uint8Array rather than a byteArray.ByteArray. So does byteArray.fromGBytes(). ts-for-gjs currently has this wrong.

However, byteArray.fromArray() (which takes a JS array of numbers as its parameter) returns a byteArray.ByteArray. Is that a bug? It seems inconsistent, and kind of redundant, because ByteArray's constructor can also take an array of numbers.

ByteArray.fromArray() is also a holdover from ByteArray.ByteArray, so that’s why it returns the legacy ByteArray. It’s not needed if using Uint8Array because that’s already an array.

Presumably this also means that functions which traditionally took a bytearray.ByteArray parameter can take a Uint8Array instead? For example Gtk.CssProvider.load_from_data().

Yes, that’s correct.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.