bl.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. 'use strict'
  2. var DuplexStream = require('readable-stream').Duplex
  3. , util = require('util')
  4. function BufferList (callback) {
  5. if (!(this instanceof BufferList))
  6. return new BufferList(callback)
  7. this._bufs = []
  8. this.length = 0
  9. if (typeof callback == 'function') {
  10. this._callback = callback
  11. var piper = function piper (err) {
  12. if (this._callback) {
  13. this._callback(err)
  14. this._callback = null
  15. }
  16. }.bind(this)
  17. this.on('pipe', function onPipe (src) {
  18. src.on('error', piper)
  19. })
  20. this.on('unpipe', function onUnpipe (src) {
  21. src.removeListener('error', piper)
  22. })
  23. } else {
  24. this.append(callback)
  25. }
  26. DuplexStream.call(this)
  27. }
  28. util.inherits(BufferList, DuplexStream)
  29. BufferList.prototype._offset = function _offset (offset) {
  30. var tot = 0, i = 0, _t
  31. if (offset === 0) return [ 0, 0 ]
  32. for (; i < this._bufs.length; i++) {
  33. _t = tot + this._bufs[i].length
  34. if (offset < _t || i == this._bufs.length - 1) {
  35. return [ i, offset - tot ]
  36. }
  37. tot = _t
  38. }
  39. }
  40. BufferList.prototype._reverseOffset = function (blOffset) {
  41. var bufferId = blOffset[0]
  42. var offset = blOffset[1]
  43. for (var i = 0; i < bufferId; i++) {
  44. offset += this._bufs[i].length
  45. }
  46. return offset
  47. }
  48. BufferList.prototype.append = function append (buf) {
  49. var i = 0
  50. if (Buffer.isBuffer(buf)) {
  51. this._appendBuffer(buf)
  52. } else if (Array.isArray(buf)) {
  53. for (; i < buf.length; i++)
  54. this.append(buf[i])
  55. } else if (buf instanceof BufferList) {
  56. // unwrap argument into individual BufferLists
  57. for (; i < buf._bufs.length; i++)
  58. this.append(buf._bufs[i])
  59. } else if (buf != null) {
  60. // coerce number arguments to strings, since Buffer(number) does
  61. // uninitialized memory allocation
  62. if (typeof buf == 'number')
  63. buf = buf.toString()
  64. this._appendBuffer(Buffer.from(buf))
  65. }
  66. return this
  67. }
  68. BufferList.prototype._appendBuffer = function appendBuffer (buf) {
  69. this._bufs.push(buf)
  70. this.length += buf.length
  71. }
  72. BufferList.prototype._write = function _write (buf, encoding, callback) {
  73. this._appendBuffer(buf)
  74. if (typeof callback == 'function')
  75. callback()
  76. }
  77. BufferList.prototype._read = function _read (size) {
  78. if (!this.length)
  79. return this.push(null)
  80. size = Math.min(size, this.length)
  81. this.push(this.slice(0, size))
  82. this.consume(size)
  83. }
  84. BufferList.prototype.end = function end (chunk) {
  85. DuplexStream.prototype.end.call(this, chunk)
  86. if (this._callback) {
  87. this._callback(null, this.slice())
  88. this._callback = null
  89. }
  90. }
  91. BufferList.prototype.get = function get (index) {
  92. if (index > this.length || index < 0) {
  93. return undefined
  94. }
  95. var offset = this._offset(index)
  96. return this._bufs[offset[0]][offset[1]]
  97. }
  98. BufferList.prototype.slice = function slice (start, end) {
  99. if (typeof start == 'number' && start < 0)
  100. start += this.length
  101. if (typeof end == 'number' && end < 0)
  102. end += this.length
  103. return this.copy(null, 0, start, end)
  104. }
  105. BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) {
  106. if (typeof srcStart != 'number' || srcStart < 0)
  107. srcStart = 0
  108. if (typeof srcEnd != 'number' || srcEnd > this.length)
  109. srcEnd = this.length
  110. if (srcStart >= this.length)
  111. return dst || Buffer.alloc(0)
  112. if (srcEnd <= 0)
  113. return dst || Buffer.alloc(0)
  114. var copy = !!dst
  115. , off = this._offset(srcStart)
  116. , len = srcEnd - srcStart
  117. , bytes = len
  118. , bufoff = (copy && dstStart) || 0
  119. , start = off[1]
  120. , l
  121. , i
  122. // copy/slice everything
  123. if (srcStart === 0 && srcEnd == this.length) {
  124. if (!copy) { // slice, but full concat if multiple buffers
  125. return this._bufs.length === 1
  126. ? this._bufs[0]
  127. : Buffer.concat(this._bufs, this.length)
  128. }
  129. // copy, need to copy individual buffers
  130. for (i = 0; i < this._bufs.length; i++) {
  131. this._bufs[i].copy(dst, bufoff)
  132. bufoff += this._bufs[i].length
  133. }
  134. return dst
  135. }
  136. // easy, cheap case where it's a subset of one of the buffers
  137. if (bytes <= this._bufs[off[0]].length - start) {
  138. return copy
  139. ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
  140. : this._bufs[off[0]].slice(start, start + bytes)
  141. }
  142. if (!copy) // a slice, we need something to copy in to
  143. dst = Buffer.allocUnsafe(len)
  144. for (i = off[0]; i < this._bufs.length; i++) {
  145. l = this._bufs[i].length - start
  146. if (bytes > l) {
  147. this._bufs[i].copy(dst, bufoff, start)
  148. } else {
  149. this._bufs[i].copy(dst, bufoff, start, start + bytes)
  150. break
  151. }
  152. bufoff += l
  153. bytes -= l
  154. if (start)
  155. start = 0
  156. }
  157. return dst
  158. }
  159. BufferList.prototype.shallowSlice = function shallowSlice (start, end) {
  160. start = start || 0
  161. end = typeof end !== 'number' ? this.length : end
  162. if (start < 0)
  163. start += this.length
  164. if (end < 0)
  165. end += this.length
  166. if (start === end) {
  167. return new BufferList()
  168. }
  169. var startOffset = this._offset(start)
  170. , endOffset = this._offset(end)
  171. , buffers = this._bufs.slice(startOffset[0], endOffset[0] + 1)
  172. if (endOffset[1] == 0)
  173. buffers.pop()
  174. else
  175. buffers[buffers.length-1] = buffers[buffers.length-1].slice(0, endOffset[1])
  176. if (startOffset[1] != 0)
  177. buffers[0] = buffers[0].slice(startOffset[1])
  178. return new BufferList(buffers)
  179. }
  180. BufferList.prototype.toString = function toString (encoding, start, end) {
  181. return this.slice(start, end).toString(encoding)
  182. }
  183. BufferList.prototype.consume = function consume (bytes) {
  184. while (this._bufs.length) {
  185. if (bytes >= this._bufs[0].length) {
  186. bytes -= this._bufs[0].length
  187. this.length -= this._bufs[0].length
  188. this._bufs.shift()
  189. } else {
  190. this._bufs[0] = this._bufs[0].slice(bytes)
  191. this.length -= bytes
  192. break
  193. }
  194. }
  195. return this
  196. }
  197. BufferList.prototype.duplicate = function duplicate () {
  198. var i = 0
  199. , copy = new BufferList()
  200. for (; i < this._bufs.length; i++)
  201. copy.append(this._bufs[i])
  202. return copy
  203. }
  204. BufferList.prototype._destroy = function _destroy (err, cb) {
  205. this._bufs.length = 0
  206. this.length = 0
  207. cb(err)
  208. }
  209. BufferList.prototype.indexOf = function (search, offset, encoding) {
  210. if (encoding === undefined && typeof offset === 'string') {
  211. encoding = offset
  212. offset = undefined
  213. }
  214. if (typeof search === 'function' || Array.isArray(search)) {
  215. throw new TypeError('The "value" argument must be one of type string, Buffer, BufferList, or Uint8Array.')
  216. } else if (typeof search === 'number') {
  217. search = Buffer.from([search])
  218. } else if (typeof search === 'string') {
  219. search = Buffer.from(search, encoding)
  220. } else if (search instanceof BufferList) {
  221. search = search.slice()
  222. } else if (!Buffer.isBuffer(search)) {
  223. search = Buffer.from(search)
  224. }
  225. offset = Number(offset || 0)
  226. if (isNaN(offset)) {
  227. offset = 0
  228. }
  229. if (offset < 0) {
  230. offset = this.length + offset
  231. }
  232. if (offset < 0) {
  233. offset = 0
  234. }
  235. if (search.length === 0) {
  236. return offset > this.length ? this.length : offset
  237. }
  238. var blOffset = this._offset(offset)
  239. var blIndex = blOffset[0] // index of which internal buffer we're working on
  240. var buffOffset = blOffset[1] // offset of the internal buffer we're working on
  241. // scan over each buffer
  242. for (blIndex; blIndex < this._bufs.length; blIndex++) {
  243. var buff = this._bufs[blIndex]
  244. while(buffOffset < buff.length) {
  245. var availableWindow = buff.length - buffOffset
  246. if (availableWindow >= search.length) {
  247. var nativeSearchResult = buff.indexOf(search, buffOffset)
  248. if (nativeSearchResult !== -1) {
  249. return this._reverseOffset([blIndex, nativeSearchResult])
  250. }
  251. buffOffset = buff.length - search.length + 1 // end of native search window
  252. } else {
  253. var revOffset = this._reverseOffset([blIndex, buffOffset])
  254. if (this._match(revOffset, search)) {
  255. return revOffset
  256. }
  257. buffOffset++
  258. }
  259. }
  260. buffOffset = 0
  261. }
  262. return -1
  263. }
  264. BufferList.prototype._match = function(offset, search) {
  265. if (this.length - offset < search.length) {
  266. return false
  267. }
  268. for (var searchOffset = 0; searchOffset < search.length ; searchOffset++) {
  269. if(this.get(offset + searchOffset) !== search[searchOffset]){
  270. return false
  271. }
  272. }
  273. return true
  274. }
  275. ;(function () {
  276. var methods = {
  277. 'readDoubleBE' : 8
  278. , 'readDoubleLE' : 8
  279. , 'readFloatBE' : 4
  280. , 'readFloatLE' : 4
  281. , 'readInt32BE' : 4
  282. , 'readInt32LE' : 4
  283. , 'readUInt32BE' : 4
  284. , 'readUInt32LE' : 4
  285. , 'readInt16BE' : 2
  286. , 'readInt16LE' : 2
  287. , 'readUInt16BE' : 2
  288. , 'readUInt16LE' : 2
  289. , 'readInt8' : 1
  290. , 'readUInt8' : 1
  291. , 'readIntBE' : null
  292. , 'readIntLE' : null
  293. , 'readUIntBE' : null
  294. , 'readUIntLE' : null
  295. }
  296. for (var m in methods) {
  297. (function (m) {
  298. if (methods[m] === null) {
  299. BufferList.prototype[m] = function (offset, byteLength) {
  300. return this.slice(offset, offset + byteLength)[m](0, byteLength)
  301. }
  302. }
  303. else {
  304. BufferList.prototype[m] = function (offset) {
  305. return this.slice(offset, offset + methods[m])[m](0)
  306. }
  307. }
  308. }(m))
  309. }
  310. }())
  311. module.exports = BufferList