1 var DATA_TYPES = require('./../constants/DataTypes');
  2 
  3 module.exports = PacketWriter;
  4 
  5 /**
  6  * Create a new instance
  7  * @constructor
  8  */
  9 function PacketWriter() {
 10   this._buffer = new Buffer(0);
 11   this._offset = 0;
 12 }
 13 
 14 /**
 15  * Write the current buffer content
 16  * @return {*}
 17  */
 18 PacketWriter.prototype._toBuffer = function () {
 19   return this._buffer.slice(0, this._offset);
 20 };
 21 
 22 /**
 23  * Write a byte value to the internal buffer
 24  * @param value
 25  */
 26 PacketWriter.prototype._writeByte = function (value) {
 27   this._allocate(DATA_TYPES.BYTE_SIZEOF);
 28 
 29   this._buffer[this._offset++] = value & 0xFF;
 30 };
 31 
 32 /**
 33  * Write a char value to the internal buffer
 34  * @param value
 35  */
 36 PacketWriter.prototype._writeChar = function (value) {
 37   this._allocate(DATA_TYPES.BYTE_SIZEOF);
 38 
 39   this._buffer[this._offset++] = value.charCodeAt(0);
 40 };
 41 
 42 /**
 43  * Write a bytes array to the internal buffer
 44  * @param bytesCount
 45  * @param value
 46  */
 47 PacketWriter.prototype._writeBytes = function (bytesCount, value) {
 48   this._allocate(bytesCount);
 49 
 50   for (var i = 0; i < bytesCount; i++) {
 51     this._buffer[this._offset++] = value[i] & 0xFF;
 52   }
 53 };
 54 
 55 /**
 56  * Write a short value to the internal buffer
 57  * @param value
 58  */
 59 PacketWriter.prototype._writeShort = function (value) {
 60   this._allocate(DATA_TYPES.SHORT_SIZEOF);
 61 
 62   this._writeByte((value >> 8) & 0xFF);
 63   this._writeByte((value >> 0) & 0xFF);
 64 };
 65 
 66 /**
 67  * Write a integer value to the internal buffer
 68  * @param value
 69  */
 70 PacketWriter.prototype._writeInt = function (value) {
 71   this._allocate(DATA_TYPES.INT_SIZEOF);
 72 
 73   this._writeByte((value >> 24) & 0xFF);
 74   this._writeByte((value >> 16) & 0xFF);
 75   this._writeByte((value >> 8) & 0xFF);
 76   this._writeByte((value >> 0) & 0xFF);
 77 };
 78 
 79 /**
 80  * Write a Long value to the internal buffer
 81  * @param value
 82  */
 83 PacketWriter.prototype._writeLong = function (value) {
 84   var reminder = value % Math.pow(2, 32);
 85   var quotient = (value - reminder) / Math.pow(2, 32);
 86   this._writeInt(quotient);
 87   this._writeInt(reminder);
 88 };
 89 
 90 /**
 91  * Write a Floating point value to the internal buffer
 92  * @param value
 93  */
 94 PacketWriter.prototype._writeFloat = function (value) {
 95   this._allocate(DATA_TYPES.FLOAT_SIZEOF);
 96   this._buffer.writeFloatBE(value, this._offset);
 97   this._offset += DATA_TYPES.FLOAT_SIZEOF;
 98 };
 99 
