From 01412ff16ecf4a17fde7a49cbc8dacdc2a28da36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20S=C3=B8rvig?= Date: Tue, 8 Nov 2022 12:26:50 +0100 Subject: [PATCH] wasm: add native QByteArray conversion functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add functions which converts to and from JavaScript data arrays: static QByteArray::fromUint8Array(emscripten::val array) emscripten::val QByteArray::toUint8Array() const with corresponding internal qstdweb API: static Uint8Array Uint8Array::copyFrom(const QByteArray &buffer) QByteArray Uint8Array::copyToQByteArray() const Both functions will make a copy of the data, i.e. there is no shared reference counting. They take and return Uint8Array typed array views, via emscripten::val JavaScript object references. Unlike other native conversion functions, these have the special property that the data referenced by the native Uint8Array exists outside the heap memory area. This means we can’t e.g. memcpy the data. However, the heap is itself a JavaScript ArrayBuffer, and we can create a Uint8Array view to the buffer owned by the QByteArray, and then use JavaScript API to copy. See the qstdweb::Uint8Array::copy() implementation. That also means that a fromRawUint8Array() variant (which does not copy) is not possible to implement, since we can’t create a pointer to the source data. The inverse toRawUint8Array() is implementable - it would return a Uint8Array view which references the heap’s ArrayBuffer. However, this may turn out to be ill-advised, since Emscripten will create a new ArrayBuffer if/when it resizes the heap. In any case this left for a future expansion. Change-Id: Icaf48fd17ea8686bf04cb523cc1eb581ce63ed34 Reviewed-by: Qt CI Bot Reviewed-by: Tor Arne Vestbø Reviewed-by: Lorn Potter --- .../code/src_corelib_text_qbytearray.cpp | 11 ++++ src/corelib/platform/wasm/qstdweb.cpp | 18 ++++++ src/corelib/platform/wasm/qstdweb_p.h | 3 + src/corelib/text/qbytearray.cpp | 57 +++++++++++++++++++ src/corelib/text/qbytearray.h | 11 ++++ 5 files changed, 100 insertions(+) diff --git a/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp b/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp index 8a9cb7c841..2a780f1c49 100644 --- a/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp @@ -461,4 +461,15 @@ QByteArray ba = QByteArrayLiteral("byte array contents"); QByteArray encoded("Qt%20is%20great%33"); QByteArray decoded = encoded.percentDecoded(); // Set to "Qt is great!" //! [54] + +//! [55] +emscripten::val uint8array = emscripten::val::global("g_uint8array"); +QByteArray byteArray = QByteArray::fromUint8Array(uint8array); +//! [55] + +//! [56] +QByteArray byteArray = "test"; +emscripten::val uint8array = QByteArray::toUint8Array(byteArray); +//! [56] + } diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp index 363a21ee8d..69ed8fe34f 100644 --- a/src/corelib/platform/wasm/qstdweb.cpp +++ b/src/corelib/platform/wasm/qstdweb.cpp @@ -573,6 +573,18 @@ void Uint8Array::copyTo(char *destination) const Uint8Array(destination, length()).set(*this); } +// Copies the Uint8Array content to a destination QByteArray +QByteArray Uint8Array::copyToQByteArray() const +{ + if (length() > std::numeric_limits::max()) + return QByteArray(); + + QByteArray destinationArray; + destinationArray.resize(length()); + copyTo(destinationArray.data()); + return destinationArray; +} + // Copies the Uint8Array content to a destination on the heap void Uint8Array::copy(char *destination, const Uint8Array &source) { @@ -587,6 +599,12 @@ Uint8Array Uint8Array::copyFrom(const char *buffer, uint32_t size) return contentCopy; } +// Copies content from a QByteArray to a new Uint8Array object +Uint8Array Uint8Array::copyFrom(const QByteArray &buffer) +{ + return copyFrom(buffer.constData(), buffer.size()); +} + emscripten::val Uint8Array::val() { return m_uint8Array; diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h index badca5d402..b3fc8a95f7 100644 --- a/src/corelib/platform/wasm/qstdweb_p.h +++ b/src/corelib/platform/wasm/qstdweb_p.h @@ -128,8 +128,11 @@ namespace qstdweb { void set(const Uint8Array &source); void copyTo(char *destination) const; + QByteArray copyToQByteArray() const; + static void copy(char *destination, const Uint8Array &source); static Uint8Array copyFrom(const char *buffer, uint32_t size); + static Uint8Array copyFrom(const QByteArray &buffer); emscripten::val val(); private: diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index ec17c0e15a..9eabf22600 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -18,6 +18,9 @@ #include "qstringconverter_p.h" #include #include +#if defined(Q_OS_WASM) +#include "private/qstdweb_p.h" +#endif #ifndef QT_NO_COMPRESS #include @@ -4838,6 +4841,60 @@ QByteArray QByteArray::toPercentEncoding(const QByteArray &exclude, const QByteA return result; } +#if defined(Q_OS_WASM) || defined(Q_QDOC) + +/*! + \brief Constructs a new QByteArray containing a copy of the Uint8Array \a uint8array. + + This function transfers data from a JavaScript data buffer - which + is not addressable from C++ code - to heap memory owned by a QByteArray. + The Uint8Array can be released once this function returns and a copy + has been made. + + The \a uint8array argument must an emscripten::val referencing an Uint8Array + object, e.g. obtained from a global JavaScript variable: + + \snippet code/src_corelib_text_qbytearray.cpp 55 + + This function returns a null QByteArray if the size of the Uint8Array + exceeds the maximum capacity of QByteArray, or if the \a uint8array + argument is not of the Uint8Array type. + + \since 6.4 + \ingroup platform-type-conversions + + \sa toUint8Array() +*/ + +QByteArray QByteArray::fromUint8Array(emscripten::val uint8array) +{ + return qstdweb::Uint8Array(uint8array).copyToQByteArray(); +} + +/*! + \brief Creates a Uint8Array from a QByteArray + + This function transfers data from heap memory owned by a QByteArray + to a JavaScript data buffer. The function allocates and copies into an + ArrayBuffer, and returns a Uint8Array view to that buffer. + + The JavaScript objects own a copy of the data, and this + QByteArray can be safely deleted after the copy has been made. + + \snippet code/src_corelib_text_qbytearray.cpp 56 + + \since 6.4 + \ingroup platform-type-conversions + + \sa toUint8Array() +*/ +emscripten::val QByteArray::toUint8Array() +{ + return qstdweb::Uint8Array::copyFrom(*this).val(); +} + +#endif + /*! \typedef QByteArray::ConstIterator \internal */ diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index 3a57b383dc..afe234d643 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -34,6 +34,12 @@ Q_FORWARD_DECLARE_CF_TYPE(CFData); Q_FORWARD_DECLARE_OBJC_CLASS(NSData); #endif +#if defined(Q_OS_WASM) || defined(Q_QDOC) +namespace emscripten { + class val; +} +#endif + QT_BEGIN_NAMESPACE class QString; @@ -387,6 +393,11 @@ public: NSData *toRawNSData() const Q_DECL_NS_RETURNS_AUTORELEASED; #endif +#if defined(Q_OS_WASM) || defined(Q_QDOC) + static QByteArray fromUint8Array(emscripten::val uint8array); + emscripten::val toUint8Array(); +#endif + typedef char *iterator; typedef const char *const_iterator; typedef iterator Iterator;