Implement a more efficient vecFromJSArray (#12463)

This is a followup of #5519 and #5655 since they were closed.

It is possible to implement emscripten::vecFromJSArray more efficiently for numeric
arrays by using the optimized TypedArray.prototype.set function.

The main issue with this method is that it will silently fail(or succeed) if elements of
the array or not numbers, as it does not do any type checking but instead works as
if it called the javascript Number() function for each element. (See ToNumber for more
details)

So instead of simply updating vecFromJSArray to use this new implementation and
break code (since there's no typechecking anymore) I added a new
convertJSArrayToNumberVector (name subject to change) and improved performance
a tiny bit for vecFromJSArray by:

*    Taking the val parameter by const reference instead of copy
*    Reserving the storage of the vector
This commit is contained in:
Alon Zakai 2020-10-07 11:11:03 -07:00 коммит произвёл GitHub
Родитель e32abfa40e
Коммит 2ee7da1cd6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 79 добавлений и 16 удалений

Просмотреть файл

@ -512,3 +512,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Marat Dukhan <maratek@google.com> (copyright owned by Google, LLC)
* Stephan Reiter <reste@google.com> (copyright owned by Google, LLC)
* kamenokonyokonyoko <kamenokonokotan@gmail.com>
* Lectem <lectem@gmail.com>

Просмотреть файл

@ -246,14 +246,22 @@ Guide material for this class can be found in :ref:`embind-val-guide`.
:returns: **HamishW**-Replace with description.
.. cpp:function:: std::vector<T> vecFromJSArray(val v)
.. cpp:function:: std::vector<T> vecFromJSArray(const val& v)
**HamishW**-Replace with description.
Copies a javascript array into a std::vector, checking the type of each element.
For a more efficient but unsafe version working with numbers, see convertJSObjectToNumberVector.
**HamishW**. I believe NOT internal. Please confirm.
:param val v: The javascript array to be copied
:returns: A std::vector<T> made from the javascript array
:param val v: **HamishW**-Replace with description.
:returns: **HamishW**-Replace with description.
.. cpp:function:: std::vector<T> convertJSArrayToNumberVector(const val& v)
Converts a javascript object into a std::vector<T> efficiently, as if using the javascript `Number()` function on each element.
This is way more efficient than vecFromJSArray on any array with more than 2 values, but is less safe.
No type checking is done, so any invalid array entry will silently be replaced by a NaN value (or 0 for interger types).
:param val v: The javascript (typed) array to be copied
:returns: A std::vector<T> made from the javascript array
.. cpp:function:: val await() const

Просмотреть файл

@ -580,15 +580,33 @@ namespace emscripten {
};
}
template<typename T>
std::vector<T> vecFromJSArray(val v) {
auto l = v["length"].as<unsigned>();
template <typename T>
std::vector<T> vecFromJSArray(const val& v) {
const size_t l = v["length"].as<size_t>();
std::vector<T> rv;
for(unsigned i = 0; i < l; ++i) {
rv.reserve(l);
for (size_t i = 0; i < l; ++i) {
rv.push_back(v[i].as<T>());
}
return rv;
};
}
template <typename T>
std::vector<T> convertJSArrayToNumberVector(const val& v) {
const size_t l = v["length"].as<size_t>();
std::vector<T> rv;
rv.resize(l);
// Copy the array into our vector through the use of typed arrays.
// It will try to convert each element through Number().
// See https://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%.prototype.set-array-offset
// and https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
val memoryView{ typed_memory_view(l, rv.data()) };
memoryView.call<void>("set", v);
return rv;
}
}

Просмотреть файл

@ -5,6 +5,7 @@
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <emscripten/bind.h>
#include <emscripten/emscripten.h>
#include <emscripten/val.h>
@ -570,7 +571,7 @@ int main()
ensure_js("test_val_throw_(new TypeError('message'))");
// this test should probably go elsewhere as it is not a member of val
test("template<typename T> std::vector<T> vecFromJSArray(val v)");
test("template<typename T> std::vector<T> vecFromJSArray(const val& v)");
EM_ASM(
// can't declare like this because i get:
// error: expected ')'
@ -578,11 +579,33 @@ int main()
//a = [1, '2'];
a = [];
a[0] = 1;
a[1] = 'b';
a[1] = '42';
a[2] = 'b';
a[3] = new Date(100000);
);
ensure(vecFromJSArray<val>(val::global("a")).at(0).as<int>() == 1);
ensure(vecFromJSArray<val>(val::global("a")).at(1).as<string>() == "b");
ensure(vecFromJSArray<val>(val::global("a")).size() == 2);
const std::vector<val>& aAsArray = vecFromJSArray<val>(val::global("a"));
ensure(aAsArray.at(0).as<int>() == 1);
ensure(aAsArray.at(1).as<string>() == "42");
ensure(aAsArray.at(2).as<string>() == "b");
ensure(aAsArray.size() == 4);
test("template<typename T> std::vector<T> convertJSArrayToNumberVector(const val& v)");
const std::vector<float>& aAsNumberVectorFloat = convertJSArrayToNumberVector<float>(val::global("a"));
ensure(aAsNumberVectorFloat.size() == 4);
ensure(aAsNumberVectorFloat.at(0) == 1.f);
ensure(aAsNumberVectorFloat.at(1) == 42.f); // String containing numbers are converted correctly
ensure(std::isnan(aAsNumberVectorFloat.at(2))); // NaN returned if can not be converted for floats
ensure(aAsNumberVectorFloat.at(3) == 100000.f); // Date returns milliseconds since epoch
const std::vector<uint32_t>& aAsNumberVectorUint32_t = convertJSArrayToNumberVector<uint32_t>(val::global("a"));
ensure(aAsNumberVectorUint32_t.size() == 4);
ensure(aAsNumberVectorUint32_t.at(0) == 1);
ensure(aAsNumberVectorUint32_t.at(1) == 42); // String containing numbers are converted correctly
ensure(aAsNumberVectorUint32_t.at(2) == 0); // 0 is returned if can not be converted for integers
ensure(aAsNumberVectorUint32_t.at(3) == 100000); // Date returns milliseconds since epoch
printf("end\n");
return 0;

Просмотреть файл

@ -244,7 +244,20 @@ pass
pass
pass
test:
template<typename T> std::vector<T> vecFromJSArray(val v)
template<typename T> std::vector<T> vecFromJSArray(const val& v)
pass
pass
pass
pass
test:
template<typename T> std::vector<T> convertJSArrayToNumberVector(const val& v)
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass