FastBufferList.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // This file was copied from https://github.com/substack/node-bufferlist
  2. // and modified to be able to copy bytes from the bufferlist directly into
  3. // a pre-existing fixed-size buffer without an additional memory allocation.
  4. // bufferlist.js
  5. // Treat a linked list of buffers as a single variable-size buffer.
  6. var Buffer = require('buffer').Buffer;
  7. var EventEmitter = require('events').EventEmitter;
  8. var bufferAllocUnsafe = require('../lib/utils').bufferAllocUnsafe;
  9. module.exports = BufferList;
  10. module.exports.BufferList = BufferList; // backwards compatibility
  11. function BufferList(opts) {
  12. if (!(this instanceof BufferList)) return new BufferList(opts);
  13. EventEmitter.call(this);
  14. var self = this;
  15. if (typeof(opts) == 'undefined') opts = {};
  16. // default encoding to use for take(). Leaving as 'undefined'
  17. // makes take() return a Buffer instead.
  18. self.encoding = opts.encoding;
  19. var head = { next : null, buffer : null };
  20. var last = { next : null, buffer : null };
  21. // length can get negative when advanced past the end
  22. // and this is the desired behavior
  23. var length = 0;
  24. self.__defineGetter__('length', function () {
  25. return length;
  26. });
  27. // keep an offset of the head to decide when to head = head.next
  28. var offset = 0;
  29. // Write to the bufferlist. Emits 'write'. Always returns true.
  30. self.write = function (buf) {
  31. if (!head.buffer) {
  32. head.buffer = buf;
  33. last = head;
  34. }
  35. else {
  36. last.next = { next : null, buffer : buf };
  37. last = last.next;
  38. }
  39. length += buf.length;
  40. self.emit('write', buf);
  41. return true;
  42. };
  43. self.end = function (buf) {
  44. if (Buffer.isBuffer(buf)) self.write(buf);
  45. };
  46. // Push buffers to the end of the linked list. (deprecated)
  47. // Return this (self).
  48. self.push = function () {
  49. var args = [].concat.apply([], arguments);
  50. args.forEach(self.write);
  51. return self;
  52. };
  53. // For each buffer, perform some action.
  54. // If fn's result is a true value, cut out early.
  55. // Returns this (self).
  56. self.forEach = function (fn) {
  57. if (!head.buffer) return bufferAllocUnsafe(0);
  58. if (head.buffer.length - offset <= 0) return self;
  59. var firstBuf = head.buffer.slice(offset);
  60. var b = { buffer : firstBuf, next : head.next };
  61. while (b && b.buffer) {
  62. var r = fn(b.buffer);
  63. if (r) break;
  64. b = b.next;
  65. }
  66. return self;
  67. };
  68. // Create a single Buffer out of all the chunks or some subset specified by
  69. // start and one-past the end (like slice) in bytes.
  70. self.join = function (start, end) {
  71. if (!head.buffer) return bufferAllocUnsafe(0);
  72. if (start == undefined) start = 0;
  73. if (end == undefined) end = self.length;
  74. var big = bufferAllocUnsafe(end - start);
  75. var ix = 0;
  76. self.forEach(function (buffer) {
  77. if (start < (ix + buffer.length) && ix < end) {
  78. // at least partially contained in the range
  79. buffer.copy(
  80. big,
  81. Math.max(0, ix - start),
  82. Math.max(0, start - ix),
  83. Math.min(buffer.length, end - ix)
  84. );
  85. }
  86. ix += buffer.length;
  87. if (ix > end) return true; // stop processing past end
  88. });
  89. return big;
  90. };
  91. self.joinInto = function (targetBuffer, targetStart, sourceStart, sourceEnd) {
  92. if (!head.buffer) return new bufferAllocUnsafe(0);
  93. if (sourceStart == undefined) sourceStart = 0;
  94. if (sourceEnd == undefined) sourceEnd = self.length;
  95. var big = targetBuffer;
  96. if (big.length - targetStart < sourceEnd - sourceStart) {
  97. throw new Error("Insufficient space available in target Buffer.");
  98. }
  99. var ix = 0;
  100. self.forEach(function (buffer) {
  101. if (sourceStart < (ix + buffer.length) && ix < sourceEnd) {
  102. // at least partially contained in the range
  103. buffer.copy(
  104. big,
  105. Math.max(targetStart, targetStart + ix - sourceStart),
  106. Math.max(0, sourceStart - ix),
  107. Math.min(buffer.length, sourceEnd - ix)
  108. );
  109. }
  110. ix += buffer.length;
  111. if (ix > sourceEnd) return true; // stop processing past end
  112. });
  113. return big;
  114. };
  115. // Advance the buffer stream by n bytes.
  116. // If n the aggregate advance offset passes the end of the buffer list,
  117. // operations such as .take() will return empty strings until enough data is
  118. // pushed.
  119. // Returns this (self).
  120. self.advance = function (n) {
  121. offset += n;
  122. length -= n;
  123. while (head.buffer && offset >= head.buffer.length) {
  124. offset -= head.buffer.length;
  125. head = head.next
  126. ? head.next
  127. : { buffer : null, next : null }
  128. ;
  129. }
  130. if (head.buffer === null) last = { next : null, buffer : null };
  131. self.emit('advance', n);
  132. return self;
  133. };
  134. // Take n bytes from the start of the buffers.
  135. // Returns a string.
  136. // If there are less than n bytes in all the buffers or n is undefined,
  137. // returns the entire concatenated buffer string.
  138. self.take = function (n, encoding) {
  139. if (n == undefined) n = self.length;
  140. else if (typeof n !== 'number') {
  141. encoding = n;
  142. n = self.length;
  143. }
  144. var b = head;
  145. if (!encoding) encoding = self.encoding;
  146. if (encoding) {
  147. var acc = '';
  148. self.forEach(function (buffer) {
  149. if (n <= 0) return true;
  150. acc += buffer.toString(
  151. encoding, 0, Math.min(n,buffer.length)
  152. );
  153. n -= buffer.length;
  154. });
  155. return acc;
  156. } else {
  157. // If no 'encoding' is specified, then return a Buffer.
  158. return self.join(0, n);
  159. }
  160. };
  161. // The entire concatenated buffer as a string.
  162. self.toString = function () {
  163. return self.take('binary');
  164. };
  165. }
  166. require('util').inherits(BufferList, EventEmitter);