function parseHead(buffer) { // Parses and returns the first type on the buffer and the total bytes consumed var type = bops.readUInt8(buffer, 0); // Nullary if (~nullaryTypes.indexOf(type)) return [ decode(bops.from([ type ])), 1 ]; // Fixed var size = fixedTypes[type]; if (size++) return [ decode(bops.subarray(buffer, 0, size)), size ]; // Flat var index; var end; if (~flatTypes.indexOf(type)) { // Find end byte for (index = 1, end = buffer.length; index < end; ++index) { if (bops.readUInt8(buffer, index) === 0x00) break; } if (index >= buffer.length) throw new Error('No ending byte found for list'); var chunk = flatUnescape(bops.subarray(buffer, 0, index)); // Add 1 to index to skip over end byte return [ decode(chunk), index + 1 ]; } // Nested, recurse for each item var list = []; index = 1; var next; while ((next = bops.readUInt8(buffer, index)) !== 0) { var result = parseHead(bops.subarray(buffer, index)); list.push(bops.readUInt8(result, 0)); index += bops.readUInt8(result, 1); if (index >= buffer.length) throw new Error('No ending byte found for nested list'); } return [ structure(type, list), index + 1 ]; }
// TODO expose in public API function flatUnescape(buffer) { var b, bytes = []; // Don't escape last byte for (var i = 0, end = buffer.length; i < end; ++i) { b = bops.readUInt8(buffer, i); // If low-byte escape tag use the following byte minus 1 if (b === 0x01) bytes.push(bops.readUInt8(buffer, ++i) - 1); // If high-byte escape tag use the following byte plus 1 else if (b === 0xfe) bytes.push(bops.readUInt8(buffer, ++i) + 1); // Otherwise no unescapement needed else bytes.push(b); } return bops.from(bytes); }
function invert(buffer) { var bytes = []; for (var i = 0, end = buffer.length; i < end; ++i) { bytes.push(~bops.readUInt8(buffer, i)); } return bops.from(bytes); }
parser.header = function(buf, packet) { var zero = bops.readUInt8(buf, 0); packet.cmd = protocol.types[zero >> protocol.CMD_SHIFT]; packet.retain = (zero & protocol.RETAIN_MASK) !== 0; packet.qos = (zero >> protocol.QOS_SHIFT) & protocol.QOS_MASK; packet.dup = (zero & protocol.DUP_MASK) !== 0; return packet; };
function decode(buffer) { var type = bops.readUInt8(buffer, 0); // Nullary types if (~nullaryTypes.indexOf(type)) { if (buffer.length !== 1) throw new Error('Invalid encoding in buffer: ' + buffer); if (type === UNDEFINED) return; if (type === NULL) return null; if (type === FALSE) return false; if (type === TRUE) return true; if (type === NEGATIVE_INFINITY) return Number.NEGATIVE_INFINITY; if (type === POSITIVE_INFINITY) return Number.POSITIVE_INFINITY; } // Fixed size types var chunk = bops.subarray(buffer, 1); var chunkSize = fixedTypes[type]; if (chunkSize) { if (chunk.length !== chunkSize) throw new Error('Invalid size for buffer: ' + buffer); if (type === NEGATIVE_NUMBER || type === POSITIVE_NUMBER) { return decodeNumber(chunk, type === NEGATIVE_NUMBER); } if (type === DATE_PRE_EPOCH || type === DATE_POST_EPOCH) { return new Date(decodeNumber(chunk, type === DATE_PRE_EPOCH)); } } // Flat types if (type === BUFFER) return chunk; if (type === STRING) return bops.to(chunk, 'utf8'); // Structured types if (~structuredTypes.indexOf(type)) { var result = parseHead(buffer); if (bops.readUInt8(result, 1) !== buffer.length) { throw new Error('List deserialization fail: ' + bops.readUInt8(result, 1) + '!=' + bops.length(buffer)); } return bops.readUInt8(result, 0); } }
test('from array works', function(assert) { var arr = [1, 2, 3] , buf = bops.from(arr) assert.equal(buf.length, arr.length) for(var i = 0, len = arr.length; i < len; ++i) { assert.equal(bops.readUInt8(buf, i), arr[i]) } assert.end() })
test('from base64 works as expected', function(assert) { var buf = bops.from('aGVsbG8gd29ybGTGkgAKCAk=', 'base64') , expect expect = [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 198, 146, 0, 10, 8, 9] assert.equal(buf.length, expect.length) for(var i = 0, len = buf.length; i < len; ++i) { assert.equal(bops.readUInt8(buf, i), expect[i]) } assert.end() })
test('from hex works as expected', function(assert) { var buf = bops.from('68656c6c6f20776f726c64c692000a0809', 'hex') , expect expect = [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 198, 146, 0, 10, 8, 9] assert.equal(buf.length, expect.length) for(var i = 0, len = buf.length; i < len; ++i) { assert.equal(bops.readUInt8(buf, i), expect[i]) } assert.end() })
test('from utf8 works as expected', function(assert) { var buf = bops.from('ƒello 淾淾淾 hello world 淾淾 yep ƒuu 淾 \ud83d\ude04', 'utf8') , expect expect = [198,146,101,108,108,111,32,230,183,190,230,183,190,230,183,190,32,104,101,108,108,111,32,119,111,114,108,100,32,230,183,190,230,183,190,32,121,101,112,32,198,146,117,117,32,230,183,190,32,0xF0,0x9F,0x98,0x84] assert.equal(buf.length, expect.length) for(var i = 0, len = buf.length; i < len; ++i) { assert.equal(bops.readUInt8(buf, i), expect[i]) } assert.end() })
Connection.prototype._parseLength = function() { var result = true, data = this.data, readByte; if (this.packet.length === undefined) { if (data.length <= this.index) { result = false; } else { readByte = bops.readUInt8(data, this.index++); while (this.tmp.pos++ < 4) { this.tmp.length += this.tmp.mul * (readByte & protocol.LENGTH_MASK); this.tmp.mul *= 0x80; if ((readByte & protocol.LENGTH_FIN_MASK) === 0) { break; } if (data.length <= this.index) { result = false; break; } readByte = bops.readUInt8(data, this.index++); } if (result) { this.packet.length = this.tmp.length; } } } return result; };
function encodeList(items) { // TODO pass around a map of references already encoded to detect cycles var buffers = []; var chunk; for (var i = 0, end = items.length; i < end; ++i) { chunk = encode(items[i]); var type = bops.readUInt8(chunk, 0); // We need to escape a few bytes in string and buffer types to prevent confusion with the end byte if (~flatTypes.indexOf(type)) chunk = flatEscape(chunk); buffers.push(chunk); } // Close the list with an end byte buffers.push(bops.create([ 0 ])); return bops.join(buffers); }
parser.connect = function(buf, packet) { parser._pos = 0; parser._len = buf.length; var protocolId // Protocol id , clientId // Client id , topic // Will topic , payload // Will payload , password // Password , username // Username , flags = {}; // Parse protocol id protocolId = parser.parse_string(buf); if (protocolId === null) return new Error('Parse error - cannot parse protocol id'); packet.protocolId = protocolId; // Parse protocol version number if(parser._pos > parser._len) return null; packet.protocolVersion = bops.readUInt8(buf, parser._pos); parser._pos += 1; // Parse connect flags flags.username = (bops.readUInt8(buf, parser._pos) & protocol.USERNAME_MASK); flags.password = (bops.readUInt8(buf, parser._pos) & protocol.PASSWORD_MASK); flags.will = (bops.readUInt8(buf, parser._pos) & protocol.WILL_FLAG_MASK); if(flags.will) { packet.will = {}; packet.will.retain = (bops.readUInt8(buf, parser._pos) & protocol.WILL_RETAIN_MASK) !== 0; packet.will.qos = (bops.readUInt8(buf, parser._pos) & protocol.WILL_QOS_MASK) >> protocol.WILL_QOS_SHIFT; } packet.clean = (bops.readUInt8(buf, parser._pos) & protocol.CLEAN_SESSION_MASK) !== 0; parser._pos += 1; // Parse keepalive packet.keepalive = this.parse_num(buf); if(packet.keepalive === null) return null; // Parse client ID clientId = this.parse_string(buf); if(clientId === null) return new Error('Parse error - cannot parse client id'); packet.clientId = clientId; if(flags.will) { // Parse will topic topic = this.parse_string(buf); if(topic === null) return new Error('Parse error - cannot parse will topic'); packet.will.topic = topic; // Parse will payload payload = this.parse_string(buf); if(payload === null) return new Error('Parse error - cannot parse will payload'); packet.will.payload = payload; } // Parse username if(flags.username) { username = this.parse_string(buf); if(username === null) return new Error('Parse error - cannot parse username'); packet.username = username; } // Parse password if(flags.password) { password = this.parse_string(buf); if(password === null) return ; packet.password = password; } return packet; };
Decoder.prototype.parse = function () { var type = this.buffer[this.offset]; var value, length, extType; // Positive FixInt if ((type & 0x80) === 0x00) { this.offset++; return type; } // FixMap if ((type & 0xf0) === 0x80) { length = type & 0x0f; this.offset++; return this.map(length); } // FixArray if ((type & 0xf0) === 0x90) { length = type & 0x0f; this.offset++; return this.array(length); } // FixStr if ((type & 0xe0) === 0xa0) { length = type & 0x1f; this.offset++; return this.str(length); } // Negative FixInt if ((type & 0xe0) === 0xe0) { value = bops.readInt8(this.buffer, this.offset); this.offset++; return value; } switch (type) { // nil case 0xc0: this.offset++; return null; // 0xc1: (never used) // false case 0xc2: this.offset++; return false; // true case 0xc3: this.offset++; return true; // bin 8 case 0xc4: length = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return this.bin(length); // bin 16 case 0xc5: length = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return this.bin(length); // bin 32 case 0xc6: length = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return this.bin(length); // ext 8 case 0xc7: length = bops.readUInt8(this.buffer, this.offset + 1); extType = bops.readUInt8(this.buffer, this.offset + 2); this.offset += 3; return [extType, this.bin(length)]; // ext 16 case 0xc8: length = bops.readUInt16BE(this.buffer, this.offset + 1); extType = bops.readUInt8(this.buffer, this.offset + 3); this.offset += 4; return [extType, this.bin(length)]; // ext 32 case 0xc9: length = bops.readUInt32BE(this.buffer, this.offset + 1); extType = bops.readUInt8(this.buffer, this.offset + 5); this.offset += 6; return [extType, this.bin(length)]; // float 32 case 0xca: value = bops.readFloatBE(this.buffer, this.offset + 1); this.offset += 5; return value; // float 64 / double case 0xcb: value = bops.readDoubleBE(this.buffer, this.offset + 1); this.offset += 9; return value; // uint8 case 0xcc: value = this.buffer[this.offset + 1]; this.offset += 2; return value; // uint 16 case 0xcd: value = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return value; // uint 32 case 0xce: value = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return value; // uint64 case 0xcf: value = bops.readUInt64BE(this.buffer, this.offset + 1); this.offset += 9; return value; // int 8 case 0xd0: value = bops.readInt8(this.buffer, this.offset + 1); this.offset += 2; return value; // int 16 case 0xd1: value = bops.readInt16BE(this.buffer, this.offset + 1); this.offset += 3; return value; // int 32 case 0xd2: value = bops.readInt32BE(this.buffer, this.offset + 1); this.offset += 5; return value; // int 64 case 0xd3: value = bops.readInt64BE(this.buffer, this.offset + 1); this.offset += 9; return value; // fixext 1 / undefined case 0xd4: extType = bops.readUInt8(this.buffer, this.offset + 1); value = bops.readUInt8(this.buffer, this.offset + 2); this.offset += 3; return (extType === 0 && value === 0) ? undefined : [extType, value]; // fixext 2 case 0xd5: extType = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return [extType, this.bin(2)]; // fixext 4 case 0xd6: extType = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return [extType, this.bin(4)]; // fixext 8 case 0xd7: extType = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return [extType, this.bin(8)]; // fixext 16 case 0xd8: extType = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return [extType, this.bin(16)]; // str 8 case 0xd9: length = bops.readUInt8(this.buffer, this.offset + 1); this.offset += 2; return this.str(length); // str 16 case 0xda: length = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return this.str(length); // str 32 case 0xdb: length = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return this.str(length); // array 16 case 0xdc: length = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return this.array(length); // array 32 case 0xdd: length = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return this.array(length); // map 16: case 0xde: length = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return this.map(length); // map 32 case 0xdf: length = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return this.map(length); // buffer 16 case 0xd8: length = bops.readUInt16BE(this.buffer, this.offset + 1); this.offset += 3; return this.buf(length); // buffer 32 case 0xd9: length = bops.readUInt32BE(this.buffer, this.offset + 1); this.offset += 5; return this.buf(length); } throw new Error("Unknown type 0x" + type.toString(16)); };
var bytesToNumberBE = function (bytes) { var acc = 0; for (var i = 0; i < bytes.length; i++) acc = (acc << 8) + bops.readUInt8(bytes, i); return acc; };
var bytesToNumberLE = function (bytes) { var acc = 0; for (var i = 0; i < bytes.length; i++) acc += bops.readUInt8(bytes, i) << (8*i); return acc; };