Skip to content

Commit

Permalink
lib: update js side of blob (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
flakey5 authored Dec 17, 2022
1 parent 9ba4eb5 commit 9d028aa
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 107 deletions.
89 changes: 32 additions & 57 deletions lib/internal/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ const {
MathMin,
ObjectDefineProperties,
ObjectDefineProperty,
PromiseResolve,
PromiseReject,
SafePromisePrototypeFinally,
ReflectConstruct,
RegExpPrototypeExec,
RegExpPrototypeSymbolReplace,
Expand All @@ -22,7 +20,7 @@ const {

const {
createBlob: _createBlob,
FixedSizeBlobCopyJob,
concat,
getDataObject,
} = internalBinding('blob');

Expand Down Expand Up @@ -51,7 +49,6 @@ const {
const { inspect } = require('internal/util/inspect');

const {
AbortError,
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
Expand All @@ -66,13 +63,8 @@ const {
} = require('internal/validators');

const kHandle = Symbol('kHandle');
const kState = Symbol('kState');
const kIndex = Symbol('kIndex');
const kType = Symbol('kType');
const kLength = Symbol('kLength');
const kArrayBufferPromise = Symbol('kArrayBufferPromise');

const kMaxChunkSize = 65536;

const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u;

Expand Down Expand Up @@ -271,40 +263,23 @@ class Blob {
if (!isBlob(this))
return PromiseReject(new ERR_INVALID_THIS('Blob'));

// If there's already a promise in flight for the content,
// reuse it, but only while it's in flight. After the cached
// promise resolves it will be cleared, allowing it to be
// garbage collected as soon as possible.
if (this[kArrayBufferPromise])
return this[kArrayBufferPromise];

const job = new FixedSizeBlobCopyJob(this[kHandle]);

const ret = job.run();

// If the job returns a value immediately, the ArrayBuffer
// was generated synchronously and should just be returned
// directly.
if (ret !== undefined)
return PromiseResolve(ret);

const {
promise,
resolve,
reject,
} = createDeferredPromise();

job.ondone = (err, ab) => {
if (err !== undefined)
return reject(new AbortError(undefined, { cause: err }));
resolve(ab);
const { promise, resolve } = createDeferredPromise();
const reader = this[kHandle].getReader();
const buffers = [];
const readNext = () => {
const result = reader.pull((status, buffer) => {
if (status === -1) {
// EOS, concat & resolve
// buffer should be undefined here
resolve(concat(buffers));
return;
}
buffers.push(buffer);
readNext();
});
};
this[kArrayBufferPromise] =
SafePromisePrototypeFinally(
promise,
() => this[kArrayBufferPromise] = undefined);

return this[kArrayBufferPromise];
readNext();
return promise;
}

/**
Expand All @@ -326,22 +301,22 @@ class Blob {
if (!isBlob(this))
throw new ERR_INVALID_THIS('Blob');

const self = this;
const reader = this[kHandle].getReader();
return new lazyReadableStream({
async start() {
this[kState] = await self.arrayBuffer();
this[kIndex] = 0;
},

pull(controller) {
if (this[kState].byteLength - this[kIndex] <= kMaxChunkSize) {
controller.enqueue(new Uint8Array(this[kState], this[kIndex]));
controller.close();
this[kState] = undefined;
} else {
controller.enqueue(new Uint8Array(this[kState], this[kIndex], kMaxChunkSize));
this[kIndex] += kMaxChunkSize;
}
#reader: reader,
pull(c) {
const { promise, resolve } = createDeferredPromise();
reader.pull((status, buffer) => {
if (status === 1) {
// EOS
c.close();
resolve();
return;
}
c.enqueue(new Uint8Array(buffer));
resolve();
});
return promise;
}
});
}
Expand Down
10 changes: 10 additions & 0 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const {
S_IFREG
} = constants;

const { createBlobFromFileHandle } = internalBinding('blob');

const binding = internalBinding('fs');
const { Buffer } = require('buffer');

Expand Down Expand Up @@ -310,6 +312,14 @@ class FileHandle extends EventEmitterMixin(JSTransferable) {
return new WriteStream(undefined, { ...options, fd: this });
}

/**
* @typedef {import('../blob').Blob} Blob
* @returns {Blob}
*/
blob() {
return createBlobFromFileHandle(this);
}

[kTransfer]() {
if (this[kClosePromise] || this[kRefs] > 1) {
throw lazyDOMException('Cannot transfer FileHandle while in use',
Expand Down
50 changes: 0 additions & 50 deletions src/node_blob.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,56 +37,6 @@ using v8::Value;

namespace {

// TODO(@flakey5, @jasnell): Update the JavaScript side of blob...
//
// Within the JavaScript Blob object, the arrayBuffer() method will change significantly.
// the pattern will be something like:
//
// const { createDeferredPromise } = require('internal/util');
// const { concat, ...} = internalBinding('blob');
//
// function arrayBuffer() {
// const { promise, resolve } = createDeferredPromise();
// const reader = this.#buffer.getReader();
// const buffers = [];
// const readNext = () => {
// const result = reader.pull((status, buffer) => {
// if (status == -1) {
// // EOS, concat and resolve.
// // buffer should be undefined here.
// resolve(concat(buffers));
// return;
// }
// buffers.push(buffer);
// readNext();
// });
// };
// readNext(); // Starts the read loop
// return promise;
// }
//
// For the stream() method, the pattern will be something like:
//
// function stream() {
// const reader = this.#buffer.getReader();
// return new ReadableStream({
// #reader = reader;
// pull(c) {
// const { promise, resolve } = createDeferredPromise();
// reader.pull((status, buffer) => {
// if (status === -1) { // EOS
// c.close();
// resolve();
// return;
// }
// c.enqueue(new Uint8Array(buffer));
// resolve();
// });
// return promise;
// }
// });
// }

// Concatenate multiple ArrayBufferView/ArrayBuffers into a single ArrayBuffer.
// This method treats all ArrayBufferView types the same.
void Concat(const FunctionCallbackInfo<Value>& args) {
Expand Down

0 comments on commit 9d028aa

Please sign in to comment.