You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.2 KiB
318 lines
9.2 KiB
'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); |
|
} |
|
} |
|
} |
|
};
|
|
|