Skip to content

Commit

Permalink
Add support for CBOR encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollrath committed Nov 6, 2018
1 parent 35e7474 commit b06486b
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 4 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"pngparse": "^2.0.1",
"ws": "^1.1.1",
"xmldom": "^0.1.19",
"cbor-js": "^0.1.0",
"socket.io": "1.4.8"
},
"directories": {
Expand Down
4 changes: 3 additions & 1 deletion src/core/Ros.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ Ros.prototype.connect = function(url) {
} else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') {
this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this));
}else {
this.socket = assign(new WebSocket(url), socketAdapter(this));
var sock = new WebSocket(url);
sock.binaryType = 'arraybuffer';
this.socket = assign(sock, socketAdapter(this));
}

};
Expand Down
5 changes: 5 additions & 0 deletions src/core/SocketAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
'use strict';

var decompressPng = require('../util/decompressPng');
var CBOR = require('cbor-js');
var typedArrayTagger = require('../util/cborTypedArrayTags');
var WebSocket = require('ws');
var BSON = null;
if(typeof bson !== 'undefined'){
Expand Down Expand Up @@ -106,6 +108,9 @@ function SocketAdapter(client) {
decodeBSON(data.data, function (message) {
handlePng(message, handleMessage);
});
} else if (data.data instanceof ArrayBuffer) {
var decoded = CBOR.decode(data.data, typedArrayTagger);
handleMessage(decoded);
} else {
var message = JSON.parse(typeof data === 'string' ? data : data.data);
handlePng(message, handleMessage);
Expand Down
4 changes: 2 additions & 2 deletions src/core/Topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var Message = require('./Message');
* * ros - the ROSLIB.Ros connection handle
* * name - the topic name, like /cmd_vel
* * messageType - the message type, like 'std_msgs/String'
* * compression - the type of compression to use, like 'png'
* * compression - the type of compression to use, like 'png' or 'cbor'
* * throttle_rate - the rate (in ms in between messages) at which to throttle the topics
* * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100)
* * latch - latch the topic when publishing
Expand All @@ -40,7 +40,7 @@ function Topic(options) {

// Check for valid compression types
if (this.compression && this.compression !== 'png' &&
this.compression !== 'none') {
this.compression !== 'cbor' && this.compression !== 'none') {
this.emit('warning', this.compression +
' compression is not supported. No compression will be used.');
}
Expand Down
113 changes: 113 additions & 0 deletions src/util/cborTypedArrayTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
var UPPER32 = Math.pow(2, 32);

var warnedPrecision = false;
function warnPrecision() {
if (!warnedPrecision) {
warnedPrecision = true;
console.warn('CBOR 64-bit integer array values may lose precision. No further warnings.');
}
}

/**
* Unpacks 64-bit unsigned integer from byte array.
* @param {Uint8Array} bytes
*/
function decodeUint64LE(bytes) {
warnPrecision();

var byteLen = bytes.byteLength;
var arrLen = byteLen / 8;

var buffer = bytes.buffer.slice(-byteLen);
var uint32View = new Uint32Array(buffer);

var arr = new Array(arrLen);
for (var i = 0; i < arrLen; i++) {
var si = i * 2;
var lo = uint32View[si];
var hi = uint32View[si+1];
arr[i] = lo + UPPER32 * hi;
}

return arr;
}

/**
* Unpacks 64-bit signed integer from byte array.
* @param {Uint8Array} bytes
*/
function decodeInt64LE(bytes) {
warnPrecision();

var byteLen = bytes.byteLength;
var arrLen = byteLen / 8;

var buffer = bytes.buffer.slice(-byteLen);
var uint32View = new Uint32Array(buffer);
var int32View = new Int32Array(buffer);

var arr = new Array(arrLen);
for (var i = 0; i < arrLen; i++) {
var si = i * 2;
var lo = uint32View[si];
var hi = int32View[si+1];
arr[i] = lo + UPPER32 * hi;
}

return arr;
}

/**
* Unpacks typed array from byte array.
* @param {Uint8Array} bytes
* @param {type} ArrayType - desired output array type
*/
function decodeNativeArray(bytes, ArrayType) {
var byteLen = bytes.byteLength;
var buffer = bytes.buffer.slice(-byteLen);
return new ArrayType(buffer);
}

/**
* Support a subset of draft CBOR typed array tags:
* <https://tools.ietf.org/html/draft-ietf-cbor-array-tags-00>
* Only support little-endian tags for now.
*/
var nativeArrayTypes = {
64: Uint8Array,
69: Uint16Array,
70: Uint32Array,
72: Int8Array,
77: Int16Array,
78: Int32Array,
85: Float32Array,
86: Float64Array
};

/**
* We can also decode 64-bit integer arrays, since ROS has these types.
*/
var conversionArrayTypes = {
71: decodeUint64LE,
79: decodeInt64LE
};

/**
* Handles CBOR typed array tags during decoding.
* @param {Uint8Array} data
* @param {Number} tag
*/
function cborTypedArrayTagger(data, tag) {
if (tag in nativeArrayTypes) {
var arrayType = nativeArrayTypes[tag];
return decodeNativeArray(data, arrayType);
}
if (tag in conversionArrayTypes) {
return conversionArrayTypes[tag](data);
}
return data;
}

if (typeof module !== 'undefined' && module.exports) {
module.exports = cborTypedArrayTagger;
}
115 changes: 115 additions & 0 deletions test/cbor.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
var expect = require('chai').expect;
var CBOR = require('cbor-js');
var cborTypedArrayTagger = require('../src/util/cborTypedArrayTags.js');

/** Convert hex string to ArrayBuffer. */
function hexToBuffer(hex) {
var tokens = hex.match(/[0-9a-fA-F]{2}/gi);
var arr = tokens.map(function(t) {
return parseInt(t, 16);
});
return new Uint8Array(arr).buffer;
}


describe('CBOR Typed Array Tagger', function() {

it('should convert tagged Uint16Array', function() {
var data = hexToBuffer('d84546010002000300');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Uint16Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Uint32Array', function() {
var data = hexToBuffer('d8464c010000000200000003000000');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Uint32Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Uint64Array', function() {
var data = hexToBuffer('d8475818010000000000000002000000000000000300000000000000');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Int8Array', function() {
var data = hexToBuffer('d8484301fe03');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Int8Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(-2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Int16Array', function() {
var data = hexToBuffer('d84d460100feff0300');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Int16Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(-2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Int32Array', function() {
var data = hexToBuffer('d84e4c01000000feffffff03000000');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Int32Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(-2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Int64Array', function() {
var data = hexToBuffer('d84f58180100000000000000feffffffffffffff0300000000000000');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.equal(1);
expect(msg[1]).to.equal(-2);
expect(msg[2]).to.equal(3);
});

it('should convert tagged Float32Array', function() {
var data = hexToBuffer('d8554ccdcc8c3fcdcc0cc033335340');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Float32Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.closeTo(1.1, 1e-5);
expect(msg[1]).to.closeTo(-2.2, 1e-5);
expect(msg[2]).to.closeTo(3.3, 1e-5);
});

it('should convert tagged Float64Array', function() {
var data = hexToBuffer('d85658189a9999999999f13f9a999999999901c06666666666660a40');
var msg = CBOR.decode(data, cborTypedArrayTagger);

expect(msg).to.be.a('Float64Array');
expect(msg).to.have.lengthOf(3);
expect(msg[0]).to.closeTo(1.1, 1e-5);
expect(msg[1]).to.closeTo(-2.2, 1e-5);
expect(msg[2]).to.closeTo(3.3, 1e-5);
});
});
2 changes: 2 additions & 0 deletions test/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ module.exports = function(config) {
// List of files / patterns to load in the browser
files: [
'../node_modules/eventemitter2/lib/eventemitter2.js',
'../node_modules/cbor-js/cbor.js',
'../src/util/cborTypedArrayTags.js',
'../build/roslib.js',
'./require-shim.js',
'*.test.js'
Expand Down
4 changes: 3 additions & 1 deletion test/require-shim.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
window.require = function require(path) {
switch (path) {
case 'eventemitter2': return EventEmitter2;
case 'xmldom': return {DOMParser: DOMParser}
case 'xmldom': return {DOMParser: DOMParser};
case 'cbor-js': return CBOR;
case '../src/util/cborTypedArrayTags.js': return cborTypedArrayTagger;
}
var lastIdx = path.lastIndexOf('/'),
path = lastIdx >= 0 ? path.slice(lastIdx + 1) : path;
Expand Down

0 comments on commit b06486b

Please sign in to comment.