Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libpipefs.js
4150 views
1
/**
2
* @license
3
* Copyright 2017 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
$PIPEFS__postset: () => addAtInit('PIPEFS.root = FS.mount(PIPEFS, {}, null);'),
9
$PIPEFS__deps: ['$FS'],
10
$PIPEFS: {
11
BUCKET_BUFFER_SIZE: 1024 * 8, // 8KiB Buffer
12
mount(mount) {
13
// Do not pollute the real root directory or its child nodes with pipes
14
// Looks like it is OK to create another pseudo-root node not linked to the FS.root hierarchy this way
15
return FS.createNode(null, '/', {{{ cDefs.S_IFDIR }}} | 0o777, 0);
16
},
17
createPipe() {
18
var pipe = {
19
buckets: [],
20
// refcnt 2 because pipe has a read end and a write end. We need to be
21
// able to read from the read end after write end is closed.
22
refcnt : 2,
23
timestamp: new Date(),
24
};
25
26
pipe.buckets.push({
27
buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE),
28
offset: 0,
29
roffset: 0
30
});
31
32
var rName = PIPEFS.nextname();
33
var wName = PIPEFS.nextname();
34
var rNode = FS.createNode(PIPEFS.root, rName, {{{ cDefs.S_IFIFO }}}, 0);
35
var wNode = FS.createNode(PIPEFS.root, wName, {{{ cDefs.S_IFIFO }}}, 0);
36
37
rNode.pipe = pipe;
38
wNode.pipe = pipe;
39
40
var readableStream = FS.createStream({
41
path: rName,
42
node: rNode,
43
flags: {{{ cDefs.O_RDONLY }}},
44
seekable: false,
45
stream_ops: PIPEFS.stream_ops
46
});
47
rNode.stream = readableStream;
48
49
var writableStream = FS.createStream({
50
path: wName,
51
node: wNode,
52
flags: {{{ cDefs.O_WRONLY }}},
53
seekable: false,
54
stream_ops: PIPEFS.stream_ops
55
});
56
wNode.stream = writableStream;
57
58
return {
59
readable_fd: readableStream.fd,
60
writable_fd: writableStream.fd
61
};
62
},
63
stream_ops: {
64
getattr(stream) {
65
var node = stream.node;
66
var timestamp = node.pipe.timestamp;
67
return {
68
dev: 14,
69
ino: node.id,
70
mode: 0o10600,
71
nlink: 1,
72
uid: 0,
73
gid: 0,
74
rdev: 0,
75
size: 0,
76
atime: timestamp,
77
mtime: timestamp,
78
ctime: timestamp,
79
blksize: 4096,
80
blocks: 0,
81
};
82
},
83
poll(stream) {
84
var pipe = stream.node.pipe;
85
86
if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_WRONLY }}}) {
87
return ({{{ cDefs.POLLWRNORM }}} | {{{ cDefs.POLLOUT }}});
88
}
89
for (var bucket of pipe.buckets) {
90
if (bucket.offset - bucket.roffset > 0) {
91
return ({{{ cDefs.POLLRDNORM }}} | {{{ cDefs.POLLIN }}});
92
}
93
}
94
95
return 0;
96
},
97
dup(stream) {
98
stream.node.pipe.refcnt++;
99
},
100
ioctl(stream, request, varargs) {
101
return {{{ cDefs.EINVAL }}};
102
},
103
fsync(stream) {
104
return {{{ cDefs.EINVAL }}};
105
},
106
read(stream, buffer, offset, length, position /* ignored */) {
107
var pipe = stream.node.pipe;
108
var currentLength = 0;
109
110
for (var bucket of pipe.buckets) {
111
currentLength += bucket.offset - bucket.roffset;
112
}
113
114
#if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB)
115
#if PTHREADS
116
assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer));
117
#else
118
assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer));
119
#endif
120
#endif
121
var data = buffer.subarray(offset, offset + length);
122
123
if (length <= 0) {
124
return 0;
125
}
126
if (currentLength == 0) {
127
// Behave as if the read end is always non-blocking
128
throw new FS.ErrnoError({{{ cDefs.EAGAIN }}});
129
}
130
var toRead = Math.min(currentLength, length);
131
132
var totalRead = toRead;
133
var toRemove = 0;
134
135
for (var bucket of pipe.buckets) {
136
var bucketSize = bucket.offset - bucket.roffset;
137
138
if (toRead <= bucketSize) {
139
var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset);
140
if (toRead < bucketSize) {
141
tmpSlice = tmpSlice.subarray(0, toRead);
142
bucket.roffset += toRead;
143
} else {
144
toRemove++;
145
}
146
data.set(tmpSlice);
147
break;
148
} else {
149
var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset);
150
data.set(tmpSlice);
151
data = data.subarray(tmpSlice.byteLength);
152
toRead -= tmpSlice.byteLength;
153
toRemove++;
154
}
155
}
156
157
if (toRemove && toRemove == pipe.buckets.length) {
158
// Do not generate excessive garbage in use cases such as
159
// write several bytes, read everything, write several bytes, read everything...
160
toRemove--;
161
pipe.buckets[toRemove].offset = 0;
162
pipe.buckets[toRemove].roffset = 0;
163
}
164
165
pipe.buckets.splice(0, toRemove);
166
167
return totalRead;
168
},
169
write(stream, buffer, offset, length, position /* ignored */) {
170
var pipe = stream.node.pipe;
171
172
#if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB)
173
#if PTHREADS
174
assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer));
175
#else
176
assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer));
177
#endif
178
#endif
179
var data = buffer.subarray(offset, offset + length);
180
181
var dataLen = data.byteLength;
182
if (dataLen <= 0) {
183
return 0;
184
}
185
186
var currBucket = null;
187
188
if (pipe.buckets.length == 0) {
189
currBucket = {
190
buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE),
191
offset: 0,
192
roffset: 0
193
};
194
pipe.buckets.push(currBucket);
195
} else {
196
currBucket = pipe.buckets[pipe.buckets.length - 1];
197
}
198
199
#if ASSERTIONS
200
assert(currBucket.offset <= PIPEFS.BUCKET_BUFFER_SIZE);
201
#endif
202
203
var freeBytesInCurrBuffer = PIPEFS.BUCKET_BUFFER_SIZE - currBucket.offset;
204
if (freeBytesInCurrBuffer >= dataLen) {
205
currBucket.buffer.set(data, currBucket.offset);
206
currBucket.offset += dataLen;
207
return dataLen;
208
} else if (freeBytesInCurrBuffer > 0) {
209
currBucket.buffer.set(data.subarray(0, freeBytesInCurrBuffer), currBucket.offset);
210
currBucket.offset += freeBytesInCurrBuffer;
211
data = data.subarray(freeBytesInCurrBuffer, data.byteLength);
212
}
213
214
var numBuckets = (data.byteLength / PIPEFS.BUCKET_BUFFER_SIZE) | 0;
215
var remElements = data.byteLength % PIPEFS.BUCKET_BUFFER_SIZE;
216
217
for (var i = 0; i < numBuckets; i++) {
218
var newBucket = {
219
buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE),
220
offset: PIPEFS.BUCKET_BUFFER_SIZE,
221
roffset: 0
222
};
223
pipe.buckets.push(newBucket);
224
newBucket.buffer.set(data.subarray(0, PIPEFS.BUCKET_BUFFER_SIZE));
225
data = data.subarray(PIPEFS.BUCKET_BUFFER_SIZE, data.byteLength);
226
}
227
228
if (remElements > 0) {
229
var newBucket = {
230
buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE),
231
offset: data.byteLength,
232
roffset: 0
233
};
234
pipe.buckets.push(newBucket);
235
newBucket.buffer.set(data);
236
}
237
238
return dataLen;
239
},
240
close(stream) {
241
var pipe = stream.node.pipe;
242
pipe.refcnt--;
243
if (pipe.refcnt === 0) {
244
pipe.buckets = null;
245
}
246
}
247
},
248
nextname() {
249
if (!PIPEFS.nextname.current) {
250
PIPEFS.nextname.current = 0;
251
}
252
return 'pipe[' + (PIPEFS.nextname.current++) + ']';
253
},
254
},
255
});
256
257