1#include "aliased_buffer-inl.h"
2#include "node_test_fixture.h"
3#include "v8.h"
4
5using node::AliasedBufferBase;
6
7class AliasBufferTest : public NodeTestFixture {};
8
9template <class NativeT>
10void CreateOracleValues(std::vector<NativeT>* buf) {
11  for (size_t i = 0, j = buf->size(); i < buf->size(); i++, j--) {
12    (*buf)[i] = static_cast<NativeT>(j);
13  }
14}
15
16template <class NativeT, class V8T>
17void WriteViaOperator(AliasedBufferBase<NativeT, V8T>* aliasedBuffer,
18                      const std::vector<NativeT>& oracle) {
19  // write through the API
20  for (size_t i = 0; i < oracle.size(); i++) {
21    (*aliasedBuffer)[i] = oracle[i];
22  }
23}
24
25template <class NativeT, class V8T>
26void WriteViaSetValue(AliasedBufferBase<NativeT, V8T>* aliasedBuffer,
27                      const std::vector<NativeT>& oracle) {
28  // write through the API
29  for (size_t i = 0; i < oracle.size(); i++) {
30    aliasedBuffer->SetValue(i, oracle[i]);
31  }
32}
33
34template <class NativeT, class V8T>
35void ReadAndValidate(v8::Isolate* isolate,
36                     v8::Local<v8::Context> context,
37                     AliasedBufferBase<NativeT, V8T>* aliasedBuffer,
38                     const std::vector<NativeT>& oracle) {
39  // read through the API
40  for (size_t i = 0; i < oracle.size(); i++) {
41    NativeT v1 = (*aliasedBuffer)[i];
42    NativeT v2 = aliasedBuffer->GetValue(i);
43    EXPECT_TRUE(v1 == oracle[i]);
44    EXPECT_TRUE(v2 == oracle[i]);
45  }
46
47  // validate size of JS Buffer
48  EXPECT_TRUE(aliasedBuffer->GetJSArray()->Length() == oracle.size());
49  EXPECT_TRUE(
50    aliasedBuffer->GetJSArray()->ByteLength() ==
51    (oracle.size() * sizeof(NativeT)));
52
53  // validate operator * and GetBuffer are the same
54  EXPECT_TRUE(aliasedBuffer->GetNativeBuffer() == *(*aliasedBuffer));
55
56  // read through the JS API
57  for (size_t i = 0; i < oracle.size(); i++) {
58    v8::Local<V8T> v8TypedArray = aliasedBuffer->GetJSArray();
59    v8::MaybeLocal<v8::Value> v = v8TypedArray->Get(context, i);
60    EXPECT_TRUE(v.IsEmpty() == false);
61    v8::Local<v8::Value> v2 = v.ToLocalChecked();
62    EXPECT_TRUE(v2->IsNumber());
63    v8::MaybeLocal<v8::Number> v3 = v2->ToNumber(context);
64    v8::Local<v8::Number> v4 = v3.ToLocalChecked();
65    NativeT actualValue = static_cast<NativeT>(v4->Value());
66    EXPECT_TRUE(actualValue == oracle[i]);
67  }
68}
69
70template <class NativeT, class V8T>
71void ReadWriteTest(v8::Isolate* isolate) {
72  v8::Isolate::Scope isolate_scope(isolate);
73  v8::HandleScope handle_scope(isolate);
74  v8::Local<v8::Context> context = v8::Context::New(isolate);
75  v8::Context::Scope context_scope(context);
76
77  const size_t size = 100;
78  AliasedBufferBase<NativeT, V8T> ab(isolate, size);
79  std::vector<NativeT> oracle(size);
80  CreateOracleValues(&oracle);
81  WriteViaOperator(&ab, oracle);
82  ReadAndValidate(isolate, context, &ab, oracle);
83
84  WriteViaSetValue(&ab, oracle);
85
86  // validate copy constructor
87  {
88    AliasedBufferBase<NativeT, V8T> ab2(ab);
89    ReadAndValidate(isolate, context, &ab2, oracle);
90  }
91  ReadAndValidate(isolate, context, &ab, oracle);
92}
93
94template <
95    class NativeT_A, class V8T_A,
96    class NativeT_B, class V8T_B,
97    class NativeT_C, class V8T_C>
98void SharedBufferTest(
99    v8::Isolate* isolate,
100    size_t count_A,
101    size_t count_B,
102    size_t count_C) {
103  v8::Isolate::Scope isolate_scope(isolate);
104  v8::HandleScope handle_scope(isolate);
105  v8::Local<v8::Context> context = v8::Context::New(isolate);
106  v8::Context::Scope context_scope(context);
107
108  size_t sizeInBytes_A = count_A * sizeof(NativeT_A);
109  size_t sizeInBytes_B = count_B * sizeof(NativeT_B);
110  size_t sizeInBytes_C = count_C * sizeof(NativeT_C);
111
112  AliasedBufferBase<uint8_t, v8::Uint8Array> rootBuffer(
113      isolate, sizeInBytes_A + sizeInBytes_B + sizeInBytes_C);
114  AliasedBufferBase<NativeT_A, V8T_A> ab_A(isolate, 0, count_A, rootBuffer);
115  AliasedBufferBase<NativeT_B, V8T_B> ab_B(
116      isolate, sizeInBytes_A, count_B, rootBuffer);
117  AliasedBufferBase<NativeT_C, V8T_C> ab_C(
118      isolate, sizeInBytes_A + sizeInBytes_B, count_C, rootBuffer);
119
120  std::vector<NativeT_A> oracle_A(count_A);
121  std::vector<NativeT_B> oracle_B(count_B);
122  std::vector<NativeT_C> oracle_C(count_C);
123  CreateOracleValues(&oracle_A);
124  CreateOracleValues(&oracle_B);
125  CreateOracleValues(&oracle_C);
126
127  WriteViaOperator(&ab_A, oracle_A);
128  WriteViaOperator(&ab_B, oracle_B);
129  WriteViaOperator(&ab_C, oracle_C);
130
131  ReadAndValidate(isolate, context, &ab_A, oracle_A);
132  ReadAndValidate(isolate, context, &ab_B, oracle_B);
133  ReadAndValidate(isolate, context, &ab_C, oracle_C);
134
135  WriteViaSetValue(&ab_A, oracle_A);
136  WriteViaSetValue(&ab_B, oracle_B);
137  WriteViaSetValue(&ab_C, oracle_C);
138
139  ReadAndValidate(isolate, context, &ab_A, oracle_A);
140  ReadAndValidate(isolate, context, &ab_B, oracle_B);
141  ReadAndValidate(isolate, context, &ab_C, oracle_C);
142}
143
144TEST_F(AliasBufferTest, Uint8Array) {
145  ReadWriteTest<uint8_t, v8::Uint8Array>(isolate_);
146}
147
148TEST_F(AliasBufferTest, Int8Array) {
149  ReadWriteTest<int8_t, v8::Int8Array>(isolate_);
150}
151
152TEST_F(AliasBufferTest, Uint16Array) {
153  ReadWriteTest<uint16_t, v8::Uint16Array>(isolate_);
154}
155
156TEST_F(AliasBufferTest, Int16Array) {
157  ReadWriteTest<int16_t, v8::Int16Array>(isolate_);
158}
159
160TEST_F(AliasBufferTest, Uint32Array) {
161  ReadWriteTest<uint32_t, v8::Uint32Array>(isolate_);
162}
163
164TEST_F(AliasBufferTest, Int32Array) {
165  ReadWriteTest<int32_t, v8::Int32Array>(isolate_);
166}
167
168TEST_F(AliasBufferTest, Float32Array) {
169  ReadWriteTest<float, v8::Float32Array>(isolate_);
170}
171
172TEST_F(AliasBufferTest, Float64Array) {
173  ReadWriteTest<double, v8::Float64Array>(isolate_);
174}
175
176TEST_F(AliasBufferTest, SharedArrayBuffer1) {
177  SharedBufferTest<
178      uint32_t, v8::Uint32Array,
179      double, v8::Float64Array,
180      int8_t, v8::Int8Array>(isolate_, 100, 80, 8);
181}
182
183TEST_F(AliasBufferTest, SharedArrayBuffer2) {
184  SharedBufferTest<
185      double, v8::Float64Array,
186      int8_t, v8::Int8Array,
187      double, v8::Float64Array>(isolate_, 100, 8, 8);
188}
189
190TEST_F(AliasBufferTest, SharedArrayBuffer3) {
191  SharedBufferTest<
192      int8_t, v8::Int8Array,
193      int8_t, v8::Int8Array,
194      double, v8::Float64Array>(isolate_, 1, 7, 8);
195}
196
197TEST_F(AliasBufferTest, SharedArrayBuffer4) {
198  SharedBufferTest<
199      int8_t, v8::Int8Array,
200      int8_t, v8::Int8Array,
201      int32_t, v8::Int32Array>(isolate_, 1, 3, 1);
202}
203
204TEST_F(AliasBufferTest, OperatorOverloads) {
205  v8::Isolate::Scope isolate_scope(isolate_);
206  v8::HandleScope handle_scope(isolate_);
207  v8::Local<v8::Context> context = v8::Context::New(isolate_);
208  v8::Context::Scope context_scope(context);
209  const size_t size = 10;
210  AliasedBufferBase<uint32_t, v8::Uint32Array> ab{isolate_, size};
211
212  EXPECT_EQ(static_cast<uint32_t>(1), ab[0] = 1);
213  EXPECT_EQ(static_cast<uint32_t>(4), ab[0] += 3);
214  EXPECT_EQ(static_cast<uint32_t>(2), ab[0] -= 2);
215  EXPECT_EQ(static_cast<uint32_t>(-2), -ab[0]);
216}
217
218TEST_F(AliasBufferTest, OperatorOverloadsRefs) {
219  v8::Isolate::Scope isolate_scope(isolate_);
220  v8::HandleScope handle_scope(isolate_);
221  v8::Local<v8::Context> context = v8::Context::New(isolate_);
222  v8::Context::Scope context_scope(context);
223  AliasedBufferBase<uint32_t, v8::Uint32Array> ab{isolate_, 2};
224  using Reference = AliasedBufferBase<uint32_t, v8::Uint32Array>::Reference;
225  Reference ref = ab[0];
226  Reference ref_value = ab[1] = 2;
227
228  EXPECT_EQ(static_cast<uint32_t>(2), ref = ref_value);
229  EXPECT_EQ(static_cast<uint32_t>(4), ref += ref_value);
230  EXPECT_EQ(static_cast<uint32_t>(2), ref -= ref_value);
231  EXPECT_EQ(static_cast<uint32_t>(-2), -ref);
232}
233