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.

425 lines
9.0 KiB

4 years ago
  1. var assert = require('minimalistic-assert');
  2. var Buffer = require('buffer').Buffer;
  3. function WBuf() {
  4. this.buffers = [];
  5. this.toReserve = 0;
  6. this.size = 0;
  7. this.maxSize = 0;
  8. this.avail = 0;
  9. this.last = null;
  10. this.offset = 0;
  11. // Used in slicing
  12. this.sliceQueue = null;
  13. this.forceReserve = false;
  14. // Mostly a constant
  15. this.reserveRate = 64;
  16. }
  17. module.exports = WBuf;
  18. WBuf.prototype.reserve = function reserve(n) {
  19. this.toReserve += n;
  20. // Force reservation of extra bytes
  21. if (this.forceReserve)
  22. this.toReserve = Math.max(this.toReserve, this.reserveRate);
  23. };
  24. WBuf.prototype._ensure = function _ensure(n) {
  25. if (this.avail >= n)
  26. return;
  27. if (this.toReserve === 0)
  28. this.toReserve = this.reserveRate;
  29. this.toReserve = Math.max(n - this.avail, this.toReserve);
  30. if (this.avail === 0)
  31. this._next();
  32. };
  33. WBuf.prototype._next = function _next() {
  34. var buf;
  35. if (this.sliceQueue === null) {
  36. // Most common case
  37. buf = new Buffer(this.toReserve);
  38. } else {
  39. // Only for `.slice()` results
  40. buf = this.sliceQueue.shift();
  41. if (this.sliceQueue.length === 0)
  42. this.sliceQueue = null;
  43. }
  44. this.toReserve = 0;
  45. this.buffers.push(buf);
  46. this.avail = buf.length;
  47. this.offset = 0;
  48. this.last = buf;
  49. };
  50. WBuf.prototype._rangeCheck = function _rangeCheck() {
  51. if (this.maxSize !== 0 && this.size > this.maxSize)
  52. throw new RangeError('WBuf overflow');
  53. };
  54. WBuf.prototype._move = function _move(n) {
  55. this.size += n;
  56. if (this.avail === 0)
  57. this.last = null;
  58. this._rangeCheck();
  59. };
  60. WBuf.prototype.slice = function slice(start, end) {
  61. assert(0 <= start && start <= this.size);
  62. assert(0 <= end && end <= this.size);
  63. if (this.last === null)
  64. this._next();
  65. var res = new WBuf();
  66. // Only last chunk is requested
  67. if (start >= this.size - this.offset) {
  68. res.buffers.push(this.last);
  69. res.last = this.last;
  70. res.offset = start - this.size + this.offset;
  71. res.maxSize = end - start;
  72. res.avail = res.maxSize;
  73. return res;
  74. }
  75. var startIndex = -1;
  76. var startOffset = 0;
  77. var endIndex = -1;
  78. // Find buffer indices
  79. var offset = 0;
  80. for (var i = 0; i < this.buffers.length; i++) {
  81. var buf = this.buffers[i];
  82. var next = offset + buf.length;
  83. // Found the start
  84. if (start >= offset && start <= next) {
  85. startIndex = i;
  86. startOffset = start - offset;
  87. if (endIndex !== -1)
  88. break;
  89. }
  90. if (end >= offset && end <= next) {
  91. endIndex = i;
  92. if (startIndex !== -1)
  93. break;
  94. }
  95. offset = next;
  96. }
  97. res.last = this.buffers[startIndex];
  98. res.offset = startOffset;
  99. res.maxSize = end - start;
  100. // Multi-buffer slice
  101. if (startIndex < endIndex) {
  102. res.sliceQueue = this.buffers.slice(startIndex + 1, endIndex + 1);
  103. res.last = res.last.slice(res.offset);
  104. res.offset = 0;
  105. }
  106. res.avail = res.last.length - res.offset;
  107. res.buffers.push(res.last);
  108. return res;
  109. };
  110. WBuf.prototype.skip = function skip(n) {
  111. if (n === 0)
  112. return this.slice(this.size, this.size);
  113. this._ensure(n);
  114. var left = n;
  115. while (left > 0) {
  116. var toSkip = Math.min(left, this.avail);
  117. left -= toSkip;
  118. this.size += toSkip;
  119. if (toSkip === this.avail) {
  120. if (left !== 0) {
  121. this._next();
  122. } else {
  123. this.avail -= toSkip;
  124. this.offset += toSkip;
  125. }
  126. } else {
  127. this.offset += toSkip;
  128. this.avail -= toSkip;
  129. }
  130. }
  131. this._rangeCheck();
  132. return this.slice(this.size - n, this.size);
  133. };
  134. WBuf.prototype.write = function write(str) {
  135. var len = 0;
  136. for (var i = 0; i < str.length; i++) {
  137. var c = str.charCodeAt(i);
  138. if (c > 255)
  139. len += 2;
  140. else
  141. len += 1;
  142. }
  143. this.reserve(len);
  144. for (var i = 0; i < str.length; i++) {
  145. var c = str.charCodeAt(i);
  146. var hi = c >>> 8;
  147. var lo = c & 0xff;
  148. if (hi)
  149. this.writeUInt8(hi);
  150. this.writeUInt8(lo);
  151. }
  152. };
  153. WBuf.prototype.copyFrom = function copyFrom(buf, start, end) {
  154. var off = start === undefined ? 0 : start;
  155. var len = end === undefined ? buf.length : end;
  156. if (off === len)
  157. return;
  158. this._ensure(len - off);
  159. while (off < len) {
  160. var toCopy = Math.min(len - off, this.avail);
  161. buf.copy(this.last, this.offset, off, off + toCopy);
  162. off += toCopy;
  163. this.size += toCopy;
  164. if (toCopy === this.avail) {
  165. if (off !== len) {
  166. this._next();
  167. } else {
  168. this.avail = 0;
  169. this.offset += toCopy;
  170. }
  171. } else {
  172. this.offset += toCopy;
  173. this.avail -= toCopy;
  174. }
  175. }
  176. this._rangeCheck();
  177. };
  178. WBuf.prototype.writeUInt8 = function writeUInt8(v) {
  179. this._ensure(1);
  180. this.last[this.offset++] = v;
  181. this.avail--;
  182. this._move(1);
  183. };
  184. WBuf.prototype.writeUInt16BE = function writeUInt16BE(v) {
  185. this._ensure(2);
  186. // Fast case - everything fits into the last buffer
  187. if (this.avail >= 2) {
  188. this.last.writeUInt16BE(v, this.offset);
  189. this.offset += 2;
  190. this.avail -= 2;
  191. // One byte here, one byte there
  192. } else {
  193. this.last[this.offset] = (v >>> 8);
  194. this._next();
  195. this.last[this.offset++] = v & 0xff;
  196. this.avail--;
  197. }
  198. this._move(2);
  199. };
  200. WBuf.prototype.writeUInt24BE = function writeUInt24BE(v) {
  201. this._ensure(3);
  202. // Fast case - everything fits into the last buffer
  203. if (this.avail >= 3) {
  204. this.last.writeUInt16BE(v >>> 8, this.offset);
  205. this.last[this.offset + 2] = v & 0xff;
  206. this.offset += 3;
  207. this.avail -= 3;
  208. this._move(3);
  209. // Two bytes here
  210. } else if (this.avail >= 2) {
  211. this.last.writeUInt16BE(v >>> 8, this.offset);
  212. this._next();
  213. this.last[this.offset++] = v & 0xff;
  214. this.avail--;
  215. this._move(3);
  216. // Just one byte here
  217. } else {
  218. this.last[this.offset] = v >>> 16;
  219. this._move(1);
  220. this._next();
  221. this.writeUInt16BE(v & 0xffff);
  222. }
  223. };
  224. WBuf.prototype.writeUInt32BE = function writeUInt32BE(v) {
  225. this._ensure(4);
  226. // Fast case - everything fits into the last buffer
  227. if (this.avail >= 4) {
  228. this.last.writeUInt32BE(v, this.offset);
  229. this.offset += 4;
  230. this.avail -= 4;
  231. this._move(4);
  232. // Three bytes here
  233. } else if (this.avail >= 3) {
  234. this.writeUInt24BE(v >>> 8);
  235. this._next();
  236. this.last[this.offset++] = v & 0xff;
  237. this.avail--;
  238. this._move(1);
  239. // Slow case, who cares
  240. } else {
  241. this.writeUInt16BE(v >>> 16);
  242. this.writeUInt16BE(v & 0xffff);
  243. }
  244. };
  245. WBuf.prototype.writeUInt16LE = function writeUInt16LE(num) {
  246. var r = ((num & 0xff) << 8) | (num >>> 8);
  247. this.writeUInt16BE(r);
  248. };
  249. WBuf.prototype.writeUInt24LE = function writeUInt24LE(num) {
  250. var r = ((num & 0xff) << 16) | (((num >>> 8) & 0xff) << 8) | (num >>> 16);
  251. this.writeUInt24BE(r);
  252. };
  253. WBuf.prototype.writeUInt32LE = function writeUInt32LE(num) {
  254. this._ensure(4);
  255. // Fast case - everything fits into the last buffer
  256. if (this.avail >= 4) {
  257. this.last.writeUInt32LE(num, this.offset);
  258. this.offset += 4;
  259. this.avail -= 4;
  260. this._move(4);
  261. // Three bytes here
  262. } else if (this.avail >= 3) {
  263. this.writeUInt24LE(num & 0xffffff);
  264. this._next();
  265. this.last[this.offset++] = num >>> 24;
  266. this.avail--;
  267. this._move(1);
  268. // Slow case, who cares
  269. } else {
  270. this.writeUInt16LE(num & 0xffff);
  271. this.writeUInt16LE(num >>> 16);
  272. }
  273. };
  274. WBuf.prototype.render = function render() {
  275. var left = this.size;
  276. var out = [];
  277. for (var i = 0; i < this.buffers.length && left >= 0; i++) {
  278. var buf = this.buffers[i];
  279. left -= buf.length;
  280. if (left >= 0) {
  281. out.push(buf);
  282. } else {
  283. out.push(buf.slice(0, buf.length + left));
  284. }
  285. }
  286. return out;
  287. };
  288. // Signed APIs
  289. WBuf.prototype.writeInt8 = function writeInt8(num) {
  290. if (num < 0)
  291. return this.writeUInt8(0x100 + num);
  292. else
  293. return this.writeUInt8(num);
  294. };
  295. function toUnsigned16(num) {
  296. if (num < 0)
  297. return 0x10000 + num;
  298. else
  299. return num;
  300. }
  301. WBuf.prototype.writeInt16LE = function writeInt16LE(num) {
  302. this.writeUInt16LE(toUnsigned16(num));
  303. };
  304. WBuf.prototype.writeInt16BE = function writeInt16BE(num) {
  305. this.writeUInt16BE(toUnsigned16(num));
  306. };
  307. function toUnsigned24(num) {
  308. if (num < 0)
  309. return 0x1000000 + num;
  310. else
  311. return num;
  312. }
  313. WBuf.prototype.writeInt24LE = function writeInt24LE(num) {
  314. this.writeUInt24LE(toUnsigned24(num));
  315. };
  316. WBuf.prototype.writeInt24BE = function writeInt24BE(num) {
  317. this.writeUInt24BE(toUnsigned24(num));
  318. };
  319. function toUnsigned32(num) {
  320. if (num < 0)
  321. return (0xffffffff + num) + 1;
  322. else
  323. return num;
  324. }
  325. WBuf.prototype.writeInt32LE = function writeInt32LE(num) {
  326. this.writeUInt32LE(toUnsigned32(num));
  327. };
  328. WBuf.prototype.writeInt32BE = function writeInt32BE(num) {
  329. this.writeUInt32BE(toUnsigned32(num));
  330. };
  331. WBuf.prototype.writeComb = function writeComb(size, endian, value) {
  332. if (size === 1)
  333. return this.writeUInt8(value);
  334. if (endian === 'le') {
  335. if (size === 2)
  336. this.writeUInt16LE(value);
  337. else if (size === 3)
  338. this.writeUInt24LE(value);
  339. else if (size === 4)
  340. this.writeUInt32LE(value);
  341. } else {
  342. if (size === 2)
  343. this.writeUInt16BE(value);
  344. else if (size === 3)
  345. this.writeUInt24BE(value);
  346. else if (size === 4)
  347. this.writeUInt32BE(value);
  348. }
  349. };