Buckets:
| /** | |
| * @license | |
| * Copyright 2017 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| addToLibrary({ | |
| $PIPEFS__postset: () => addAtInit('PIPEFS.root = FS.mount(PIPEFS, {}, null);'), | |
| $PIPEFS__deps: ['$FS'], | |
| $PIPEFS: { | |
| BUCKET_BUFFER_SIZE: 1024 * 8, // 8KiB Buffer | |
| mount(mount) { | |
| // Do not pollute the real root directory or its child nodes with pipes | |
| // Looks like it is OK to create another pseudo-root node not linked to the FS.root hierarchy this way | |
| return FS.createNode(null, '/', {{{ cDefs.S_IFDIR }}} | 0o777, 0); | |
| }, | |
| createPipe() { | |
| var pipe = { | |
| buckets: [], | |
| // refcnt 2 because pipe has a read end and a write end. We need to be | |
| // able to read from the read end after write end is closed. | |
| refcnt : 2, | |
| timestamp: new Date(), | |
| #if PTHREADS || ASYNCIFY | |
| readableHandlers: [], | |
| registerReadableHandler: (callback) => { | |
| callback.registerCleanupFunc(() => { | |
| const i = pipe.readableHandlers.indexOf(callback); | |
| if (i !== -1) pipe.readableHandlers.splice(i, 1); | |
| }); | |
| pipe.readableHandlers.push(callback); | |
| }, | |
| notifyReadableHandlers: () => { | |
| while (pipe.readableHandlers.length > 0) { | |
| const cb = pipe.readableHandlers.shift(); | |
| if (cb) cb({{{ cDefs.POLLRDNORM }}} | {{{ cDefs.POLLIN }}}); | |
| } | |
| pipe.readableHandlers = []; | |
| } | |
| #endif | |
| }; | |
| pipe.buckets.push({ | |
| buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), | |
| offset: 0, | |
| roffset: 0 | |
| }); | |
| var rName = PIPEFS.nextname(); | |
| var wName = PIPEFS.nextname(); | |
| var rNode = FS.createNode(PIPEFS.root, rName, {{{ cDefs.S_IFIFO }}}, 0); | |
| var wNode = FS.createNode(PIPEFS.root, wName, {{{ cDefs.S_IFIFO }}}, 0); | |
| rNode.pipe = pipe; | |
| wNode.pipe = pipe; | |
| var readableStream = FS.createStream({ | |
| path: rName, | |
| node: rNode, | |
| flags: {{{ cDefs.O_RDONLY }}}, | |
| seekable: false, | |
| stream_ops: PIPEFS.stream_ops | |
| }); | |
| rNode.stream = readableStream; | |
| var writableStream = FS.createStream({ | |
| path: wName, | |
| node: wNode, | |
| flags: {{{ cDefs.O_WRONLY }}}, | |
| seekable: false, | |
| stream_ops: PIPEFS.stream_ops | |
| }); | |
| wNode.stream = writableStream; | |
| return { | |
| readable_fd: readableStream.fd, | |
| writable_fd: writableStream.fd | |
| }; | |
| }, | |
| stream_ops: { | |
| getattr(stream) { | |
| var node = stream.node; | |
| var timestamp = node.pipe.timestamp; | |
| return { | |
| dev: 14, | |
| ino: node.id, | |
| mode: 0o10600, | |
| nlink: 1, | |
| uid: 0, | |
| gid: 0, | |
| rdev: 0, | |
| size: 0, | |
| atime: timestamp, | |
| mtime: timestamp, | |
| ctime: timestamp, | |
| blksize: 4096, | |
| blocks: 0, | |
| }; | |
| }, | |
| poll(stream, timeout, notifyCallback) { | |
| var pipe = stream.node.pipe; | |
| if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_WRONLY }}}) { | |
| return ({{{ cDefs.POLLWRNORM }}} | {{{ cDefs.POLLOUT }}}); | |
| } | |
| for (var bucket of pipe.buckets) { | |
| if (bucket.offset - bucket.roffset > 0) { | |
| return ({{{ cDefs.POLLRDNORM }}} | {{{ cDefs.POLLIN }}}); | |
| } | |
| } | |
| #if PTHREADS || ASYNCIFY | |
| if (notifyCallback) pipe.registerReadableHandler(notifyCallback); | |
| #endif | |
| return 0; | |
| }, | |
| dup(stream) { | |
| stream.node.pipe.refcnt++; | |
| }, | |
| ioctl(stream, request, argp) { | |
| if (request == {{{ cDefs.FIONREAD }}}) { | |
| var pipe = stream.node.pipe; | |
| var currentLength = 0; | |
| for (var bucket of pipe.buckets) { | |
| currentLength += bucket.offset - bucket.roffset; | |
| } | |
| {{{ makeSetValue('argp', 0, 'currentLength', 'i32') }}}; | |
| return 0; | |
| } | |
| return {{{ cDefs.EINVAL }}}; | |
| }, | |
| fsync(stream) { | |
| return {{{ cDefs.EINVAL }}}; | |
| }, | |
| read(stream, buffer, offset, length, position /* ignored */) { | |
| var pipe = stream.node.pipe; | |
| var currentLength = 0; | |
| for (var bucket of pipe.buckets) { | |
| currentLength += bucket.offset - bucket.roffset; | |
| } | |
| #if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB) | |
| #if PTHREADS | |
| assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); | |
| #else | |
| assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); | |
| #endif | |
| #endif | |
| var data = buffer.subarray(offset, offset + length); | |
| if (length <= 0) { | |
| return 0; | |
| } | |
| if (currentLength == 0) { | |
| // Behave as if the read end is always non-blocking | |
| throw new FS.ErrnoError({{{ cDefs.EAGAIN }}}); | |
| } | |
| var toRead = Math.min(currentLength, length); | |
| var totalRead = toRead; | |
| var toRemove = 0; | |
| for (var bucket of pipe.buckets) { | |
| var bucketSize = bucket.offset - bucket.roffset; | |
| if (toRead <= bucketSize) { | |
| var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset); | |
| if (toRead < bucketSize) { | |
| tmpSlice = tmpSlice.subarray(0, toRead); | |
| bucket.roffset += toRead; | |
| } else { | |
| toRemove++; | |
| } | |
| data.set(tmpSlice); | |
| break; | |
| } else { | |
| var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset); | |
| data.set(tmpSlice); | |
| data = data.subarray(tmpSlice.byteLength); | |
| toRead -= tmpSlice.byteLength; | |
| toRemove++; | |
| } | |
| } | |
| if (toRemove && toRemove == pipe.buckets.length) { | |
| // Do not generate excessive garbage in use cases such as | |
| // write several bytes, read everything, write several bytes, read everything... | |
| toRemove--; | |
| pipe.buckets[toRemove].offset = 0; | |
| pipe.buckets[toRemove].roffset = 0; | |
| } | |
| pipe.buckets.splice(0, toRemove); | |
| return totalRead; | |
| }, | |
| write(stream, buffer, offset, length, position /* ignored */) { | |
| var pipe = stream.node.pipe; | |
| #if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB) | |
| #if PTHREADS | |
| assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); | |
| #else | |
| assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); | |
| #endif | |
| #endif | |
| var data = buffer.subarray(offset, offset + length); | |
| var dataLen = data.byteLength; | |
| if (dataLen <= 0) { | |
| return 0; | |
| } | |
| var currBucket = null; | |
| if (pipe.buckets.length == 0) { | |
| currBucket = { | |
| buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), | |
| offset: 0, | |
| roffset: 0 | |
| }; | |
| pipe.buckets.push(currBucket); | |
| } else { | |
| currBucket = pipe.buckets[pipe.buckets.length - 1]; | |
| } | |
| #if ASSERTIONS | |
| assert(currBucket.offset <= PIPEFS.BUCKET_BUFFER_SIZE); | |
| #endif | |
| var freeBytesInCurrBuffer = PIPEFS.BUCKET_BUFFER_SIZE - currBucket.offset; | |
| if (freeBytesInCurrBuffer >= dataLen) { | |
| currBucket.buffer.set(data, currBucket.offset); | |
| currBucket.offset += dataLen; | |
| #if PTHREADS || ASYNCIFY | |
| pipe.notifyReadableHandlers(); | |
| #endif | |
| return dataLen; | |
| } else if (freeBytesInCurrBuffer > 0) { | |
| currBucket.buffer.set(data.subarray(0, freeBytesInCurrBuffer), currBucket.offset); | |
| currBucket.offset += freeBytesInCurrBuffer; | |
| data = data.subarray(freeBytesInCurrBuffer, data.byteLength); | |
| } | |
| var numBuckets = (data.byteLength / PIPEFS.BUCKET_BUFFER_SIZE) | 0; | |
| var remElements = data.byteLength % PIPEFS.BUCKET_BUFFER_SIZE; | |
| for (var i = 0; i < numBuckets; i++) { | |
| var newBucket = { | |
| buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), | |
| offset: PIPEFS.BUCKET_BUFFER_SIZE, | |
| roffset: 0 | |
| }; | |
| pipe.buckets.push(newBucket); | |
| newBucket.buffer.set(data.subarray(0, PIPEFS.BUCKET_BUFFER_SIZE)); | |
| data = data.subarray(PIPEFS.BUCKET_BUFFER_SIZE, data.byteLength); | |
| } | |
| if (remElements > 0) { | |
| var newBucket = { | |
| buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), | |
| offset: data.byteLength, | |
| roffset: 0 | |
| }; | |
| pipe.buckets.push(newBucket); | |
| newBucket.buffer.set(data); | |
| } | |
| #if PTHREADS || ASYNCIFY | |
| pipe.notifyReadableHandlers(); | |
| #endif | |
| return dataLen; | |
| }, | |
| close(stream) { | |
| var pipe = stream.node.pipe; | |
| pipe.refcnt--; | |
| if (pipe.refcnt === 0) { | |
| pipe.buckets = null; | |
| } | |
| } | |
| }, | |
| nextname() { | |
| if (!PIPEFS.nextname.current) { | |
| PIPEFS.nextname.current = 0; | |
| } | |
| return 'pipe[' + (PIPEFS.nextname.current++) + ']'; | |
| }, | |
| }, | |
| }); | |
Xet Storage Details
- Size:
- 9.04 kB
- Xet hash:
- 49896e88a05c089ab94092f9e11d24f7cb36a6ef18424dd304e58af84843a2b9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.