Skip to content

Commit 20e977b

Browse files
author
Minggang Wang
authored
Merge pull request #219 from kenny-y/typed-array
Use TypedArray for primitive array so that it can save memory and run faster at the same time
2 parents 6becd78 + 3e43bf6 commit 20e977b

File tree

7 files changed

+151
-25
lines changed

7 files changed

+151
-25
lines changed

lib/message_translator.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
const debug = require('debug')('rclnodejs:message_translator');
2020

21+
function isTypedArray(value) {
22+
return ArrayBuffer.isView(value) && !(value instanceof DataView);
23+
}
24+
2125
function copyMsgObject(msg, obj) {
2226
if (typeof obj === 'object') {
2327
for (let i in obj) {
@@ -26,7 +30,7 @@ function copyMsgObject(msg, obj) {
2630
if (type === 'string' || type === 'number' || type === 'boolean') {
2731
// A primitive-type value
2832
msg[i] = obj[i];
29-
} else if (Array.isArray(obj[i])) { // TODO(Kenny): deal with TypedArray
33+
} else if (Array.isArray(obj[i]) || isTypedArray(obj[i])) {
3034
// It's an array
3135
if (typeof obj[i][0] === 'object') {
3236
// It's an array of objects: converting to ROS message objects
@@ -45,7 +49,7 @@ function copyMsgObject(msg, obj) {
4549
}
4650
} else {
4751
// Proceed further of this object
48-
copyMsgObject(msg[i], obj[i]); // TODO(Kenny): deal with TypedArray
52+
copyMsgObject(msg[i], obj[i]);
4953
}
5054
} else {
5155
// Extra fields in obj (but not in msg)

rosidl_gen/templates/message.dot

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,40 @@ function getPrimitiveNameByType(type) {
7272
}
7373
}
7474

75-
let primitiveBaseType = ['Bool', 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
76-
'Int64', 'UInt64', 'Float64', 'Float32', 'Char', 'Byte', 'String'];
75+
function getTypedArrayName(type) {
76+
if (type.type === 'int8') {
77+
return 'Int8Array';
78+
} else if (type.type === 'uint8') {
79+
return 'Uint8Array';
80+
} else if (type.type === 'int16') {
81+
return 'Int16Array';
82+
} else if (type.type === 'uint16') {
83+
return 'Uint16Array';
84+
} else if (type.type === 'int32') {
85+
return 'Int32Array';
86+
} else if (type.type === 'uint32') {
87+
return 'Uint32Array';
88+
} else if (type.type === 'int64') {
89+
return 'Int64Array';
90+
} else if (type.type === 'uint64') {
91+
return 'Uint64Array';
92+
} else if (type.type === 'float64') {
93+
return 'Float64Array';
94+
} else if (type.type === 'float32') {
95+
return 'Float32Array';
96+
} else if (type.type === 'char') {
97+
return 'Int8Array';
98+
} else if (type.type === 'byte') {
99+
return 'Uint8Array';
100+
} else {
101+
return '';
102+
}
103+
}
104+
105+
const primitiveBaseType = ['Bool', 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
106+
'Int64', 'UInt64', 'Float64', 'Float32', 'Char', 'Byte', 'String'];
107+
const typedArrayType = ['int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32',
108+
'int64', 'uint64', 'float64', 'float32', 'char', 'byte'];
77109

78110
let existedModules = [];
79111

@@ -85,6 +117,10 @@ function isPrimitivePackage(baseType) {
85117
return primitiveBaseType.indexOf(baseType.type) !== -1;
86118
}
87119

120+
function isTypedArrayType(type) {
121+
return typedArrayType.indexOf(type.type) !== -1;
122+
}
123+
88124
function shouldRequire(baseType, fieldType) {
89125
let requiredModule = '{{=getPackageNameByType(fieldType)}}/{{=getJSFileNameByType(fieldType)}}';
90126
let shouldRequire = !isPrimitivePackage(baseType) && (fieldType.isArray || !fieldType.isPrimitiveType || fieldType.type === 'string');
@@ -272,7 +308,14 @@ class {{=objectWrapper}} {
272308

273309
{{~ it.spec.fields :field}}
274310
get {{=field.name}}() {
275-
{{? field.type.isArray && field.type.isPrimitiveType}}
311+
{{? field.type.isArray && isTypedArrayType(field.type)}}
312+
const src = this._wrapperFields['{{=field.name}}'].data;
313+
let values = new {{=getTypedArrayName(field.type)}}(src.length);
314+
for (let i = 0; i < src.length; ++i) {
315+
values[i] = src[i].data;
316+
}
317+
return values;
318+
{{?? field.type.isArray && field.type.isPrimitiveType}}
276319
let values = [];
277320
this._wrapperFields['{{=field.name}}'].data.forEach((wrapper, index) => {
278321
values.push(wrapper.data);
@@ -348,14 +391,15 @@ class {{=arrayWrapper}} {
348391
}
349392

350393
fill(values) {
351-
let length = values.length;
394+
const length = values.length;
352395
this._resize(length);
353396
{{? isPrimitivePackage(it.spec.baseType)}}
354-
values.forEach((value, index) => {
397+
// Use for loop to make it also work for TypedArray
398+
for (let i = 0; i < length; ++i) {
355399
let wrapper = new {{=objectWrapper}}();
356-
wrapper.data = value;
357-
this._wrappers[index] = wrapper;
358-
});
400+
wrapper.data = values[i];
401+
this._wrappers[i] = wrapper;
402+
}
359403
{{?? !isPrimitivePackage(it.spec.baseType)}}
360404
values.forEach((value, index) => {
361405
this._wrappers[index].copy(value);

test/test-array.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ describe('rclnodejs message communication', function() {
3939
assert.deepStrictEqual(state.header.stamp.nanosec, 789);
4040
assert.deepStrictEqual(state.header.frame_id, 'main frame');
4141
assert.deepStrictEqual(state.name, ['Tom', 'Jerry']);
42-
assert.deepStrictEqual(state.position, [1, 2]);
43-
assert.deepStrictEqual(state.velocity, [2, 3]);
44-
assert.deepStrictEqual(state.effort, [4, 5, 6]);
42+
assert.deepStrictEqual(state.position, Float64Array.from([1, 2]));
43+
assert.deepStrictEqual(state.velocity, Float64Array.from([2, 3]));
44+
assert.deepStrictEqual(state.effort, Float64Array.from([4, 5, 6]));
4545

4646
if (!destroy) {
4747
publisher.kill('SIGINT');

test/test-message-translator-complex.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ describe('Rclnodejs message translation: complex types', function() {
127127
},
128128
data: [1.0, 2.0, 3.0, 8.5, 6.75, 0.5, -0.25],
129129
},
130+
{
131+
layout: {
132+
dim: [
133+
{label: 'height', size: 480, stride: 921600},
134+
{label: 'width', size: 640, stride: 1920},
135+
{label: 'channel', size: 3, stride: 8},
136+
],
137+
data_offset: 1024,
138+
},
139+
data: Float32Array.from([1.0, 2.0, 3.0, 8.5, 6.75, 0.5, -0.25]),
140+
},
130141
]
131142
},
132143
{
@@ -141,7 +152,72 @@ describe('Rclnodejs message translation: complex types', function() {
141152
],
142153
data_offset: 0,
143154
},
144-
data: [-10, 1, 2, 3, 8, 6, 0, -25],
155+
data: [-10, 1, 2, 3, 8, 6, 0, -25], // Provide data via Array
156+
},
157+
{
158+
layout: {
159+
dim: [
160+
{label: 'height', size: 10, stride: 600},
161+
{label: 'width', size: 20, stride: 60},
162+
{label: 'channel', size: 3, stride: 4},
163+
],
164+
data_offset: 0,
165+
},
166+
data: Int32Array.from([-10, 1, 2, 3, 8, 6, 0, -25]), // Provide data via TypedArray
167+
},
168+
]
169+
},
170+
{
171+
pkg: 'std_msgs', type: 'Int16MultiArray',
172+
values: [
173+
{
174+
layout: {
175+
dim: [
176+
{label: 'height', size: 10, stride: 600},
177+
{label: 'width', size: 20, stride: 60},
178+
{label: 'channel', size: 3, stride: 4},
179+
],
180+
data_offset: 0,
181+
},
182+
data: [-10, 1, 2, 3, 8, 6, 0, -25], // Provide data via Array
183+
},
184+
{
185+
layout: {
186+
dim: [
187+
{label: 'height', size: 10, stride: 600},
188+
{label: 'width', size: 20, stride: 60},
189+
{label: 'channel', size: 3, stride: 4},
190+
],
191+
data_offset: 0,
192+
},
193+
data: Int16Array.from([-10, 1, 2, 3, 8, 6, 0, -25]), // Provide data via TypedArray
194+
},
195+
]
196+
},
197+
{
198+
pkg: 'std_msgs', type: 'Int8MultiArray',
199+
values: [
200+
{
201+
layout: {
202+
dim: [
203+
{label: 'height', size: 10, stride: 600},
204+
{label: 'width', size: 20, stride: 60},
205+
{label: 'channel', size: 3, stride: 4},
206+
],
207+
data_offset: 0,
208+
},
209+
data: [-10, 1, 2, 3, 8, 6, 0, -25], // Provide data via Array
210+
},
211+
{
212+
layout: {
213+
dim: [
214+
{label: 'height', size: 10, stride: 600},
215+
{label: 'width', size: 20, stride: 60},
216+
{label: 'channel', size: 3, stride: 4},
217+
],
218+
data_offset: 0,
219+
},
220+
data: Int8Array.from([-10, 1, 2, 3, 8, 6, 0, -25]), // Provide data via TypedArray
145221
},
146222
]
147223
},
@@ -185,6 +261,8 @@ describe('Rclnodejs message translation: complex types', function() {
185261
node.destroy();
186262
resolve();
187263
} else {
264+
console.log('got', value);
265+
console.log('expected', v);
188266
reject('case ' + i + '. Expected: ' + v + ', Got: ' + value);
189267
}
190268
});

test/test-message-type.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,9 @@ describe('Rclnodejs message type testing', function() {
299299
publisher.kill('SIGINT');
300300

301301
assert.deepStrictEqual(state.name, ['Tom', 'Jerry']);
302-
assert.deepStrictEqual(state.position, [1, 2]);
303-
assert.deepStrictEqual(state.velocity, [2, 3]);
304-
assert.deepStrictEqual(state.effort, [4, 5, 6]);
302+
assert.deepStrictEqual(state.position, Float64Array.from([1, 2]));
303+
assert.deepStrictEqual(state.velocity, Float64Array.from([2, 3]));
304+
assert.deepStrictEqual(state.effort, Float64Array.from([4, 5, 6]));
305305

306306
done();
307307
});

test/test-msg-type-cpp-node.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ describe('Rclnodejs - Cpp message type testing', function() {
322322

323323
var publisher = childProcess.spawn(cppPublisherPath, ['-t', 'Array_cpp_js_channel', '-m', 'Array']);
324324
var subscription = node.createSubscription(ByteMultiArray, 'Array_cpp_js_channel', (msg) => {
325-
assert.deepStrictEqual(msg.data, [65, 66, 67]);
325+
assert.deepStrictEqual(msg.data, Uint8Array.from([65, 66, 67]));
326326

327327
if (!destroy) {
328328
node.destroy();
@@ -366,9 +366,9 @@ describe('Rclnodejs - Cpp message type testing', function() {
366366
assert.deepStrictEqual(msg.header.stamp.nanosec, 789);
367367
assert.deepStrictEqual(msg.header.frame_id, 'main frame');
368368
assert.deepStrictEqual(msg.name, ['Tom', 'Jerry']);
369-
assert.deepStrictEqual(msg.position, [1, 2]);
370-
assert.deepStrictEqual(msg.velocity, [2, 3]);
371-
assert.deepStrictEqual(msg.effort, [4, 5, 6]);
369+
assert.deepStrictEqual(msg.position, Float64Array.from([1, 2]));
370+
assert.deepStrictEqual(msg.velocity, Float64Array.from([2, 3]));
371+
assert.deepStrictEqual(msg.effort, Float64Array.from([4, 5, 6]));
372372

373373
if (!destroy) {
374374
node.destroy();

test/test-msg-type-py-node.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ describe('Rclnodejs - Python message type testing', function() {
302302
var destroy = false;
303303
var publisher = utils.launchPythonProcess([`${__dirname}/py/publisher_msg.py`, 'Array']);
304304
var subscription = node.createSubscription(ByteMultiArray, 'Array_py_js_channel', (msg) => {
305-
assert.deepStrictEqual(msg.data, [65, 66, 67]);
305+
assert.deepStrictEqual(msg.data, Uint8Array.from([65, 66, 67]));
306306

307307
if (!destroy) {
308308
node.destroy();
@@ -344,9 +344,9 @@ describe('Rclnodejs - Python message type testing', function() {
344344
assert.deepStrictEqual(msg.header.stamp.nanosec, 789);
345345
assert.deepStrictEqual(msg.header.frame_id, 'main frame');
346346
assert.deepStrictEqual(msg.name, ['Tom', 'Jerry']);
347-
assert.deepStrictEqual(msg.position, [1, 2]);
348-
assert.deepStrictEqual(msg.velocity, [2, 3]);
349-
assert.deepStrictEqual(msg.effort, [4, 5, 6]);
347+
assert.deepStrictEqual(msg.position, Float64Array.from([1, 2]));
348+
assert.deepStrictEqual(msg.velocity, Float64Array.from([2, 3]));
349+
assert.deepStrictEqual(msg.effort, Float64Array.from([4, 5, 6]));
350350

351351
if (!destroy) {
352352
node.destroy();

0 commit comments

Comments
 (0)