Skip to content

Commit

Permalink
feat: 🎸 add writev() method
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 20, 2023
1 parent 5971c39 commit 17b0446
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 30 deletions.
34 changes: 34 additions & 0 deletions src/fsa-to-node/FsaNodeFs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import type * as misc from '../node/types/misc';
import type * as opts from '../node/types/options';
import type * as fsa from '../fsa/types';
import type { FsCommonObjects } from '../node/types/FsCommonObjects';
import type {WritevCallback} from '../node/types/callback';

const notSupported: (...args: any[]) => any = () => {
throw new Error('Method not supported by the File System Access API.');
Expand Down Expand Up @@ -264,6 +265,39 @@ export class FsaNodeFs implements FsCallbackApi, FsSynchronousApi, FsCommonObjec
);
};

public readonly writev: FsCallbackApi['writev'] = (
fd: number,
buffers: ArrayBufferView[],
a: number | null | WritevCallback,
b?: WritevCallback
): void => {
validateFd(fd);
let position: number | null = null;
let callback: WritevCallback;
if (typeof a === 'function') {
callback = a;
} else {
position = Number(a);
callback = <WritevCallback>b;
}
validateCallback(callback);
(async () => {
const openFile = await this.getFileByFd(fd, 'writev');
const length = buffers.length;
let bytesWritten = 0;
for (let i = 0; i < length; i++) {
const data = buffers[i];
await openFile.write(data, position);
bytesWritten += data.byteLength;
position = null;
}
return bytesWritten;
})().then(
bytesWritten => callback(null, bytesWritten, buffers),
error => callback(error),
);
};

