'use strict' const { StringPrototypeSlice, SymbolIterator, TypedArrayPrototypeSet, Uint8Array } = require('../../ours/primordials') const { Buffer } = require('buffer') const { inspect } = require('../../ours/util') module.exports = class BufferList { constructor() { this.head = null this.tail = null this.length = 0 } push(v) { const entry = { data: v, next: null } if (this.length > 0) this.tail.next = entry else this.head = entry this.tail = entry ++this.length } unshift(v) { const entry = { data: v, next: this.head } if (this.length === 0) this.tail = entry this.head = entry ++this.length } shift() { if (this.length === 0) return const ret = this.head.data if (this.length === 1) this.head = this.tail = null else this.head = this.head.next --this.length return ret } clear() { this.head = this.tail = null this.length = 0 } join(s) { if (this.length === 0) return '' let p = this.head let ret = '' + p.data while ((p = p.next) !== null) ret += s + p.data return ret } concat(n) { if (this.length === 0) return Buffer.alloc(0) const ret = Buffer.allocUnsafe(n >>> 0) let p = this.head let i = 0 while (p) { TypedArrayPrototypeSet(ret, p.data, i) i += p.data.length p = p.next } return ret } // Consumes a specified amount of bytes or characters from the buffered data. consume(n, hasStrings) { const data = this.head.data if (n < data.length) { // `slice` is the same for buffers and strings. const slice = data.slice(0, n) this.head.data = data.slice(n) return slice } if (n === data.length) { // First chunk is a perfect match. return this.shift() } // Result spans more than one buffer. return hasStrings ? this._getString(n) : this._getBuffer(n) } first() { return this.head.data } *[SymbolIterator]() { for (let p = this.head; p; p = p.next) { yield p.data } } // Consumes a specified amount of characters from the buffered data. _getString(n) { let ret = '' let p = this.head let c = 0 do { const str = p.data if (n > str.length) { ret += str n -= str.length } else { if (n === str.length) { ret += str ++c if (p.next) this.head = p.next else this.head = this.tail = null } else { ret += StringPrototypeSlice(str, 0, n) this.head = p p.data = StringPrototypeSlice(str, n) } break } ++c } while ((p = p.next) !== null) this.length -= c return ret } // Consumes a specified amount of bytes from the buffered data. _getBuffer(n) { const ret = Buffer.allocUnsafe(n) const retLen = n let p = this.head let c = 0 do { const buf = p.data if (n > buf.length) { TypedArrayPrototypeSet(ret, buf, retLen - n) n -= buf.length } else { if (n === buf.length) { TypedArrayPrototypeSet(ret, buf, retLen - n) ++c if (p.next) this.head = p.next else this.head = this.tail = null } else { TypedArrayPrototypeSet(ret, new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n) this.head = p p.data = buf.slice(n) } break } ++c } while ((p = p.next) !== null) this.length -= c return ret } // Make sure the linked list only shows the minimal necessary information. [Symbol.for('nodejs.util.inspect.custom')](_, options) { return inspect(this, { ...options, // Only inspect one level. depth: 0, // It should not recurse. customInspect: false }) } }