A Twitch.tv viewer reward and games system.
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.

197 lines
4.9 KiB

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