• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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