Skip to content

Commit 74beea3

Browse files
committed
Levrage BigInt to represent int64/uint64
1 parent 6295ef8 commit 74beea3

21 files changed

+332
-269
lines changed

lib/parameter.js

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ const ParameterType = {
6161
PARAMETER_DOUBLE_ARRAY: 8,
6262
/** @member {number} */
6363
PARAMETER_STRING_ARRAY: 9,
64+
/** @member {number} */
65+
PARAMETER_BYTE: 10,
6466
};
6567

6668
/**
@@ -125,7 +127,9 @@ class Parameter {
125127
constructor(name, type, value) {
126128
this._name = name;
127129
this._type = type;
128-
this._value = value;
130+
// Convert to bigint if it's type of integer.
131+
this._value =
132+
this._type == ParameterType.PARAMETER_INTEGER ? BigInt(value) : value;
129133
this._isDirty = true;
130134

131135
this.validate();
@@ -240,10 +244,10 @@ class Parameter {
240244
msg.double_array_value = this.value;
241245
break;
242246
case ParameterType.PARAMETER_INTEGER:
243-
msg.integer_value = Math.trunc(this.value);
247+
msg.integer_value = this.value;
244248
break;
245249
case ParameterType.PARAMETER_INTEGER_ARRAY:
246-
msg.integer_array_value = this.value.map((val) => Math.trunc(val));
250+
msg.integer_array_value = this.value;
247251
break;
248252
case ParameterType.PARAMETER_STRING:
249253
msg.string_value = this.value;
@@ -540,7 +544,7 @@ class Range {
540544
* A TypeError is thrown when value is not a number.
541545
* Subclasses should override and call this method for basic type checking.
542546
*
543-
* @param {number} value - The number to check.
547+
* @param {number|bigint} value - The number or bigint to check.
544548
* @return {boolean} - True if value satisfies the range; false otherwise.
545549
*/
546550
inRange(value) {
@@ -550,8 +554,8 @@ class Range {
550554
(inRange, val) => inRange && this.inRange(val),
551555
true
552556
);
553-
} else if (typeof value !== 'number') {
554-
throw new TypeError('Value must be a number');
557+
} else if (typeof value !== 'number' && typeof value !== 'bigint') {
558+
throw new TypeError('Value must be a number or bigint');
555559
}
556560

