'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const { randomFill, randomFillSync } = require('crypto');
const typedArrays = [
Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array,
Int32Array, Uint32Array, Float32Array, Float64Array,
BigInt64Array, BigUint64Array,
];
if (globalThis.Float16Array !== undefined)
typedArrays.push(globalThis.Float16Array);
const factories = [];
for (const Backing of [ArrayBuffer, SharedArrayBuffer]) {
for (const Type of [...typedArrays, Buffer, DataView]) {
const elementSize = Type.BYTES_PER_ELEMENT || 1;
factories.push({
name: `${Type.name} on ${Backing.name}`,
elementSize,
make(length = 64) {
const backing = new Backing(length + 64);
const input = Type === Buffer ? Buffer.from(backing, 32, length) :
new Type(backing, 32, length / elementSize);
return { input, backing, bytes: new Uint8Array(backing), start: 32, length };
},
});
}
factories.push({
name: Backing.name,
elementSize: 1,
make(length = 64) {
const input = new Backing(length);
return { input, backing: input, bytes: new Uint8Array(input), start: 0, length };
},
});
}
function shadowMetadata(target, mode) {
const values = {
byteLength: mode === 'zero' ? 0 : 4096,
length: mode === 'zero' ? 0 : 4096,
BYTES_PER_ELEMENT: mode === 'zero' ? 0 : 4096,
buffer: new ArrayBuffer(0),
byteOffset: mode === 'zero' ? 0 : 4096,
[Symbol.toStringTag]: 'NotAnArrayBufferView',
};
for (const key of Reflect.ownKeys(values)) {
Object.defineProperty(target, key, mode === 'getters' ?
{ get: common.mustNotCall(`Unexpected ${String(key)} getter`) } :
{ value: values[key] });
}
}
function checkFilled({ input, bytes, start }, result, offset, size, label) {
assert.strictEqual(result, input, label);
assert(bytes.subarray(0, start + offset).every((byte) => byte === 0), label);
assert(bytes.subarray(start + offset + size).every((byte) => byte === 0), label);
// Every nonempty test range contains at least 32 random bytes. Detect a
// no-op without assuming that any particular generated byte is nonzero.
if (size !== 0)
assert(bytes.subarray(start + offset, start + offset + size).some((byte) => byte !== 0), label);
}
for (const factory of factories) {
const { elementSize } = factory;
const ranges = [
{ args: [], offset: 0, size: 64 },
{ args: [16 / elementSize], offset: 16, size: 48 },
{ args: [16 / elementSize, 32 / elementSize], offset: 16, size: 32 },
{ args: [16 / elementSize, undefined], offset: 16, size: 48 },
{ args: [64 / elementSize], offset: 64, size: 0 },
];
for (const mode of ['getters', 'zero', 'inflated']) {
for (const { args, offset, size } of ranges) {
for (const async of [false, true]) {
const value = factory.make();
shadowMetadata(value.input, mode);
if (value.backing !== value.input)
shadowMetadata(value.backing, mode);
const label = `${factory.name}, ${mode}, ${async ? 'async' : 'sync'}, ${args}`;
if (async) {
assert.strictEqual(randomFill(value.input, ...args, common.mustSucceed((result) => {
checkFilled(value, result, offset, size, label);
})), undefined);
} else {
checkFilled(value, randomFillSync(value.input, ...args), offset, size, label);
}
}
}
}
// Inflated public bounds must not let an invalid range reach the native job.
// Keep the real element width here so each range exceeds the actual buffer.
const units = 64 / elementSize;
for (const args of [[units + 1], [0, units + 1], [units, 1]]) {
const { input } = factory.make();
Object.defineProperties(input, {
byteLength: { value: 4096 },
length: { value: 4096 },
});
assert.throws(() => randomFillSync(input, ...args), { code: 'ERR_OUT_OF_RANGE' });
assert.throws(() => randomFill(input, ...args, common.mustNotCall()), {
code: 'ERR_OUT_OF_RANGE',
});
}
for (const args of [[], [0], [0, 0], [0, undefined]]) {
const value = factory.make(0);
shadowMetadata(value.input, 'inflated');
checkFilled(value, randomFillSync(value.input, ...args), 0, 0, factory.name);
assert.strictEqual(randomFill(value.input, ...args, common.mustSucceed((result) => {
checkFilled(value, result, 0, 0, factory.name);
})), undefined);
}
}