1 'use strict'; 2 3 const { 4 SymbolIterator, 5 Uint8Array, 6 } = primordials; 7 8 const { Buffer } = require('buffer'); 9 const { inspect } = require('internal/util/inspect'); 10 11 module.exports = class BufferList { 12 constructor() { 13 this.head = null; 14 this.tail = null; 15 this.length = 0; 16 } 17 18 push(v) { 19 const entry = { data: v, next: null }; 20 if (this.length > 0) 21 this.tail.next = entry; 22 else 23 this.head = entry; 24 this.tail = entry; 25 ++this.length; 26 } 27 28 unshift(v) { 29 const entry = { data: v, next: this.head }; 30 if (this.length === 0) 31 this.tail = entry; 32 this.head = entry; 33 ++this.length; 34 } 35 36 shift() { 37 if (this.length === 0) 38 return; 39 const ret = this.head.data; 40 if (this.length === 1) 41 this.head = this.tail = null; 42 else 43 this.head = this.head.next; 44 --this.length; 45 return ret; 46 } 47 48 clear() { 49 this.head = this.tail = null; 50 this.length = 0; 51 } 52 53 join(s) { 54 if (this.length === 0) 55 return ''; 56 let p = this.head; 57 let ret = '' + p.data; 58 while (p = p.next) 59 ret += s + p.data; 60 return ret; 61 } 62 63 concat(n) { 64 if (this.length === 0) 65 return Buffer.alloc(0); 66 const ret = Buffer.allocUnsafe(n >>> 0); 67 let p = this.head; 68 let i = 0; 69 while (p) { 70 ret.set(p.data, i); 71 i += p.data.length; 72 p = p.next; 73 } 74 return ret; 75 } 76 77 // Consumes a specified amount of bytes or characters from the buffered data. 78 consume(n, hasStrings) { 79 const data = this.head.data; 80 if (n < data.length) { 81 // `slice` is the same for buffers and strings. 82 const slice = data.slice(0, n); 83 this.head.data = data.slice(n); 84 return slice; 85 } 86 if (n === data.length) { 87 // First chunk is a perfect match. 88 return this.shift(); 89 } 90 // Result spans more than one buffer. 91 return hasStrings ? this._getString(n) : this._getBuffer(n); 92 } 93 94 first() { 95 return this.head.data; 96 } 97 98 *[SymbolIterator]() { 99 for (let p = this.head; p; p = p.next) { 100 yield p.data; 101 } 102 } 103 104 // Consumes a specified amount of characters from the buffered data. 105 _getString(n) { 106 let ret = ''; 107 let p = this.head; 108 let c = 0; 109 do { 110 const str = p.data; 111 if (n > str.length) { 112 ret += str; 113 n -= str.length; 114 } else { 115 if (n === str.length) { 116 ret += str; 117 ++c; 118 if (p.next) 119 this.head = p.next; 120 else 121 this.head = this.tail = null; 122 } else { 123 ret += str.slice(0, n); 124 this.head = p; 125 p.data = str.slice(n); 126 } 127 break; 128 } 129 ++c; 130 } while (p = p.next); 131 this.length -= c; 132 return ret; 133 } 134 135 // Consumes a specified amount of bytes from the buffered data. 136 _getBuffer(n) { 137 const ret = Buffer.allocUnsafe(n); 138 const retLen = n; 139 let p = this.head; 140 let c = 0; 141 do { 142 const buf = p.data; 143 if (n > buf.length) { 144 ret.set(buf, retLen - n); 145 n -= buf.length; 146 } else { 147 if (n === buf.length) { 148 ret.set(buf, retLen - n); 149 ++c; 150 if (p.next) 151 this.head = p.next; 152 else 153 this.head = this.tail = null; 154 } else { 155 ret.set(new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n); 156 this.head = p; 157 p.data = buf.slice(n); 158 } 159 break; 160 } 161 ++c; 162 } while (p = p.next); 163 this.length -= c; 164 return ret; 165 } 166 167 // Make sure the linked list only shows the minimal necessary information. 168 [inspect.custom](_, options) { 169 return inspect(this, { 170 ...options, 171 // Only inspect one level. 172 depth: 0, 173 // It should not recurse. 174 customInspect: false 175 }); 176 } 177 }; 178