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:
Родитель
e32abfa40e
Коммит
2ee7da1cd6
1
AUTHORS
1
AUTHORS
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче