Skip to content

Commit

Permalink
Adds support for Uint8Array handling to stdin/stdout/stderr.
Browse files Browse the repository at this point in the history
1. `stdin` now accepts Uint8Arrays directly.
2. Uint8Arrays are sent to `stdout/stderr` handlers, when `outputBuffers: true` is passed to `useStdio({})` options.
  • Loading branch information
arjunbajaj committed Sep 8, 2024
1 parent 04f3750 commit 570be9d
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ const wasi = new WASI({
});
```

By default, the `stdout` and `stderr` handlers are passed strings. You can pass `outputBuffers: true` to get `Uint8Array` buffers instead. Along with that, you can also pass `Uint8Array` buffers to `stdin`.

```js
import { WASI, useStdio } from "uwasi";
const wasi = new WASI({
features: [useStdio({
outputBuffers: true,
stdin: () => new Uint8Array([1, 2, 3, 4, 5]),
stdout: (buf) => console.log(buf),
stderr: (buf) => console.error(buf),
})],
});
```

## Implementation Status

Some of WASI system calls are not implemented yet. Contributions are welcome!
Expand Down
40 changes: 29 additions & 11 deletions src/features/fd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ interface FdEntry {

class WritableTextProxy implements FdEntry {
private decoder = new TextDecoder('utf-8');
constructor(private readonly handler: (lines: string) => void) { }
constructor(
private readonly handler: (lines: string | Uint8Array) => void,
private readonly outputBuffers: boolean
) { }

writev(iovs: Uint8Array[]): number {
const totalBufferSize = iovs.reduce((acc, iov) => acc + iov.byteLength, 0);
Expand All @@ -20,8 +23,13 @@ class WritableTextProxy implements FdEntry {
offset += buffer.byteLength;
}

const lines = this.decoder.decode(concatBuffer);
this.handler(lines);
if (this.outputBuffers) {
this.handler(concatBuffer);
} else {
const lines = this.decoder.decode(concatBuffer);
this.handler(lines);
}

return concatBuffer.length;
}
readv(_iovs: Uint8Array[]): number {
Expand All @@ -33,7 +41,7 @@ class WritableTextProxy implements FdEntry {
export class ReadableTextProxy implements FdEntry {
private encoder = new TextEncoder();
private pending: Uint8Array | null = null;
constructor(private readonly consume: () => string) { }
constructor(private readonly consume: () => string | Uint8Array) { }

writev(_iovs: Uint8Array[]): number {
return 0;
Expand All @@ -59,8 +67,15 @@ export class ReadableTextProxy implements FdEntry {
read += consumed.byteLength;
}
while (remaining > 0) {
const newText = this.consume();
const bytes = this.encoder.encode(newText);
const newData = this.consume();
let bytes: Uint8Array;

if (newData instanceof Uint8Array) {
bytes = newData;
} else {
bytes = this.encoder.encode(newData);
}

if (bytes.length == 0) {
return read;
}
Expand Down Expand Up @@ -108,16 +123,19 @@ export class ReadableTextProxy implements FdEntry {
*/
export function useStdio(
useOptions: {
stdin?: () => string,
stdout?: (lines: string) => void,
stderr?: (lines: string) => void,
stdin?: () => string | Uint8Array,
stdout?: (lines: string | Uint8Array) => void,
stderr?: (lines: string | Uint8Array) => void,
outputBuffers?: boolean,
} = {}
): WASIFeatureProvider {
return (options, abi, memoryView) => {
const outputBuffers = useOptions.outputBuffers || false;

const fdTable = [
new ReadableTextProxy(useOptions.stdin || (() => { return "" })),
new WritableTextProxy(useOptions.stdout || console.log),
new WritableTextProxy(useOptions.stderr || console.error),
new WritableTextProxy(useOptions.stdout || console.log, outputBuffers),
new WritableTextProxy(useOptions.stderr || console.error, outputBuffers),
]
return {
fd_fdstat_get: (fd: number, buf: number) => {
Expand Down

0 comments on commit 570be9d

Please sign in to comment.