'use strict'; function ArrayBufferSer(type, data, options) { if(type === undefined) { throw "ArrayBufferSer should be created with a type"; } this.options = options || {}; this._initFunctions(); this.headerSize = 8; this.data = data; this.type = type = Math.min(type, ArrayBufferSer.BUFFER); var size = this._sizeFor(this.headerSize, data); this.buffer = new Buffer(this.headerSize + size); this.buffer.writeUInt32BE(type, 0); // this could be one byte but for byte padding is better to be 4 bytes this.buffer.writeUInt32BE(size, 4); this.offset = this.headerSize; var w = this.writeFn[type]; var i; if(!this.options.delta) { for(i = 0; i < data.length; ++i) { this[w](data[i]); } } else { this[w](data[0]); for(i = 1; i < data.length; ++i) { this[w](data[i] - data[i - 1]); } } } // // constants // ArrayBufferSer.INT8 = 1; ArrayBufferSer.UINT8 = 2; ArrayBufferSer.UINT8_CLAMP = 3; ArrayBufferSer.INT16 = 4; ArrayBufferSer.UINT16 = 5; ArrayBufferSer.INT32 = 6; ArrayBufferSer.UINT32 = 7; ArrayBufferSer.FLOAT32 = 8; //ArrayBufferSer.FLOAT64 = 9; not supported ArrayBufferSer.STRING = 10; ArrayBufferSer.BUFFER = 11; ArrayBufferSer.MAX_PADDING = ArrayBufferSer.INT32; ArrayBufferSer.typeNames = { 'int8': ArrayBufferSer.INT8, 'uint8': ArrayBufferSer.UINT8, 'uintclamp': ArrayBufferSer.UINT8_CLAMP, 'int16': ArrayBufferSer.INT16, 'uint16': ArrayBufferSer.UINT16, 'int32': ArrayBufferSer.INT32, 'uint32': ArrayBufferSer.UINT32, 'float32': ArrayBufferSer.FLOAT32, 'string': ArrayBufferSer.STRING, 'buffer': ArrayBufferSer.BUFFER }; ArrayBufferSer.prototype = { // 0 not used sizes: [NaN, 1, 1, 1, 2, 2, 4, 4, 4, 8], _paddingFor: function(off, type) { var s = this.sizes[type]; if(s) { var r = off % s; return r === 0 ? 0 : s - r; } return 0; }, _sizeFor: function(offset, t) { var self = this; var s = this.sizes[this.type]; if(s) { return s*t.length; } s = 0; if(this.type === ArrayBufferSer.STRING) { // calculate size with padding t.forEach(function(arr) { var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); s += pad; offset += pad; var len = (self.headerSize + arr.length*2); s += len; offset += len; }); } else { t.forEach(function(arr) { var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); s += pad; offset += pad; s += arr.getSize(); offset += arr.getSize(); }); } return s; }, getDataSize: function() { return this._sizeFor(0, this.data); }, getSize: function() { return this.headerSize + this._sizeFor(this.headerSize, this.data); }, writeFn: [ '', 'writeInt8', 'writeUInt8', 'writeUInt8Clamp', 'writeInt16LE', 'writeUInt16LE', 'writeUInt32LE', 'writeUInt32LE', 'writeFloatLE', 'writeDoubleLE', 'writeString', 'writteBuffer' ], _initFunctions: function() { var self = this; this.writeFn.forEach(function(fn) { if(self[fn] === undefined) { self[fn] = function(d) { self.buffer[fn](d, self.offset); self.offset += self.sizes[self.type]; }; } }); }, writeUInt8Clamp: function(c) { this.buffer.writeUInt8(Math.min(255, c), this.offset); this.offset += 1; }, writeString: function(s) { var arr = []; for(var i = 0, len = s.length; i < len; ++i) { arr.push(s.charCodeAt(i)); } var str = new ArrayBufferSer(ArrayBufferSer.UINT16, arr); this.writteBuffer(str); }, writteBuffer: function(b) { this.offset += this._paddingFor(this.offset, ArrayBufferSer.MAX_PADDING); // copy header b.buffer.copy(this.buffer, this.offset); this.offset += b.buffer.length; } }; module.exports = ArrayBufferSer;