557561
return true;
@@ -652,27 +656,16 @@ class FloatingPointRange extends Range {
652656
* Defines a range for integer values.
653657
* @class
654658
*/
655-
class IntegerRange extends FloatingPointRange {
659+
class IntegerRange extends Range {
656660
/**
657661
* Create a new instance.
658662
* @constructor
659-
* @param {number} fromValue - The lowest inclusive value in range
660-
* @param {number} toValue - The highest inclusive value in range
661-
* @param {number} step - The internal unit size.
662-
* @param {number} tolerance - The plus/minus tolerance for number equivalence.
663+
* @param {bigint} fromValue - The lowest inclusive value in range
664+
* @param {bigint} toValue - The highest inclusive value in range
665+
* @param {bigint} step - The internal unit size.
663666
*/
664-
constructor(
665-
fromValue,
666-
toValue,
667-
step = 1,
668-
tolerance = DEFAULT_NUMERIC_RANGE_TOLERANCE
669-
) {
670-
super(
671-
Math.trunc(fromValue),
672-
Math.trunc(toValue),
673-
Math.trunc(step),
674-
tolerance
675-
);
667+
constructor(fromValue, toValue, step = 1n) {
668+
super(fromValue, toValue, step);
676669
}
677670

678671
/**
@@ -689,6 +682,19 @@ class IntegerRange extends FloatingPointRange {
689682
parameterType === ParameterType.PARAMETER_INTEGER_ARRAY;
690683
return result;
691684
}
685+
686+
inRange(value) {
687+
const min = this.fromValue;
688+
const max = this.toValue;
689+
if (value < min || value > max) {
690+
return false;
691+
}
692+
693+
if (this.step != 0n && (value - min) % this.step !== 0n) {
694+
return false;
695+
}
696+
return true;
697+
}
692698
}
693699

694700
/**
@@ -763,10 +769,13 @@ function validValue(value, type) {
763769
case ParameterType.PARAMETER_STRING:
764770
result = typeof value === 'string';
765771
break;
766-
case ParameterType.PARAMETER_INTEGER:
767772
case ParameterType.PARAMETER_DOUBLE:
773+
case ParameterType.PARAMETER_BYTE:
768774
result = typeof value === 'number';
769775
break;
776+
case ParameterType.PARAMETER_INTEGER:
777+
result = typeof value === 'bigint';
778+
break;
770779
case ParameterType.PARAMETER_BOOL_ARRAY:
771780
case ParameterType.PARAMETER_BYTE_ARRAY:
772781
case ParameterType.PARAMETER_INTEGER_ARRAY:
@@ -789,7 +798,7 @@ function _validArray(values, type) {
789798
if (type === ParameterType.PARAMETER_BOOL_ARRAY) {
790799
arrayElementType = ParameterType.PARAMETER_BOOL;
791800
} else if (type === ParameterType.PARAMETER_BYTE_ARRAY) {
792-
arrayElementType = ParameterType.PARAMETER_INTEGER;
801+
arrayElementType = ParameterType.PARAMETER_BYTE;
793802
}
794803
if (type === ParameterType.PARAMETER_INTEGER_ARRAY) {
795804
arrayElementType = ParameterType.PARAMETER_INTEGER;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"dot": "^1.1.3",
7676
"dtslint": "^4.2.1",
7777
"fs-extra": "^11.2.0",
78+
"json-bigint": "^1.0.0",
7879
"int64-napi": "^1.0.2",
7980
"is-close": "^1.3.3",
8081
"mkdirp": "^3.0.1",

rosidl_gen/message_translator.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ function copyMsgObject(msg, obj) {
2727
for (let i in obj) {
2828
if (msg.hasMember(i)) {
2929
const type = typeof obj[i];
30-
if (type === 'string' || type === 'number' || type === 'boolean') {
30+
if (
31+
type === 'string' ||
32+
type === 'number' ||
33+
type === 'boolean' ||
34+
type === 'bigint'
35+
) {
3136
// A primitive-type value
3237
msg[i] = obj[i];
3338
} else if (Array.isArray(obj[i]) || isTypedArray(obj[i])) {
@@ -80,17 +85,20 @@ function verifyMessage(message, obj) {
8085
case 'char':
8186
case 'int16':
8287
case 'int32':
83-
case 'int64':
8488
case 'byte':
8589
case 'uint16':
8690
case 'uint32':
87-
case 'uint64':
8891
case 'float32':
8992
case 'float64':
9093
if (typeof obj[name] != 'number') {
9194
return false;
9295
}
9396
break;
97+
case 'int64':
98+
case 'uint64':
99+
if (typeof obj[name] != 'bigint') {
100+
return false;
101+
}
94102
case 'bool':
95103
if (typeof obj[name] != 'boolean') {
96104
return false;
@@ -171,7 +179,12 @@ function toROSMessage(TypeClass, obj) {
171179

172180
function constructFromPlanObject(msg, obj) {
173181
const type = typeof obj;
174-
if (type === 'string' || type === 'number' || type === 'boolean') {
182+
if (
183+
type === 'string' ||
184+
type === 'number' ||
185+
type === 'boolean' ||
186+
type === 'bigint'
187+
) {
175188
msg.data = obj;
176189
} else if (type === 'object') {
177190
copyMsgObject(msg, obj);

rosidl_gen/packages.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@ async function generateMsgForSrv(filePath, interfaceInfo, pkgMap) {
161161
async function addInterfaceInfos(filePath, dir, pkgMap) {
162162
const interfaceInfo = grabInterfaceInfo(filePath, true);
163163
const ignore = pkgFilters.matchesAny(interfaceInfo);
164-
if (ignore) {
165-
console.log('Omitting filtered interface: ', interfaceInfo);
166-
} else {
164+
if (!ignore) {
167165
if (path.extname(filePath) === '.msg') {
168166
// Some .msg files were generated prior to 0.3.2 for .action files,
169167
// which has been disabled. So these files should be ignored here.
@@ -232,9 +230,7 @@ async function findPackagesInDirectory(dir) {
232230
amentExecuted
233231
);
234232
const ignore = pkgFilters.matchesAny(interfaceInfo);
235-
if (ignore) {
236-
console.log('Omitting filtered interface: ', interfaceInfo);
237-
} else {
233+
if (!ignore) {
238234
if (path.extname(file.name) === '.msg') {
239235
// Some .msg files were generated prior to 0.3.2 for .action files,
240236
// which has been disabled. So these files should be ignored here.

rosidl_gen/templates/message.dot

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ function isTypedArrayType(type) {
157157
return typedArrayType.indexOf(type.type.toLowerCase()) !== -1;
158158
}
159159

160+
function isBigInt(type) {
161+
return ['int64', 'uint64'].indexOf(type.type.toLowerCase()) !== -1;
162+
}
163+
160164
const willUseTypedArray = isTypedArrayType(it.spec.baseType);
161165
const currentTypedArray = getTypedArrayName(it.spec.baseType);
162166
const currentTypedArrayElementType = getTypedArrayElementName(it.spec.baseType);
@@ -303,7 +307,11 @@ class {{=objectWrapper}} {
303307
this._refObject.{{=field.name}} = {{=field.default_value}};
304308
{{?}}
305309
{{?? field.type.isPrimitiveType && !isTypedArrayType(field.type) && field.default_value}}
306-
this._{{=field.name}}Array = {{=JSON.stringify(field.default_value)}};
310+
{{? isBigInt(field.type)}}
311+
this._{{=field.name}}Array = {{=JSON.stringify(field.default_value)}}.map(num => BigInt(num));
312+
{{??}}
313+
this._{{=field.name}}Array = {{=JSON.stringify(field.default_value)}};
314+
{{?}}
307315
{{?? field.type.isPrimitiveType && isTypedArrayType(field.type) && field.default_value}}
308316
this._wrapperFields.{{=field.name}}.fill({{=getTypedArrayName(field.type)}}.from({{=JSON.stringify(field.default_value)}}));
309317
{{?}}
@@ -376,8 +384,11 @@ class {{=objectWrapper}} {
376384
}
377385
}
378386
}
387+
{{?? isBigInt(field.type)}}
388+
{{/* For non-TypedArray like int64/uint64. */}}
389+
this._refObject.{{=field.name}} = this._{{=field.name}}Array.map(num => num.toString());
379390
{{??}}
380-
{{/* For non-TypedArray like int64/uint64/bool. */}}
391+
{{/* For non-TypedArray like bool. */}}
381392
this._refObject.{{=field.name}} = this._{{=field.name}}Array;
382393
{{?}}
383394
{{?? field.type.isArray && field.type.isPrimitiveType && isTypedArrayType(field.type) && field.type.isFixedSizeArray}}
@@ -527,6 +538,8 @@ class {{=objectWrapper}} {
527538
return this._wrapperFields.{{=field.name}};
528539
{{?? !field.type.isArray && field.type.type === 'string' && it.spec.msgName !== 'String'}}
529540
return this._wrapperFields.{{=field.name}}.data;
541+
{{?? isBigInt(field.type)}}
542+
return BigInt(this._refObject.{{=field.name}});
530543
{{??}}
531544
return this._refObject.{{=field.name}};
532545
{{?}}
@@ -559,6 +572,16 @@ class {{=objectWrapper}} {
559572
}
560573
{{?? !field.type.isArray && field.type.type === 'string' && it.spec.msgName !== 'String'}}
561574
this._wrapperFields.{{=field.name}}.data = value;
575+
{{?? isBigInt(field.type)}}
576+
if (typeof value !== "bigint") {
577+
throw new TypeError('Must be type of bigint');
578+
}
579+
this._refObject.{{=field.name}} = value.toString();
580+
/*if (typeof value === "bigint") {
581+
this._refObject.{{=field.name}} = value.toString();
582+
} else {
583+
this._refObject.{{=field.name}} = value
584+
}*/
562585
{{??}}
563586
{{? it.spec.msgName === 'String'}}
564587
this._refObject.size = Buffer.byteLength(value);

rosidl_parser/rosidl_parser.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414

1515
'use strict';
1616

17+
const compareVersions = require('compare-versions');
1718
const path = require('path');
1819
const execFile = require('child_process').execFile;
1920

2021
const pythonExecutable = require('./py_utils').getPythonExecutable('python3');
2122

23+
const version = process.version;
24+
const isContextSupported = compareVersions.compare(
25+
version.substring(1, version.length),
26+
'21.0.0.0',
27+
'>='
28+
);
29+
2230
const rosidlParser = {
2331
parseMessageFile(packageName, filePath) {
2432
return this._parseFile('parse_message_file', packageName, filePath);
@@ -32,6 +40,22 @@ const rosidlParser = {
3240
return this._parseFile('parse_action_file', packageName, filePath);
3341
},
3442

43+
_parseJSONObject(str) {
44+
if (isContextSupported) {
45+
return JSON.parse(str, (key, value, context) => {
46+
if (
47+
Number.isInteger(value) &&
48+
!Number.isSafeInteger(Number(context.source))
49+
) {
50+
return context.source;
51+
}
52+
return value;
53+
});
54+
}
55+
const JSONbigString = require('json-bigint')({ storeAsString: true });
56+
return JSONbigString.parse(str);
57+
},
58+
3559
_parseFile(command, packageName, filePath) {
3660
return new Promise((resolve, reject) => {
3761
const args = [
@@ -54,7 +78,7 @@ const rosidlParser = {
5478
)
5579
);
5680
} else {
57-
resolve(JSON.parse(stdout));
81+
resolve(this._parseJSONObject(stdout));
5882
}
5983
}
6084
);

rostsd_gen/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,6 @@ function primitiveType2JSName(type) {
478478
case 'int8':
479479
case 'int16':
480480
case 'int32':
481-
case 'int64':
482481

483482
// signed explicit float types
484483
case 'float32':
@@ -488,7 +487,6 @@ function primitiveType2JSName(type) {
488487
case 'uint8':
489488
case 'uint16':
490489
case 'uint32':
491-
case 'uint64':
492490
jsName = 'number';
493491
break;
494492
case 'bool':
@@ -499,6 +497,10 @@ function primitiveType2JSName(type) {
499497
case 'wstring':
500498
jsName = 'string';
501499
break;
500+
case 'int64':
501+
case 'uint64':
502+
jsName = 'bigint';
503+
break;
502504
}
503505

504506
return jsName;

test/client_setup.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
'use strict';
1616

17+
const assert = require('assert');
1718
const rclnodejs = require('../index.js');
1819

1920
rclnodejs
@@ -24,13 +25,15 @@ rclnodejs
2425
const Int8 = 'std_msgs/msg/Int8';
2526
var client = node.createClient(AddTwoInts, 'add_two_ints');
2627
const request = {
27-
a: 1,
28-
b: 2,
28+
a: 1n,
29+
b: 2n,
2930
};
3031
var publisher = node.createPublisher(Int8, 'back_add_two_ints');
3132
client.waitForService().then(() => {
3233
client.sendRequest(request, (response) => {
33-
publisher.publish(response.sum);
34+
// Conver `response.sum` from bigint to number.
35+
assert.equal(typeof response.sum, 'bigint');
36+
publisher.publish(Number(response.sum));
3437
});
3538
});
3639

test/publisher_msg.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ const rclnodejs = require('../index.js');
1919
var rclType = process.argv[2];
2020
var rclValue = eval(process.argv[3]);
2121

22+
if (['int64', 'uint64'].indexOf(rclType.toLowerCase()) !== -1) {
23+
rclValue = BigInt(rclValue);
24+
0;
25+
}
26+
2227
rclnodejs
2328
.init()
2429
.then(() => {

0 commit comments

Comments
 (0)