Skip to content

Commit bfe321b

Browse files
author
Benjamin Pasero
committed
files2 - first cut buffered read impl
1 parent ffe5db3 commit bfe321b

8 files changed

Lines changed: 1081 additions & 154 deletions

File tree

src/vs/base/common/buffer.ts

Lines changed: 246 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ let textDecoder: TextDecoder | null;
1111

1212
export class VSBuffer {
1313

14-
public static alloc(byteLength: number): VSBuffer {
14+
static alloc(byteLength: number): VSBuffer {
1515
if (hasBuffer) {
1616
return new VSBuffer(Buffer.allocUnsafe(byteLength));
1717
} else {
1818
return new VSBuffer(new Uint8Array(byteLength));
1919
}
2020
}
2121

22-
public static wrap(actual: Uint8Array): VSBuffer {
22+
static wrap(actual: Uint8Array): VSBuffer {
2323
if (hasBuffer && !(Buffer.isBuffer(actual))) {
2424
// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
2525
// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array
@@ -28,7 +28,7 @@ export class VSBuffer {
2828
return new VSBuffer(actual);
2929
}
3030

31-
public static fromString(source: string): VSBuffer {
31+
static fromString(source: string): VSBuffer {
3232
if (hasBuffer) {
3333
return new VSBuffer(Buffer.from(source));
3434
} else {
@@ -39,7 +39,7 @@ export class VSBuffer {
3939
}
4040
}
4141

42-
public static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
42+
static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
4343
if (typeof totalLength === 'undefined') {
4444
totalLength = 0;
4545
for (let i = 0, len = buffers.length; i < len; i++) {
@@ -58,15 +58,15 @@ export class VSBuffer {
5858
return ret;
5959
}
6060

61-
public readonly buffer: Uint8Array;
62-
public readonly byteLength: number;
61+
readonly buffer: Uint8Array;
62+
readonly byteLength: number;
6363

6464
private constructor(buffer: Uint8Array) {
6565
this.buffer = buffer;
6666
this.byteLength = this.buffer.byteLength;
6767
}
6868

69-
public toString(): string {
69+
toString(): string {
7070
if (hasBuffer) {
7171
return this.buffer.toString();
7272
} else {
@@ -77,30 +77,29 @@ export class VSBuffer {
7777
}
7878
}
7979

80-
public slice(start?: number, end?: number): VSBuffer {
80+
slice(start?: number, end?: number): VSBuffer {
8181
return new VSBuffer(this.buffer.slice(start, end));
8282
}
8383

84-
public set(array: VSBuffer, offset?: number): void {
84+
set(array: VSBuffer, offset?: number): void {
8585
this.buffer.set(array.buffer, offset);
8686
}
8787

88-
public readUint32BE(offset: number): number {
88+
readUint32BE(offset: number): number {
8989
return readUint32BE(this.buffer, offset);
9090
}
9191

92-
public writeUint32BE(value: number, offset: number): void {
92+
writeUint32BE(value: number, offset: number): void {
9393
writeUint32BE(this.buffer, value, offset);
9494
}
9595

96-
public readUint8(offset: number): number {
96+
readUint8(offset: number): number {
9797
return readUint8(this.buffer, offset);
9898
}
9999

100-
public writeUint8(value: number, offset: number): void {
100+
writeUint8(value: number, offset: number): void {
101101
writeUint8(this.buffer, value, offset);
102102
}
103-
104103
}
105104

106105
function readUint32BE(source: Uint8Array, offset: number): number {
@@ -139,6 +138,27 @@ export interface VSBufferReadable {
139138
read(): VSBuffer | null;
140139
}
141140

141+
export interface VSBufferReadableStream {
142+
143+
/**
144+
* The 'data' event is emitted whenever the stream is
145+
* relinquishing ownership of a chunk of data to a consumer.
146+
*/
147+
on(event: 'data', callback: (chunk: VSBuffer) => void): void;
148+
149+
/**
150+
* Emitted when any error occurs.
151+
*/
152+
on(event: 'error', callback: (err: any) => void): void;
153+
154+
/**
155+
* The 'end' event is emitted when there is no more data
156+
* to be consumed from the stream. The 'end' event will
157+
* not be emitted unless the data is completely consumed.
158+
*/
159+
on(event: 'end', callback: () => void): void;
160+
}
161+
142162
/**
143163
* Helper to fully read a VSBuffer readable into a single buffer.
144164
*/
@@ -158,6 +178,7 @@ export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
158178
*/
159179
export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
160180
let done = false;
181+
161182
return {
162183
read: () => {
163184
if (done) {
@@ -169,4 +190,215 @@ export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
169190
return buffer;
170191
}
171192
};
193+
}
194+
195+
/**
196+
* Helper to fully read a VSBuffer stream into a single buffer.
197+
*/
198+
export function streamToBuffer(stream: VSBufferReadableStream): Promise<VSBuffer> {
199+
return new Promise((resolve, reject) => {
200+
const chunks: VSBuffer[] = [];
201+
202+
stream.on('data', chunk => chunks.push(chunk));
203+
stream.on('error', error => reject(error));
204+
stream.on('end', () => resolve(VSBuffer.concat(chunks)));
205+
});
206+
}
207+
208+
/**
209+
* Helper to create a VSBufferStream from an existing VSBuffer.
210+
*/
211+
export function bufferToStream(buffer: VSBuffer): VSBufferReadableStream {
212+
const stream = writeableBufferStream();
213+
214+
stream.end(buffer);
215+
216+
return stream;
217+
}
218+
219+
/**
220+
* Helper to create a VSBufferStream that can be pushed
221+
* buffers to. Will only start to emit data when a listener
222+
* is added.
223+
*/
224+
export function writeableBufferStream(): VSBufferWriteableStream {
225+
return new VSBufferWriteableStreamImpl();
226+
}
227+
228+
export interface VSBufferWriteableStream extends VSBufferReadableStream {
229+
data(chunk: VSBuffer): void;
230+
error(error: Error): void;
231+
end(result?: VSBuffer | Error): void;
232+
}
233+
234+
class VSBufferWriteableStreamImpl implements VSBufferWriteableStream {
235+
236+
private readonly state = {
237+
flowing: false,
238+
ended: false,
239+
finished: false
240+
};
241+
242+
private readonly buffer = {
243+
data: [] as VSBuffer[],
244+
error: [] as Error[]
245+
};
246+
247+
private readonly listeners = {
248+
data: [] as { (chunk: VSBuffer): void }[],
249+
error: [] as { (error: Error): void }[],
250+
end: [] as { (): void }[]
251+
};
252+
253+
data(chunk: VSBuffer): void {
254+
if (this.state.finished) {
255+
return;
256+
}
257+
258+
// flowing: directly send the data to listeners
259+
if (this.state.flowing) {
260+
this.listeners.data.forEach(listener => listener(chunk));
261+
}
262+
263+
// not yet flowing: buffer data until flowing
264+
else {
265+
this.buffer.data.push(chunk);
266+
}
267+
}
268+
269+
error(error: Error): void {
270+
if (this.state.finished) {
271+
return;
272+
}
273+
274+
// flowing: directly send the error to listeners
275+
if (this.state.flowing) {
276+
this.listeners.error.forEach(listener => listener(error));
277+
}
278+
279+
// not yet flowing: buffer errors until flowing
280+
else {
281+
this.buffer.error.push(error);
282+
}
283+
}
284+
285+
end(result?: VSBuffer | Error): void {
286+
if (this.state.finished) {
287+
return;
288+
}
289+
290+
// end with data or error if provided
291+
if (result instanceof Error) {
292+
this.error(result);
293+
} else if (result) {
294+
this.data(result);
295+
}
296+
297+
// flowing: send end event to listeners
298+
if (this.state.flowing) {
299+
this.listeners.end.forEach(listener => listener());
300+
301+
this.finish();
302+
}
303+
304+
// not yet flowing: remember state
305+
else {
306+
this.state.ended = true;
307+
}
308+
}
309+
310+
on(event: 'data', callback: (chunk: VSBuffer) => void): void;
311+
on(event: 'error', callback: (err: any) => void): void;
312+
on(event: 'end', callback: () => void): void;
313+
on(event: 'data' | 'error' | 'end', callback: (arg0?: any) => void): void {
314+
if (this.state.finished) {
315+
return;
316+
}
317+
318+
switch (event) {
319+
case 'data':
320+
this.listeners.data.push(callback);
321+
322+
// switch into flowing mode as soon as the first 'data'
323+
// listener is added and we are not yet in flowing mode
324+
if (!this.state.flowing) {
325+
this.state.flowing = true;
326+
327+
// emit buffered events
328+
this.flowData();
329+
this.flowErrors();
330+
this.flowEnd();
331+
}
332+
333+
break;
334+
335+
case 'end':
336+
this.listeners.end.push(callback);
337+
338+
// emit 'end' event directly if we are flowing
339+
// and the end has already been reached
340+
//
341+
// finish() when it went through
342+
if (this.state.flowing && this.flowEnd()) {
343+
this.finish();
344+
}
345+
346+
break;
347+
348+
case 'error':
349+
this.listeners.error.push(callback);
350+
351+
// emit buffered 'error' events unless done already
352+
// now that we know that we have at least one listener
353+
if (this.state.flowing) {
354+
this.flowErrors();
355+
}
356+
357+
break;
358+
}
359+
}
360+
361+
private flowData(): void {
362+
if (this.buffer.data.length > 0) {
363+
const fullDataBuffer = VSBuffer.concat(this.buffer.data);
364+
365+
this.listeners.data.forEach(listener => listener(fullDataBuffer));
366+
367+
this.buffer.data.length = 0;
368+
}
369+
}
370+
371+
private flowErrors(): void {
372+
if (this.listeners.error.length > 0) {
373+
for (const error of this.buffer.error) {
374+
this.listeners.error.forEach(listener => listener(error));
375+
}
376+
377+
this.buffer.error.length = 0;
378+
}
379+
}
380+
381+
private flowEnd(): boolean {
382+
if (this.state.ended) {
383+
this.listeners.end.forEach(listener => listener());
384+
385+
return this.listeners.end.length > 0;
386+
}
387+
388+
return false;
389+
}
390+
391+
private finish(): void {
392+
if (!this.state.finished) {
393+
this.state.finished = true;
394+
this.state.ended = true;
395+
396+
this.buffer.data.length = 0;
397+
this.buffer.error.length = 0;
398+
399+
this.listeners.data.length = 0;
400+
this.listeners.error.length = 0;
401+
this.listeners.end.length = 0;
402+
}
403+
}
172404
}

0 commit comments

Comments
 (0)