PacketWriter.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. var BIT_16 = Math.pow(2, 16);
  2. var BIT_24 = Math.pow(2, 24);
  3. var BUFFER_ALLOC_SIZE = Math.pow(2, 8);
  4. // The maximum precision JS Numbers can hold precisely
  5. // Don't panic: Good enough to represent byte values up to 8192 TB
  6. var IEEE_754_BINARY_64_PRECISION = Math.pow(2, 53);
  7. var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1;
  8. var Buffer = require('safe-buffer').Buffer;
  9. module.exports = PacketWriter;
  10. function PacketWriter() {
  11. this._buffer = null;
  12. this._offset = 0;
  13. }
  14. PacketWriter.prototype.toBuffer = function toBuffer(parser) {
  15. if (!this._buffer) {
  16. this._buffer = Buffer.alloc(0);
  17. this._offset = 0;
  18. }
  19. var buffer = this._buffer;
  20. var length = this._offset;
  21. var packets = Math.floor(length / MAX_PACKET_LENGTH) + 1;
  22. this._buffer = Buffer.allocUnsafe(length + packets * 4);
  23. this._offset = 0;
  24. for (var packet = 0; packet < packets; packet++) {
  25. var isLast = (packet + 1 === packets);
  26. var packetLength = (isLast)
  27. ? length % MAX_PACKET_LENGTH
  28. : MAX_PACKET_LENGTH;
  29. var packetNumber = parser.incrementPacketNumber();
  30. this.writeUnsignedNumber(3, packetLength);
  31. this.writeUnsignedNumber(1, packetNumber);
  32. var start = packet * MAX_PACKET_LENGTH;
  33. var end = start + packetLength;
  34. this.writeBuffer(buffer.slice(start, end));
  35. }
  36. return this._buffer;
  37. };
  38. PacketWriter.prototype.writeUnsignedNumber = function(bytes, value) {
  39. this._allocate(bytes);
  40. for (var i = 0; i < bytes; i++) {
  41. this._buffer[this._offset++] = (value >> (i * 8)) & 0xff;
  42. }
  43. };
  44. PacketWriter.prototype.writeFiller = function(bytes) {
  45. this._allocate(bytes);
  46. for (var i = 0; i < bytes; i++) {
  47. this._buffer[this._offset++] = 0x00;
  48. }
  49. };
  50. PacketWriter.prototype.writeNullTerminatedString = function(value, encoding) {
  51. // Typecast undefined into '' and numbers into strings
  52. value = value || '';
  53. value = value + '';
  54. var bytes = Buffer.byteLength(value, encoding || 'utf-8') + 1;
  55. this._allocate(bytes);
  56. this._buffer.write(value, this._offset, encoding);
  57. this._buffer[this._offset + bytes - 1] = 0x00;
  58. this._offset += bytes;
  59. };
  60. PacketWriter.prototype.writeString = function(value) {
  61. // Typecast undefined into '' and numbers into strings
  62. value = value || '';
  63. value = value + '';
  64. var bytes = Buffer.byteLength(value, 'utf-8');
  65. this._allocate(bytes);
  66. this._buffer.write(value, this._offset, 'utf-8');
  67. this._offset += bytes;
  68. };
  69. PacketWriter.prototype.writeBuffer = function(value) {
  70. var bytes = value.length;
  71. this._allocate(bytes);
  72. value.copy(this._buffer, this._offset);
  73. this._offset += bytes;
  74. };
  75. PacketWriter.prototype.writeLengthCodedNumber = function(value) {
  76. if (value === null) {
  77. this._allocate(1);
  78. this._buffer[this._offset++] = 251;
  79. return;
  80. }
  81. if (value <= 250) {
  82. this._allocate(1);
  83. this._buffer[this._offset++] = value;
  84. return;
  85. }
  86. if (value > IEEE_754_BINARY_64_PRECISION) {
  87. throw new Error(
  88. 'writeLengthCodedNumber: JS precision range exceeded, your ' +
  89. 'number is > 53 bit: "' + value + '"'
  90. );
  91. }
  92. if (value < BIT_16) {
  93. this._allocate(3);
  94. this._buffer[this._offset++] = 252;
  95. } else if (value < BIT_24) {
  96. this._allocate(4);
  97. this._buffer[this._offset++] = 253;
  98. } else {
  99. this._allocate(9);
  100. this._buffer[this._offset++] = 254;
  101. }
  102. // 16 Bit
  103. this._buffer[this._offset++] = value & 0xff;
  104. this._buffer[this._offset++] = (value >> 8) & 0xff;
  105. if (value < BIT_16) {
  106. return;
  107. }
  108. // 24 Bit
  109. this._buffer[this._offset++] = (value >> 16) & 0xff;
  110. if (value < BIT_24) {
  111. return;
  112. }
  113. this._buffer[this._offset++] = (value >> 24) & 0xff;
  114. // Hack: Get the most significant 32 bit (JS bitwise operators are 32 bit)
  115. value = value.toString(2);
  116. value = value.substr(0, value.length - 32);
  117. value = parseInt(value, 2);
  118. this._buffer[this._offset++] = value & 0xff;
  119. this._buffer[this._offset++] = (value >> 8) & 0xff;
  120. this._buffer[this._offset++] = (value >> 16) & 0xff;
  121. // Set last byte to 0, as we can only support 53 bits in JS (see above)
  122. this._buffer[this._offset++] = 0;
  123. };
  124. PacketWriter.prototype.writeLengthCodedBuffer = function(value) {
  125. var bytes = value.length;
  126. this.writeLengthCodedNumber(bytes);
  127. this.writeBuffer(value);
  128. };
  129. PacketWriter.prototype.writeNullTerminatedBuffer = function(value) {
  130. this.writeBuffer(value);
  131. this.writeFiller(1); // 0x00 terminator
  132. };
  133. PacketWriter.prototype.writeLengthCodedString = function(value) {
  134. if (value === null) {
  135. this.writeLengthCodedNumber(null);
  136. return;
  137. }
  138. value = (value === undefined)
  139. ? ''
  140. : String(value);
  141. var bytes = Buffer.byteLength(value, 'utf-8');
  142. this.writeLengthCodedNumber(bytes);
  143. if (!bytes) {
  144. return;
  145. }
  146. this._allocate(bytes);
  147. this._buffer.write(value, this._offset, 'utf-8');
  148. this._offset += bytes;
  149. };
  150. PacketWriter.prototype._allocate = function _allocate(bytes) {
  151. if (!this._buffer) {
  152. this._buffer = Buffer.alloc(Math.max(BUFFER_ALLOC_SIZE, bytes));
  153. this._offset = 0;
  154. return;
  155. }
  156. var bytesRemaining = this._buffer.length - this._offset;
  157. if (bytesRemaining >= bytes) {
  158. return;
  159. }
  160. var newSize = this._buffer.length + Math.max(BUFFER_ALLOC_SIZE, bytes);
  161. var oldBuffer = this._buffer;
  162. this._buffer = Buffer.alloc(newSize);
  163. oldBuffer.copy(this._buffer);
  164. };