• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  SymbolIterator,
5  Uint8Array,
6} = primordials;
7
8const { Buffer } = require('buffer');
9const { inspect } = require('internal/util/inspect');
10
11module.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