#include "v8.h" #include "aliased_buffer.h" #include "node_test_fixture.h" using node::AliasedBufferBase; class AliasBufferTest : public NodeTestFixture {}; template void CreateOracleValues(std::vector* buf) { for (size_t i = 0, j = buf->size(); i < buf->size(); i++, j--) { (*buf)[i] = static_cast(j); } } template void WriteViaOperator(AliasedBufferBase* aliasedBuffer, const std::vector& oracle) { // write through the API for (size_t i = 0; i < oracle.size(); i++) { (*aliasedBuffer)[i] = oracle[i]; } } template void WriteViaSetValue(AliasedBufferBase* aliasedBuffer, const std::vector& oracle) { // write through the API for (size_t i = 0; i < oracle.size(); i++) { aliasedBuffer->SetValue(i, oracle[i]); } } template void ReadAndValidate(v8::Isolate* isolate, v8::Local context, AliasedBufferBase* aliasedBuffer, const std::vector& oracle) { // read through the API for (size_t i = 0; i < oracle.size(); i++) { NativeT v1 = (*aliasedBuffer)[i]; NativeT v2 = aliasedBuffer->GetValue(i); EXPECT_TRUE(v1 == oracle[i]); EXPECT_TRUE(v2 == oracle[i]); } // validate size of JS Buffer EXPECT_TRUE(aliasedBuffer->GetJSArray()->Length() == oracle.size()); EXPECT_TRUE( aliasedBuffer->GetJSArray()->ByteLength() == (oracle.size() * sizeof(NativeT))); // validate operator * and GetBuffer are the same EXPECT_TRUE(aliasedBuffer->GetNativeBuffer() == *(*aliasedBuffer)); // read through the JS API for (size_t i = 0; i < oracle.size(); i++) { v8::Local v8TypedArray = aliasedBuffer->GetJSArray(); v8::MaybeLocal v = v8TypedArray->Get(context, i); EXPECT_TRUE(v.IsEmpty() == false); v8::Local v2 = v.ToLocalChecked(); EXPECT_TRUE(v2->IsNumber()); v8::MaybeLocal v3 = v2->ToNumber(context); v8::Local v4 = v3.ToLocalChecked(); NativeT actualValue = static_cast(v4->Value()); EXPECT_TRUE(actualValue == oracle[i]); } } template void ReadWriteTest(v8::Isolate* isolate) { v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); const size_t size = 100; AliasedBufferBase ab(isolate, size); std::vector oracle(size); CreateOracleValues(&oracle); WriteViaOperator(&ab, oracle); ReadAndValidate(isolate, context, &ab, oracle); WriteViaSetValue(&ab, oracle); // validate copy constructor { AliasedBufferBase ab2(ab); ReadAndValidate(isolate, context, &ab2, oracle); } ReadAndValidate(isolate, context, &ab, oracle); } template < class NativeT_A, class V8T_A, class NativeT_B, class V8T_B, class NativeT_C, class V8T_C> void SharedBufferTest( v8::Isolate* isolate, size_t count_A, size_t count_B, size_t count_C) { v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); size_t sizeInBytes_A = count_A * sizeof(NativeT_A); size_t sizeInBytes_B = count_B * sizeof(NativeT_B); size_t sizeInBytes_C = count_C * sizeof(NativeT_C); AliasedBufferBase rootBuffer( isolate, sizeInBytes_A + sizeInBytes_B + sizeInBytes_C); AliasedBufferBase ab_A(isolate, 0, count_A, rootBuffer); AliasedBufferBase ab_B( isolate, sizeInBytes_A, count_B, rootBuffer); AliasedBufferBase ab_C( isolate, sizeInBytes_A + sizeInBytes_B, count_C, rootBuffer); std::vector oracle_A(count_A); std::vector oracle_B(count_B); std::vector oracle_C(count_C); CreateOracleValues(&oracle_A); CreateOracleValues(&oracle_B); CreateOracleValues(&oracle_C); WriteViaOperator(&ab_A, oracle_A); WriteViaOperator(&ab_B, oracle_B); WriteViaOperator(&ab_C, oracle_C); ReadAndValidate(isolate, context, &ab_A, oracle_A); ReadAndValidate(isolate, context, &ab_B, oracle_B); ReadAndValidate(isolate, context, &ab_C, oracle_C); WriteViaSetValue(&ab_A, oracle_A); WriteViaSetValue(&ab_B, oracle_B); WriteViaSetValue(&ab_C, oracle_C); ReadAndValidate(isolate, context, &ab_A, oracle_A); ReadAndValidate(isolate, context, &ab_B, oracle_B); ReadAndValidate(isolate, context, &ab_C, oracle_C); } TEST_F(AliasBufferTest, Uint8Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Int8Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Uint16Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Int16Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Uint32Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Int32Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Float32Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, Float64Array) { ReadWriteTest(isolate_); } TEST_F(AliasBufferTest, SharedArrayBuffer1) { SharedBufferTest< uint32_t, v8::Uint32Array, double, v8::Float64Array, int8_t, v8::Int8Array>(isolate_, 100, 80, 8); } TEST_F(AliasBufferTest, SharedArrayBuffer2) { SharedBufferTest< double, v8::Float64Array, int8_t, v8::Int8Array, double, v8::Float64Array>(isolate_, 100, 8, 8); } TEST_F(AliasBufferTest, SharedArrayBuffer3) { SharedBufferTest< int8_t, v8::Int8Array, int8_t, v8::Int8Array, double, v8::Float64Array>(isolate_, 1, 7, 8); } TEST_F(AliasBufferTest, SharedArrayBuffer4) { SharedBufferTest< int8_t, v8::Int8Array, int8_t, v8::Int8Array, int32_t, v8::Int32Array>(isolate_, 1, 3, 1); } TEST_F(AliasBufferTest, OperatorOverloads) { v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); v8::Local context = v8::Context::New(isolate_); v8::Context::Scope context_scope(context); const size_t size = 10; AliasedBufferBase ab{isolate_, size}; EXPECT_EQ(static_cast(1), ab[0] = 1); EXPECT_EQ(static_cast(4), ab[0] += 3); EXPECT_EQ(static_cast(2), ab[0] -= 2); EXPECT_EQ(static_cast(-2), -ab[0]); } TEST_F(AliasBufferTest, OperatorOverloadsRefs) { v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); v8::Local context = v8::Context::New(isolate_); v8::Context::Scope context_scope(context); AliasedBufferBase ab{isolate_, 2}; using Reference = AliasedBufferBase::Reference; Reference ref = ab[0]; Reference ref_value = ab[1] = 2; EXPECT_EQ(static_cast(2), ref = ref_value); EXPECT_EQ(static_cast(4), ref += ref_value); EXPECT_EQ(static_cast(2), ref -= ref_value); EXPECT_EQ(static_cast(-2), -ref); }