1
1
import { WASIAbi } from "../abi" ;
2
2
import { WASIFeatureProvider , WASIOptions } from "../options" ;
3
3
4
- const iovec_t = {
5
- size : 8 ,
6
- bufferOffset : 0 ,
7
- lengthOffset : 4 ,
4
+ interface FdEntry {
5
+ writev ( iovs : Uint8Array [ ] ) : number
6
+ readv ( iovs : Uint8Array [ ] ) : number
7
+ close ( ) : void
8
+ }
9
+
10
+ class WritableTextProxy implements FdEntry {
11
+ private decoder = new TextDecoder ( 'utf-8' ) ;
12
+ constructor ( private readonly handler : ( lines : string ) => void ) { }
13
+
14
+ writev ( iovs : Uint8Array [ ] ) : number {
15
+ const totalBufferSize = iovs . reduce ( ( acc , iov ) => acc + iov . byteLength , 0 ) ;
16
+ let offset = 0 ;
17
+ const concatBuffer = new Uint8Array ( totalBufferSize ) ;
18
+ for ( const buffer of iovs ) {
19
+ concatBuffer . set ( buffer , offset ) ;
20
+ offset += buffer . length ;
21
+ }
22
+
23
+ const lines = this . decoder . decode ( concatBuffer ) ;
24
+ this . handler ( lines ) ;
25
+ return concatBuffer . length ;
26
+ }
27
+ readv ( _iovs : Uint8Array [ ] ) : number {
28
+ return 0 ;
29
+ }
30
+ close ( ) : void { }
31
+ }
32
+
33
+ class ReadableTextProxy implements FdEntry {
34
+ private encoder = new TextEncoder ( ) ;
35
+ private pending : Uint8Array | null ;
36
+ constructor ( private readonly consume : ( ) => string ) { }
37
+
38
+ writev ( _iovs : Uint8Array [ ] ) : number {
39
+ return 0 ;
40
+ }
41
+ readv ( iovs : Uint8Array [ ] ) : number {
42
+ let read = 0 ;
43
+ for ( const buffer of iovs ) {
44
+ let remaining = buffer . byteLength ;
45
+ if ( this . pending ) {
46
+ const reading = Math . min ( remaining , this . pending . byteLength ) ;
47
+ buffer . set ( this . pending . slice ( 0 , reading ) , 0 ) ;
48
+ remaining -= reading ;
49
+ if ( remaining < this . pending . byteLength ) {
50
+ this . pending = this . pending . slice ( reading ) ;
51
+ continue ;
52
+ } else {
53
+ this . pending = null ;
54
+ }
55
+ }
56
+ while ( remaining > 0 ) {
57
+ const newText = this . consume ( ) ;
58
+ const bytes = this . encoder . encode ( newText ) ;
59
+ if ( bytes . length == 0 ) {
60
+ return read ;
61
+ }
62
+ if ( bytes . length > remaining ) {
63
+ buffer . set ( bytes . slice ( 0 , remaining ) , buffer . byteLength - remaining ) ;
64
+ this . pending = bytes . slice ( remaining ) ;
65
+ read += remaining ;
66
+ remaining = 0 ;
67
+ } else {
68
+ buffer . set ( bytes , buffer . byteLength - remaining ) ;
69
+ read += bytes . length ;
70
+ remaining -= bytes . length ;
71
+ }
72
+ }
73
+ }
74
+ return read ;
75
+ }
76
+ close ( ) : void { }
8
77
}
9
78
10
79
/**
@@ -34,15 +103,21 @@ const iovec_t = {
34
103
*/
35
104
export function useStdio (
36
105
useOptions : {
106
+ stdin : ( ) => string ,
37
107
stdout : ( lines : string ) => void ,
38
- stderr : ( lines : string ) => void
108
+ stderr : ( lines : string ) => void ,
39
109
} = {
110
+ stdin : ( ) => { return "" } ,
40
111
stdout : console . log ,
41
112
stderr : console . error ,
42
113
}
43
114
) : WASIFeatureProvider {
44
115
return ( options , abi , memoryView ) => {
45
- const decoder = new TextDecoder ( 'utf-8' ) ;
116
+ const fdTable = [
117
+ new ReadableTextProxy ( useOptions . stdin ) ,
118
+ new WritableTextProxy ( useOptions . stdout ) ,
119
+ new WritableTextProxy ( useOptions . stderr ) ,
120
+ ]
46
121
return {
47
122
fd_prestat_get : ( fd : number , buf : number ) => {
48
123
return WASIAbi . WASI_ERRNO_BADF ;
@@ -51,35 +126,21 @@ export function useStdio(
51
126
return WASIAbi . WASI_ERRNO_BADF ;
52
127
} ,
53
128
fd_write : ( fd : number , iovs : number , iovsLen : number , nwritten : number ) => {
54
- if ( fd > 2 ) return WASIAbi . WASI_ERRNO_BADF ;
55
-
129
+ const fdEntry = fdTable [ fd ] ;
130
+ if ( ! fdEntry ) return WASIAbi . WASI_ERRNO_BADF ;
56
131
const view = memoryView ( ) ;
57
- const partialBuffers : Uint8Array [ ] = [ ] ;
58
- let iovsOffset = iovs ;
59
- let concatBufferSize = 0 ;
60
-
61
- for ( let i = 0 ; i < iovsLen ; i ++ ) {
62
- const offset = view . getUint32 ( iovsOffset + iovec_t . bufferOffset , true ) ;
63
- const len = view . getUint32 ( iovsOffset + iovec_t . lengthOffset , true ) ;
64
-
65
- partialBuffers . push ( new Uint8Array ( view . buffer , offset , len ) ) ;
66
- iovsOffset += iovec_t . size ;
67
- concatBufferSize += len ;
68
- }
69
- const concatBuffer = new Uint8Array ( concatBufferSize ) ;
70
- let offset = 0 ;
71
- for ( const buffer of partialBuffers ) {
72
- concatBuffer . set ( buffer , offset ) ;
73
- offset += buffer . length ;
74
- }
75
-
76
- const lines = decoder . decode ( concatBuffer ) ;
77
- if ( fd === 1 ) {
78
- useOptions . stdout ( lines ) ;
79
- } else if ( fd === 2 ) {
80
- useOptions . stderr ( lines ) ;
81
- }
82
- view . setUint32 ( nwritten , concatBuffer . length , true ) ;
132
+ const iovsBuffers = abi . iovViews ( view , iovs , iovsLen ) ;
133
+ const writtenValue = fdEntry . writev ( iovsBuffers ) ;
134
+ view . setUint32 ( nwritten , writtenValue , true ) ;
135
+ return WASIAbi . WASI_ESUCCESS ;
136
+ } ,
137
+ fd_read : ( fd : number , iovs : number , iovsLen : number , nread : number ) => {
138
+ const fdEntry = fdTable [ fd ] ;
139
+ if ( ! fdEntry ) return WASIAbi . WASI_ERRNO_BADF ;
140
+ const view = memoryView ( ) ;
141
+ const iovsBuffers = abi . iovViews ( view , iovs , iovsLen ) ;
142
+ const readValue = fdEntry . readv ( iovsBuffers ) ;
143
+ view . setUint32 ( nread , readValue , true ) ;
83
144
return WASIAbi . WASI_ESUCCESS ;
84
145
}
85
146
}
0 commit comments