100 /**
101  * Write a Double precision value to the internal buffer
102  * @param value
103  */
104 PacketWriter.prototype._writeDouble = function (value) {
105   this._allocate(DATA_TYPES.DOUBLE_SIZEOF);
106   this._buffer.writeDoubleBE(value, this._offset);
107   this._offset += DATA_TYPES.DOUBLE_SIZEOF;
108 };
109 
110 /**
111  * Write a Numeric value to the internal buffer
112  * @param value
113  */
114 PacketWriter.prototype._writeNumeric = function (value) {
115   this._writeNullTerminatedString(value.toString(10));
116 };
117 
118 /**
119  * Write the specified value to the internal buffer
120  * @param bytesCount
121  * @param fillerValue
122  */
123 PacketWriter.prototype._writeFiller = function (bytesCount, fillerValue) {
124   var fillerVal;
125   this._allocate(bytesCount);
126 
127   fillerValue = typeof fillerValue != 'undefined' ? fillerValue : 0x00;
128 
129   if (typeof fillerValue == 'string') {
130     fillerVal = fillerValue.charCodeAt(0);
131   } else {
132     fillerVal = fillerValue & 0xFF;
133   }
134 
135   for (var i = 0; i < bytesCount; i++) {
136     this._buffer[this._offset++] = fillerVal;
137   }
138 };
139 
140 /**
141  * Write a null-terminate string to the internal buffer
142  * @param value
143  */
144 PacketWriter.prototype._writeNullTerminatedString = function (value) {
145   //Typecast undefined into '' and numbers into strings
146   value = value || '';
147   value = value + '';
148 
149   var stringLengthInBytes = Buffer.byteLength(value)
150   var count = DATA_TYPES.INT_SIZEOF + stringLengthInBytes + DATA_TYPES.BYTE_SIZEOF;
151   this._allocate(count);
152 
153   //Write length
154   this._writeInt(stringLengthInBytes + 1);
155 
156   this._buffer.write(value, this._offset, stringLengthInBytes);
157   this._offset += stringLengthInBytes;
158 
159   //Write null-terminate
160   this._buffer[this._offset++] = 0;
161 };
162 
163 /**
164  * Write a fixed-length string to the internal buffer
165  * @param value
166  * @param fillerValue
167  * @param fixedLength
168  */
169 PacketWriter.prototype._writeFixedLengthString = function (value, fillerValue, fixedLength) {
170   var fillerVal;
171   //Typecast undefined into '' and numbers into strings
172   value = value || '';
173   value = value + '';
174 
175   var count = value.length;
176   if (count >= fixedLength) {
177     count = fixedLength;
178   }
179 
180   this._allocate(fixedLength);
181 
182   for (var i = 0; i < value.length; i++) {
183     this._buffer[this._offset++] = value[i].charCodeAt(0);
184   }
185 
186   if (typeof fillerValue == 'string') {
187     fillerVal = fillerValue.charCodeAt(0);
188   } else {
189     fillerVal = fillerValue & 0xFF;
190   }
191 
192   for (var j = 1; j <= fixedLength - count; j++) {
193     this._buffer[this._offset++] = fillerVal;
194   }
195 };
196 
197 /**
198  * Write a Date value to the internal buffer
199  * @param year
200  * @param month
201  * @param day
202  */
203 PacketWriter.prototype._writeDate = function (year, month, day) {
204   this._allocate(DATA_TYPES.DATETIME_SIZEOF);
205 
206   this._writeShort(year);
207   this._writeShort(month);
208   this._writeShort(day);
209   this._writeShort(0);
210   this._writeShort(0);
211   this._writeShort(0);
212   this._writeShort(0);
213 };
214 
215 /**
216  * Write a DateTime value to the internal buffer
217  * @param year
218  * @param month
219  * @param day
220  * @param hour
221  * @param min
222  * @param sec
223  * @param msec
224  */
225 PacketWriter.prototype._writeDateTime = function (year, month, day, hour, min, sec, msec) {
226   this._allocate(DATA_TYPES.DATETIME_SIZEOF);
227 
228   this._writeShort(year);
229   this._writeShort(month);
230   this._writeShort(day);
231   this._writeShort(hour);
232   this._writeShort(min);
233   this._writeShort(sec);
234   this._writeShort(msec);
235 };
236 
237 /**
238  * Write a Time value to the internal buffer
239  * @param hour
240  * @param min
241  * @param sec
242  */
243 PacketWriter.prototype._writeTime = function (hour, min, sec) {
244   this._allocate(DATA_TYPES.DATETIME_SIZEOF);
245 
246   this._writeShort(0);
247   this._writeShort(0);
248   this._writeShort(0);
249   this._writeShort(hour);
250   this._writeShort(min);
251   this._writeShort(sec);
252   this._writeShort(0);
253 };
254 
255 /**
256  * Write a Timestamp value to the internal buffer
257  * @param year
258  * @param month
259  * @param day
260  * @param hour
261  * @param min
262  * @param sec
263  */
264 PacketWriter.prototype._writeTimestamp = function (year, month, day, hour, min, sec) {
265   this._allocate(DATA_TYPES.DATETIME_SIZEOF);
266 
267   this._writeShort(year);
268   this._writeShort(month);
269   this._writeShort(day);
270   this._writeShort(hour);
271   this._writeShort(min);
272   this._writeShort(sec);
273   this._writeShort(0);
274 };
275 
276 /**
277  * Write a generic object value to the internal buffer
278  * @param value
279  */
280 PacketWriter.prototype._writeBuffer = function (value) {
281   var count = value.length;
282 
283   this._allocate(count);
284   value.copy(this._buffer, this._offset);
285   this._offset += count;
286 };
287 
288 //TODO Optimize the performance of this function
289 /**
290  * Allocate space to the internal buffer
291  * @param count
292  * @private
293  */
294 PacketWriter.prototype._allocate = function (count) {
295   if (!this._buffer) {
296     this._buffer = new Buffer(count);
297     return;
298   }
299 
300   //Verify if we need to allocate more space
301   var bytesRemaining = this._buffer.length - this._offset;
302   if (bytesRemaining >= count) {
303     return;
304   }
305 
306   var oldBuffer = this._buffer;
307   this._buffer = new Buffer(oldBuffer.length + count);
308   oldBuffer.copy(this._buffer);
309 };
310