UNPKG

amqp

Version:
319 lines (270 loc) 9.41 kB
'use strict'; var jspack = require('../jspack').jspack; var serializer = module.exports = { serializeFloat: function(b, size, value, bigEndian) { var jp = new jspack(bigEndian); switch(size) { case 4: var x = jp.Pack('f', [value]); for (var i = 0; i < x.length; ++i) b[b.used++] = x[i]; break; case 8: var x = jp.Pack('d', [value]); for (var i = 0; i < x.length; ++i) b[b.used++] = x[i]; break; default: throw new Error("Unknown floating point size"); } }, serializeInt: function (b, size, int) { if (b.used + size > b.length) { throw new Error("write out of bounds"); } // Only 4 cases - just going to be explicit instead of looping. switch (size) { // octet case 1: b[b.used++] = int; break; // short case 2: b[b.used++] = (int & 0xFF00) >> 8; b[b.used++] = (int & 0x00FF) >> 0; break; // long case 4: b[b.used++] = (int & 0xFF000000) >> 24; b[b.used++] = (int & 0x00FF0000) >> 16; b[b.used++] = (int & 0x0000FF00) >> 8; b[b.used++] = (int & 0x000000FF) >> 0; break; // long long case 8: b[b.used++] = (int & 0xFF00000000000000) >> 56; b[b.used++] = (int & 0x00FF000000000000) >> 48; b[b.used++] = (int & 0x0000FF0000000000) >> 40; b[b.used++] = (int & 0x000000FF00000000) >> 32; b[b.used++] = (int & 0x00000000FF000000) >> 24; b[b.used++] = (int & 0x0000000000FF0000) >> 16; b[b.used++] = (int & 0x000000000000FF00) >> 8; b[b.used++] = (int & 0x00000000000000FF) >> 0; break; default: throw new Error("Bad size"); } }, serializeShortString: function (b, string) { if (typeof(string) != "string") { throw new Error("param must be a string"); } var byteLength = Buffer.byteLength(string, 'utf8'); if (byteLength > 0xFF) { throw new Error("String too long for 'shortstr' parameter"); } if (1 + byteLength + b.used >= b.length) { throw new Error("Not enough space in buffer for 'shortstr'"); } b[b.used++] = byteLength; b.write(string, b.used, 'utf8'); b.used += byteLength; }, serializeLongString: function(b, string) { // we accept string, object, or buffer for this parameter. // in the case of string we serialize it to utf8. if (typeof(string) == 'string') { var byteLength = Buffer.byteLength(string, 'utf8'); serializer.serializeInt(b, 4, byteLength); b.write(string, b.used, 'utf8'); b.used += byteLength; } else if (typeof(string) == 'object') { serializer.serializeTable(b, string); } else { // data is Buffer var byteLength = string.length; serializer.serializeInt(b, 4, byteLength); b.write(string, b.used); // memcpy b.used += byteLength; } }, serializeDate: function(b, date) { serializer.serializeInt(b, 8, date.valueOf() / 1000); }, serializeBuffer: function(b, buffer) { serializer.serializeInt(b, 4, buffer.length); buffer.copy(b, b.used, 0); b.used += buffer.length; }, serializeBase64: function(b, buffer) { serializer.serializeLongString(b, buffer.toString('base64')); }, isBigInt: function(value) { return value > 0xffffffff; }, getCode: function(dec) { var hexArray = "0123456789ABCDEF".split(''); var code1 = Math.floor(dec / 16); var code2 = dec - code1 * 16; return hexArray[code2]; }, isFloat: function(value){ return value === +value && value !== (value|0); }, serializeValue: function(b, value) { switch (typeof(value)) { case 'string': b[b.used++] = 'S'.charCodeAt(0); serializer.serializeLongString(b, value); break; case 'number': if (!serializer.isFloat(value)) { if (serializer.isBigInt(value)) { // 64-bit uint b[b.used++] = 'l'.charCodeAt(0); serializer.serializeInt(b, 8, value); } else { //32-bit uint b[b.used++] = 'I'.charCodeAt(0); serializer.serializeInt(b, 4, value); } } else { //64-bit float b[b.used++] = 'd'.charCodeAt(0); serializer.serializeFloat(b, 8, value); } break; case 'boolean': b[b.used++] = 't'.charCodeAt(0); b[b.used++] = value; break; default: if (value instanceof Date) { b[b.used++] = 'T'.charCodeAt(0); serializer.serializeDate(b, value); } else if (value instanceof Buffer) { b[b.used++] = 'x'.charCodeAt(0); serializer.serializeBuffer(b, value); } else if (Array.isArray(value)) { b[b.used++] = 'A'.charCodeAt(0); serializer.serializeArray(b, value); } else if (typeof(value) === 'object') { b[b.used++] = 'F'.charCodeAt(0); serializer.serializeTable(b, value); } else { throw new Error("unsupported type in amqp table: " + typeof(value)); } } }, serializeTable: function(b, object) { if (typeof(object) != "object") { throw new Error("param must be an object"); } // Save our position so that we can go back and write the length of this table // at the beginning of the packet (once we know how many entries there are). var lengthIndex = b.used; b.used += 4; // sizeof long var startIndex = b.used; for (var key in object) { if (!object.hasOwnProperty(key)) continue; serializer.serializeShortString(b, key); serializer.serializeValue(b, object[key]); } var endIndex = b.used; b.used = lengthIndex; serializer.serializeInt(b, 4, endIndex - startIndex); b.used = endIndex; }, serializeArray: function(b, arr) { // Save our position so that we can go back and write the byte length of this array // at the beginning of the packet (once we have serialized all elements). var lengthIndex = b.used; b.used += 4; // sizeof long var startIndex = b.used; var len = arr.length; for (var i = 0; i < len; i++) { serializer.serializeValue(b, arr[i]); } var endIndex = b.used; b.used = lengthIndex; serializer.serializeInt(b, 4, endIndex - startIndex); b.used = endIndex; }, serializeFields: function(buffer, fields, args, strict) { var bitField = 0; var bitIndex = 0; for (var i = 0; i < fields.length; i++) { var field = fields[i]; var domain = field.domain; if (!(field.name in args)) { if (strict) { throw new Error("Missing field '" + field.name + "' of type '" + domain + "' while executing AMQP method '" + arguments.callee.caller.arguments[1].name + "'"); } continue; } var param = args[field.name]; //debug("domain: " + domain + " param: " + param); switch (domain) { case 'bit': if (typeof(param) != "boolean") { throw new Error("Unmatched field " + JSON.stringify(field)); } if (param) bitField |= (1 << bitIndex); bitIndex++; if (!fields[i+1] || fields[i+1].domain != 'bit') { //debug('SET bit field ' + field.name + ' 0x' + bitField.toString(16)); buffer[buffer.used++] = bitField; bitField = 0; bitIndex = 0; } break; case 'octet': if (typeof(param) != "number" || param > 0xFF) { throw new Error("Unmatched field " + JSON.stringify(field)); } buffer[buffer.used++] = param; break; case 'short': if (typeof(param) != "number" || param > 0xFFFF) { throw new Error("Unmatched field " + JSON.stringify(field)); } serializer.serializeInt(buffer, 2, param); break; case 'long': if (typeof(param) != "number" || param > 0xFFFFFFFF) { throw new Error("Unmatched field " + JSON.stringify(field)); } serializer.serializeInt(buffer, 4, param); break; // In a previous version this shared code with 'longlong', which caused problems when passed Date // integers. Nobody expects to pass a Buffer here, 53 bits is still 28 million years after 1970, we'll be fine. case 'timestamp': serializer.serializeInt(buffer, 8, param); break; case 'longlong': for (var j = 0; j < 8; j++) { buffer[buffer.used++] = param[j]; } break; case 'shortstr': if (typeof(param) != "string" || param.length > 0xFF) { throw new Error("Unmatched field " + JSON.stringify(field)); } serializer.serializeShortString(buffer, param); break; case 'longstr': serializer.serializeLongString(buffer, param); break; case 'table': if (typeof(param) != "object") { throw new Error("Unmatched field " + JSON.stringify(field)); } serializer.serializeTable(buffer, param); break; default: throw new Error("Unknown domain value type " + domain); } } } };