smart-buffer.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. var SmartBuffer = (function () {
  2. /**
  3. * Constructor for SmartBuffer.
  4. * @param arg1 {Buffer || Number || String} Buffer to read from, or expected size to write to, or encoding to use.
  5. * @param arg2 {String} Encoding to use for writing and reading strings. Defaults to utf8. If encoding is given in arg1, this is ignored.
  6. * @constructor
  7. *
  8. * There are a few ways to construct a SmartBuffer:
  9. *
  10. * SmartBuffer() - Defaults to utf8, 4096 pre-set internal Buffer length.
  11. * SmartBuffer(size) - Defaults to utf8, sets internal Buffer length to the size given.
  12. * SmartBuffer(encoding) - Sets the given encoding, defaults to 4096 pre-set internal Buffer length.
  13. * SmartBuffer(Buffer) - Defaults to utf8, sets the internal Buffer to the given buffer (same memory).
  14. * SmartBuffer(Buffer, encoding) - Sets the given encoding, sets the internal Buffer to the given buffer (same memory).
  15. *
  16. */
  17. function SmartBuffer(arg1, arg2) {
  18. var type;
  19. switch (type = typeof arg1) {
  20. case 'number':
  21. if (isFinite(arg1) && arg1 > 0) {
  22. this.buff = new Buffer(Math.ceil(arg1));
  23. this.length = 0;
  24. } else {
  25. throw new Error('When specifying a size, it must be a valid number above zero.');
  26. }
  27. break;
  28. case 'string':
  29. if (Buffer.isEncoding(arg1)) {
  30. this.buff = new Buffer(4096);
  31. this.length = 0;
  32. this.encoding = arg1;
  33. } else {
  34. throw new Error('Invalid Encoding');
  35. }
  36. break;
  37. case 'object':
  38. if (Buffer.isBuffer(arg1)) {
  39. this.buff = arg1;
  40. this.length = arg1.length;
  41. } else {
  42. throw new TypeError('First argument must be a Buffer, Number representing the size, or a String representing the encoding.');
  43. }
  44. break;
  45. default:
  46. this.buff = new Buffer(4096);
  47. this.length = 0;
  48. break;
  49. }
  50. if (typeof this.encoding === 'undefined') {
  51. if (typeof arg2 === 'string') {
  52. if (Buffer.isEncoding(arg2)) {
  53. this.encoding = arg2;
  54. } else {
  55. throw new Error('Invalid Encoding');
  56. }
  57. }
  58. }
  59. this._readOffset = 0;
  60. this._writeOffset = 0;
  61. }
  62. SmartBuffer.prototype._ensureWritable = function (len, offset) {
  63. this._ensureCapacity(this.length + len + (typeof offset === 'number' ? offset : 0));
  64. if (typeof offset === 'number') {
  65. this.buff.copy(this.buff, offset + len, offset, this.buff.length);
  66. }
  67. this.length = Math.max(this.length + len, (typeof offset === 'number' ? offset : 0) + len);
  68. };
  69. SmartBuffer.prototype._ensureCapacity = function (minlen) {
  70. var oldlen = this.buff.length;
  71. if (minlen > oldlen) {
  72. var data = this.buff;
  73. var newlen = (oldlen * 3) / 2 + 1;
  74. if (newlen < minlen)
  75. newlen = minlen;
  76. this.buff = new Buffer(newlen);
  77. data.copy(this.buff, 0, 0, oldlen);
  78. }
  79. };
  80. var makeReader = function (func, size) {
  81. return function () {
  82. var ret = func.call(this.buff, this._readOffset);
  83. this._readOffset += size;
  84. return ret;
  85. }
  86. };
  87. var makeWriter = function (func, size) {
  88. return function (value, offset) {
  89. this._ensureWritable(size, offset);
  90. func.call(this.buff, value, typeof offset === 'number' ? offset : this._writeOffset);
  91. this._writeOffset += size;
  92. return this;
  93. }
  94. };
  95. /*
  96. Read Operations
  97. */
  98. SmartBuffer.prototype.readInt8 = makeReader(Buffer.prototype.readInt8, 1);
  99. SmartBuffer.prototype.readInt16BE = makeReader(Buffer.prototype.readInt16BE, 2);
  100. SmartBuffer.prototype.readInt16LE = makeReader(Buffer.prototype.readInt16LE, 2);
  101. SmartBuffer.prototype.readInt32BE = makeReader(Buffer.prototype.readInt32BE, 4);
  102. SmartBuffer.prototype.readInt32LE = makeReader(Buffer.prototype.readInt32LE, 4);
  103. SmartBuffer.prototype.readUInt8 = makeReader(Buffer.prototype.readUInt8, 1);
  104. SmartBuffer.prototype.readUInt16BE = makeReader(Buffer.prototype.readUInt16BE, 2);
  105. SmartBuffer.prototype.readUInt16LE = makeReader(Buffer.prototype.readUInt16LE, 2);
  106. SmartBuffer.prototype.readUInt32BE = makeReader(Buffer.prototype.readUInt32BE, 4);
  107. SmartBuffer.prototype.readUInt32LE = makeReader(Buffer.prototype.readUInt32LE, 4);
  108. SmartBuffer.prototype.readFloatBE = makeReader(Buffer.prototype.readFloatBE, 4);
  109. SmartBuffer.prototype.readFloatLE = makeReader(Buffer.prototype.readFloatLE, 4);
  110. SmartBuffer.prototype.readDoubleBE = makeReader(Buffer.prototype.readDoubleBE, 8);
  111. SmartBuffer.prototype.readDoubleLE = makeReader(Buffer.prototype.readDoubleLE, 8);
  112. /**
  113. * Reads a string of the given length.
  114. * @param length {Number} The length of the string to read. (Defaults to the length of the remaining data)
  115. * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or utf8)
  116. * @returns {string} The string.
  117. */
  118. SmartBuffer.prototype.readString = function (length, encoding) {
  119. var len = Math.min(length, this.length - this._readOffset) || (this.length - this._readOffset);
  120. var ret = this.buff.slice(this._readOffset, this._readOffset + len).toString(encoding || this.encoding);
  121. this._readOffset += len;
  122. return ret;
  123. };
  124. /**
  125. * Reads a null terminated string from the underlying buffer.
  126. * @param encoding {String} Encoding to use. Defaults to encoding set in constructor, or utf8.
  127. * @returns {string}
  128. */
  129. SmartBuffer.prototype.readStringNT = function (encoding) {
  130. var nullpos = this.length;
  131. for (var i = this._readOffset; i < this.length; i++) {
  132. if (this.buff[i] == 0x00) {
  133. nullpos = i;
  134. break;
  135. }
  136. }
  137. var result = this.buff.slice(this._readOffset, nullpos);
  138. this._readOffset = nullpos + 1;
  139. return result.toString(encoding || this.encoding);
  140. };
  141. /**
  142. * Reads a specified number of bytes.
  143. * @param len {Number} Numbers of bytes to read. (Defaults to the remaining data length)
  144. * @returns {Buffer} Buffer containing the read bytes.
  145. */
  146. SmartBuffer.prototype.readBuffer = function (len) {
  147. var endpoint = Math.min(this.length, this._readOffset + (typeof len === 'number' ? len : this.length));
  148. var ret = this.buff.slice(this._readOffset, endpoint);
  149. this._readOffset = endpoint;
  150. return ret;
  151. };
  152. /**
  153. * Reads a null terminated sequence of bytes from the underlying buffer.
  154. * @returns {Buffer} Buffer containing the read bytes.
  155. */
  156. SmartBuffer.prototype.readBufferNT = function () {
  157. var nullpos = this.length;
  158. for (var i = this._readOffset; i < this.length; i++) {
  159. if (this.buff[i] == 0x00) {
  160. nullpos = i;
  161. break;
  162. }
  163. }
  164. var ret = this.buff.slice(this._readOffset, nullpos);
  165. this._readOffset = nullpos + 1;
  166. return ret;
  167. };
  168. /*
  169. Write Operations
  170. */
  171. SmartBuffer.prototype.writeInt8 = makeWriter(Buffer.prototype.writeInt8, 1);
  172. SmartBuffer.prototype.writeInt16BE = makeWriter(Buffer.prototype.writeInt16BE, 2);
  173. SmartBuffer.prototype.writeInt16LE = makeWriter(Buffer.prototype.writeInt16LE, 2);
  174. SmartBuffer.prototype.writeInt32BE = makeWriter(Buffer.prototype.writeInt32BE, 4);
  175. SmartBuffer.prototype.writeInt32LE = makeWriter(Buffer.prototype.writeInt32LE, 4);
  176. SmartBuffer.prototype.writeUInt8 = makeWriter(Buffer.prototype.writeUInt8, 1);
  177. SmartBuffer.prototype.writeUInt16BE = makeWriter(Buffer.prototype.writeUInt16BE, 2);
  178. SmartBuffer.prototype.writeUInt16LE = makeWriter(Buffer.prototype.writeUInt16LE, 2);
  179. SmartBuffer.prototype.writeUInt32BE = makeWriter(Buffer.prototype.writeUInt32BE, 4);
  180. SmartBuffer.prototype.writeUInt32LE = makeWriter(Buffer.prototype.writeUInt32LE, 4);
  181. SmartBuffer.prototype.writeFloatBE = makeWriter(Buffer.prototype.writeFloatBE, 4);
  182. SmartBuffer.prototype.writeFloatLE = makeWriter(Buffer.prototype.writeFloatLE, 4);
  183. SmartBuffer.prototype.writeDoubleBE = makeWriter(Buffer.prototype.writeDoubleBE, 8);
  184. SmartBuffer.prototype.writeDoubleLE = makeWriter(Buffer.prototype.writeDoubleLE, 8);
  185. /**
  186. * Writes a string to the underlying buffer.
  187. * @param value {String} The string to write.
  188. * @param offset {Number} The offset to write the string to. (Encoding can also be set here in place of offset)
  189. * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or to utf8)
  190. * @returns {*}
  191. */
  192. SmartBuffer.prototype.writeString = function (value, offset, encoding) {
  193. var len, _offset, type = typeof offset;
  194. if (type === 'number') {
  195. _offset = offset;
  196. } else if (type === 'string') {
  197. encoding = offset;
  198. offset = this._writeOffset;
  199. } else {
  200. encoding = undefined;
  201. offset = this._writeOffset;
  202. }
  203. len = Buffer.byteLength(value, encoding || this.encoding);
  204. this._ensureWritable(len, _offset);
  205. this.buff.write(value, offset, len, encoding || this.encoding);
  206. this._writeOffset += len;
  207. return this;
  208. };
  209. /**
  210. * Writes a null terminated string to the underlying buffer.
  211. * @param value {String} The string to write.
  212. * @param offset {Number} The offset to write the string to. (Encoding can also be set here in place of offset)
  213. * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or to utf8)
  214. * @returns {*}
  215. */
  216. SmartBuffer.prototype.writeStringNT = function (value, offset, encoding) {
  217. this.writeString(value, offset, encoding);
  218. this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this._writeOffset));
  219. return this;
  220. };
  221. /**
  222. * Writes a Buffer to the underlying buffer.
  223. * @param value {Buffer} The buffer to write.
  224. * @param offset {Number} The offset to write the Buffer to.
  225. * @returns {*}
  226. */
  227. SmartBuffer.prototype.writeBuffer = function (value, offset) {
  228. var len = value.length;
  229. this._ensureWritable(len, offset);
  230. value.copy(this.buff, typeof offset === 'number' ? offset : this._writeOffset);
  231. this._writeOffset += len;
  232. return this;
  233. };
  234. /**
  235. * Writes a null terminated Buffer to the underlying buffer.
  236. * @param value {Buffer} The buffer to write.
  237. * @param offset {Number} The offset to write the Buffer to.
  238. * @returns {*}
  239. */
  240. SmartBuffer.prototype.writeBufferNT = function (value, offset) {
  241. this.writeBuffer(value, offset);
  242. this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this._writeOffset));
  243. return this;
  244. };
  245. /**
  246. * Resets the Endless Buffer.
  247. */
  248. SmartBuffer.prototype.clear = function () {
  249. this._writeOffset = 0;
  250. this._readOffset = 0;
  251. this.length = 0;
  252. };
  253. /**
  254. * Gets the remaining number of bytes to be read from the existing Buffer.
  255. * @returns {number} The number of bytes remaining.
  256. */
  257. SmartBuffer.prototype.remaining = function () {
  258. return this.length - this._readOffset;
  259. };
  260. /**
  261. * Skips the read position forward by the amount of given.
  262. * @param amount {Number} The amount of bytes to skip forward.
  263. */
  264. SmartBuffer.prototype.skip = function (amount) {
  265. if (this._readOffset + amount > this.length)
  266. throw new Error('Target position is beyond the bounds of the data.');
  267. this._readOffset += amount;
  268. };
  269. /**
  270. * Rewinds the read position backward by the amount given.
  271. * @param amount {Number} The amount of bytes to reverse backward.
  272. */
  273. SmartBuffer.prototype.rewind = function (amount) {
  274. if (this._readOffset - amount < 0)
  275. throw new Error('Target position is beyond the bounds of the data.');
  276. this._readOffset -= amount;
  277. };
  278. /**
  279. * Skips the read position to the given position.
  280. * @param position {Number} The position to skip to.
  281. */
  282. SmartBuffer.prototype.skipTo = function (position) {
  283. if (position < 0 || position > this.length)
  284. throw new Error('Target position is beyond the bounds of the data.');
  285. this._readOffset = position;
  286. };
  287. /**
  288. * Gets the underlying Buffer.
  289. * @returns {*}
  290. */
  291. SmartBuffer.prototype.toBuffer = function () {
  292. return this.buff.slice(0, this.length);
  293. };
  294. /**
  295. * Gets a string representation of the underlying Buffer.
  296. * @param encoding {String} Encoding to use. (Defaults to encoding set in constructor, or utf8.)
  297. * @returns {*}
  298. */
  299. SmartBuffer.prototype.toString = function (encoding) {
  300. return this.buff.toString(encoding || this.encoding, 0, this.length);
  301. };
  302. /**
  303. * Destroys the underlying Buffer, and resets the SmartBuffer.
  304. */
  305. SmartBuffer.prototype.destroy = function () {
  306. delete this.buff;
  307. this.clear();
  308. };
  309. return SmartBuffer;
  310. })();
  311. module.exports = SmartBuffer;