wasm: add native QByteArray conversion functions

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 <qt_ci_bot@qt-project.org>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
bb10
Morten Sørvig 2022-11-08 12:26:50 +01:00 committed by Morten Johan Sørvig
parent 3aaf5975e4
commit 01412ff16e
5 changed files with 100 additions and 0 deletions

View File

@ -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]
}

View File

@ -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<qsizetype>::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;

View File

@ -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:

View File

@ -18,6 +18,9 @@
#include "qstringconverter_p.h"
#include <qdatastream.h>
#include <qmath.h>
#if defined(Q_OS_WASM)
#include "private/qstdweb_p.h"
#endif
#ifndef QT_NO_COMPRESS
#include <zconf.h>
@ -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
*/

View File

@ -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;