public readonly writeFile: FsCallbackApi['writeFile'] = (
id: misc.TFileId,
data: misc.TData,
Expand Down
4 changes: 2 additions & 2 deletions src/fsa-to-node/FsaNodeFsOpenFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class FsaNodeFsOpenFile {

public async close(): Promise<void> {}

public async write(data: Uint8Array, seek: number | null): Promise<void> {
public async write(data: ArrayBufferView, seek: number | null): Promise<void> {
if (typeof seek !== 'number') seek = this.seek;
const writer = await this.file.createWritable({ keepExistingData: this.keepExistingData });
await writer.write({
Expand All @@ -38,6 +38,6 @@ export class FsaNodeFsOpenFile {
});
await writer.close();
this.keepExistingData = true;
this.seek += data.length;
this.seek += data.byteLength;
}
}
24 changes: 24 additions & 0 deletions src/fsa-to-node/__tests__/FsaNodeFs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,30 @@ describe('.write()', () => {
});
});

describe('.writev()', () => {
test('can write to a file two buffers', async () => {
const { fs, mfs } = setup({});
const fd = await new Promise<number>((resolve, reject) =>
fs.open('/test.txt', 'w', (err, fd) => {
if (err) reject(err);
else resolve(fd!);
}),
);
const [bytesWritten, data] = await new Promise<[number, any]>((resolve, reject) => {
const buffers = [
Buffer.from('a'),
Buffer.from('b'),
];
fs.writev(fd, buffers, (err, bytesWritten, data) => {
if (err) reject(err);
else resolve([bytesWritten!, data]);
});
});
expect(bytesWritten).toBe(2);
expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('ab');
});
});

describe('.exists()', () => {
test('can works for folders and files', async () => {
const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' });
Expand Down
58 changes: 31 additions & 27 deletions src/node/types/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,6 @@ export interface FsCallbackApi {
): void;
readFile(id: misc.TFileId, callback: misc.TCallback<misc.TDataOut>);
readFile(id: misc.TFileId, options: opts.IReadFileOptions | string, callback: misc.TCallback<misc.TDataOut>);
write(fd: number, buffer: Buffer | ArrayBufferView | DataView, callback: (...args) => void);
write(fd: number, buffer: Buffer | ArrayBufferView | DataView, offset: number, callback: (...args) => void);
write(
fd: number,
buffer: Buffer | ArrayBufferView | DataView,
offset: number,
length: number,
callback: (...args) => void,
);
write(
fd: number,
buffer: Buffer | ArrayBufferView | DataView,
offset: number,
length: number,
position: number,
callback: (...args) => void,
);
write(fd: number, str: string, callback: (...args) => void);
write(fd: number, str: string, position: number, callback: (...args) => void);
write(fd: number, str: string, position: number, encoding: BufferEncoding, callback: (...args) => void);
writeFile(id: misc.TFileId, data: misc.TData, callback: misc.TCallback<void>);
writeFile(
id: misc.TFileId,
data: misc.TData,
options: opts.IWriteFileOptions | string,
callback: misc.TCallback<void>,
);
copyFile(src: misc.PathLike, dest: misc.PathLike, callback: misc.TCallback<void>);
copyFile(src: misc.PathLike, dest: misc.PathLike, flags: misc.TFlagsCopy, callback: misc.TCallback<void>);
link(existingPath: misc.PathLike, newPath: misc.PathLike, callback: misc.TCallback<void>): void;
Expand Down Expand Up @@ -117,4 +90,35 @@ export interface FsCallbackApi {
options?: opts.IWatchOptions | string,
listener?: (eventType: string, filename: string) => void,
): misc.IFSWatcher;
write(fd: number, buffer: Buffer | ArrayBufferView | DataView, callback: (...args) => void);
write(fd: number, buffer: Buffer | ArrayBufferView | DataView, offset: number, callback: (...args) => void);
write(
fd: number,
buffer: Buffer | ArrayBufferView | DataView,
offset: number,
length: number,
callback: (...args) => void,
);
write(
fd: number,
buffer: Buffer | ArrayBufferView | DataView,
offset: number,
length: number,
position: number,
callback: (...args) => void,
);
write(fd: number, str: string, callback: (...args) => void);
write(fd: number, str: string, position: number, callback: (...args) => void);
write(fd: number, str: string, position: number, encoding: BufferEncoding, callback: (...args) => void);
writeFile(id: misc.TFileId, data: misc.TData, callback: misc.TCallback<void>);
writeFile(
id: misc.TFileId,
data: misc.TData,
options: opts.IWriteFileOptions | string,
callback: misc.TCallback<void>,
);
writev(fd: number, buffers: ArrayBufferView[], callback: WritevCallback): void;
writev(fd: number, buffers: ArrayBufferView[], position: number | null, callback: WritevCallback): void;
}

export type WritevCallback = (err: Error | null, bytesWritten?: number, buffers?: ArrayBufferView[]) => void;
1 change: 0 additions & 1 deletion src/node/types/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type {
IReadStreamOptions,
IStatOptions,
IWriteFileOptions,
IWriteStreamOptions,
} from './options';
import type { Readable, Writable } from 'stream';

Expand Down
10 changes: 10 additions & 0 deletions src/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
bufferToEncoding,
} from './node/util';
import type { PathLike, symlink } from 'fs';
import {WritevCallback} from './node/types/callback';

const resolveCrossPlatform = pathModule.resolve;
const {
Expand Down Expand Up @@ -973,6 +974,12 @@ export class Volume {
});
}

writev(fd: number, buffers: ArrayBufferView[], callback: WritevCallback): void;
writev(fd: number, buffers: ArrayBufferView[], position: number | null, callback: WritevCallback): void;
writev(fd: number, buffers: ArrayBufferView[], a, b?): void {
throw new Error('not implemented');
}

private writeFileBase(id: TFileId, buf: Buffer, flagsNum: number, modeNum: number) {
// console.log('writeFileBase', id, buf, flagsNum, modeNum);
// const node = this.getNodeByIdOrCreate(id, flagsNum, modeNum);
Expand Down Expand Up @@ -2198,6 +2205,7 @@ function closeOnOpen(fd) {
export interface IWriteStream extends Writable {
bytesWritten: number;
path: string;
pending: boolean;
new (path: PathLike, options: IWriteStreamOptions);
open();
close();
Expand All @@ -2222,6 +2230,7 @@ function FsWriteStream(vol, path, options) {
this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
this.pos = undefined;
this.bytesWritten = 0;
this.pending = true;

if (this.start !== undefined) {
if (typeof this.start !== 'number') {
Expand Down Expand Up @@ -2261,6 +2270,7 @@ FsWriteStream.prototype.open = function () {
}

this.fd = fd;
this.pending = false;
this.emit('open', fd);
}.bind(this),
);
Expand Down

0 comments on commit 17b0446

Please sign in to comment.