Skip to content

Commit

Permalink
Refactor getStreamAsArrayBuffer() (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored Aug 16, 2023
1 parent 215b672 commit eee8fbe
Showing 1 changed file with 12 additions and 13 deletions.
25 changes: 12 additions & 13 deletions source/array-buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export async function getStreamAsArrayBuffer(stream, options) {
return getStreamContents(stream, arrayBufferMethods, options);
}

const initArrayBuffer = () => new Uint8Array(0);
const initArrayBuffer = () => new ArrayBuffer(0);

const useTextEncoder = chunk => textEncoder.encode(chunk);
const textEncoder = new TextEncoder();
Expand All @@ -17,45 +17,44 @@ const useUint8ArrayWithOffset = chunk => new Uint8Array(chunk.buffer, chunk.byte
// `contents` is an increasingly growing `Uint8Array`.
const addArrayBufferChunk = (convertedChunk, contents, length, previousLength) => {
const newContents = hasArrayBufferResize() ? resizeArrayBuffer(contents, length) : resizeArrayBufferSlow(contents, length);
newContents.set(convertedChunk, previousLength);
new Uint8Array(newContents).set(convertedChunk, previousLength);
return newContents;
};

// Without `ArrayBuffer.resize()`, `contents` size is always a power of 2.
// This means its last bytes are zeroes (not stream data), which need to be
// trimmed at the end with `ArrayBuffer.slice()`.
const resizeArrayBufferSlow = (contents, length) => {
if (length <= contents.length) {
if (length <= contents.byteLength) {
return contents;
}

const newContents = new Uint8Array(getNewContentsLength(length));
newContents.set(contents, 0);
return newContents;
const arrayBuffer = new ArrayBuffer(getNewContentsLength(length));
new Uint8Array(arrayBuffer).set(new Uint8Array(contents), 0);
return arrayBuffer;
};

// With `ArrayBuffer.resize()`, `contents` size matches exactly the size of
// the stream data. It does not include extraneous zeroes to trim at the end.
// The underlying `ArrayBuffer` does allocate a number of bytes that is a power
// of 2, but those bytes are only visible after calling `ArrayBuffer.resize()`.
const resizeArrayBuffer = (contents, length) => {
if (length <= contents.buffer.maxByteLength) {
contents.buffer.resize(length);
return new Uint8Array(contents.buffer, 0, length);
if (length <= contents.maxByteLength) {
contents.resize(length);
return contents;
}

const arrayBuffer = new ArrayBuffer(length, {maxByteLength: getNewContentsLength(length)});
const newContents = new Uint8Array(arrayBuffer);
newContents.set(contents, 0);
return newContents;
new Uint8Array(arrayBuffer).set(new Uint8Array(contents), 0);
return arrayBuffer;
};

// Retrieve the closest `length` that is both >= and a power of 2
const getNewContentsLength = length => SCALE_FACTOR ** Math.ceil(Math.log(length) / Math.log(SCALE_FACTOR));

const SCALE_FACTOR = 2;

const finalizeArrayBuffer = ({buffer}, length) => hasArrayBufferResize() ? buffer : buffer.slice(0, length);
const finalizeArrayBuffer = (contents, length) => hasArrayBufferResize() ? contents : contents.slice(0, length);

// `ArrayBuffer.slice()` is slow. When `ArrayBuffer.resize()` is available
// (Node >=20.0.0, Safari >=16.4 and Chrome), we can use it instead.
Expand Down

0 comments on commit eee8fbe

Please sign in to comment.