123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- import {getStreamContents} from './contents.js';
- import {noop, throwObjectStream, getLengthProperty} from './utils.js';
- export async function getStreamAsArrayBuffer(stream, options) {
- return getStreamContents(stream, arrayBufferMethods, options);
- }
- const initArrayBuffer = () => ({contents: new ArrayBuffer(0)});
- const useTextEncoder = chunk => textEncoder.encode(chunk);
- const textEncoder = new TextEncoder();
- const useUint8Array = chunk => new Uint8Array(chunk);
- const useUint8ArrayWithOffset = chunk => new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
- const truncateArrayBufferChunk = (convertedChunk, chunkSize) => convertedChunk.slice(0, chunkSize);
- // `contents` is an increasingly growing `Uint8Array`.
- const addArrayBufferChunk = (convertedChunk, {contents, length: previousLength}, length) => {
- const newContents = hasArrayBufferResize() ? resizeArrayBuffer(contents, length) : resizeArrayBufferSlow(contents, length);
- 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.byteLength) {
- return contents;
- }
- 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.maxByteLength) {
- contents.resize(length);
- return contents;
- }
- const arrayBuffer = new ArrayBuffer(length, {maxByteLength: getNewContentsLength(length)});
- 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 = ({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.
- // eslint-disable-next-line no-warning-comments
- // TODO: remove after dropping support for Node 20.
- // eslint-disable-next-line no-warning-comments
- // TODO: use `ArrayBuffer.transferToFixedLength()` instead once it is available
- const hasArrayBufferResize = () => 'resize' in ArrayBuffer.prototype;
- const arrayBufferMethods = {
- init: initArrayBuffer,
- convertChunk: {
- string: useTextEncoder,
- buffer: useUint8Array,
- arrayBuffer: useUint8Array,
- dataView: useUint8ArrayWithOffset,
- typedArray: useUint8ArrayWithOffset,
- others: throwObjectStream,
- },
- getSize: getLengthProperty,
- truncateChunk: truncateArrayBufferChunk,
- addChunk: addArrayBufferChunk,
- getFinalChunk: noop,
- finalize: finalizeArrayBuffer,
- };
|