From: Lady Date: Sat, 24 Jun 2023 04:32:04 +0000 (-0700) Subject: Add isArrayBufferView & toArrayBuffer to binary.js X-Git-Tag: 0.4.0~11 X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/f9b002cc8f12241e67a53386438a8c6e9ff6d347?hp=995a63a35e6f2bad83f0c331c2ddd8d3e686e510 Add isArrayBufferView & toArrayBuffer to binary.js This also removes a previous less‐than‐ideal dependency on `instanceof` when converting arguments to array buffers for base⸺ conversions. --- diff --git a/binary.js b/binary.js index 5ee2bda..dcf32e5 100644 --- a/binary.js +++ b/binary.js @@ -72,14 +72,16 @@ const { * Returns an ArrayBuffer for encoding generated from the provided * arguments. */ -const bufferFromArgs = ($, $s) => - $ instanceof Buffer - ? $ - : $ instanceof View - ? call(getViewBuffer, $, []) - : $ instanceof TypedArray - ? call(getTypedArrayBuffer, $, []) - : ((string) => +const bufferFromArgs = ($, $s) => { + try { + // Try just getting the array buffer associated with the first + // argument and returning it if possible. + return toArrayBuffer($); + } catch { + // There is no array buffer associated with the first argument. + // + // Construct a string and convert it to an array buffer instead. + return ((string) => call( getViewBuffer, reduce( @@ -105,6 +107,8 @@ const bufferFromArgs = ($, $s) => ) : `${$}`, ); + } +}; /** * Returns the result of decoding the provided base16 string into an @@ -631,6 +635,14 @@ export const filenameSafeBase64Binary = ($, ...$s) => export const filenameSafeBase64String = ($, ...$s) => encodeBase64(bufferFromArgs($, $s), true); +/** + * Returns whether the provided value is a view on an underlying array + * buffer. + * + * ※ This function returns true for typed arrays and data views. + */ +export const { isView: isArrayBufferView } = Buffer; + /** * Returns whether the provided value is a base16 string. * @@ -722,6 +734,28 @@ export const isWRMGBase32 = ($) => { } }; +/** + * Returns the array buffer associated with the provided object. + * + * ☡ This function throws if the provided object is not a data view or + * typed array. + */ +export const toArrayBuffer = ($) => { + try { + // The provided argument has array buffer internal slots. + return call(getBufferByteLength, $, []), $; + } catch {} + try { + // The provided argument has typed array internal slots. + return call(getTypedArrayBuffer, $, []); + } catch {} + try { + // The provided argument has data view internal slots. + return call(getViewBuffer, $, []); + } catch {} + throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`); +}; + /** * Returns an ArrayBuffer generated from the provided W·R·M·G * (Crockford) base32 string. diff --git a/binary.test.js b/binary.test.js index 82fa4e0..1a0920f 100644 --- a/binary.test.js +++ b/binary.test.js @@ -24,11 +24,13 @@ import { base64String, filenameSafeBase64Binary, filenameSafeBase64String, + isArrayBufferView, isBase16, isBase32, isBase64, isFilenameSafeBase64, isWRMGBase32, + toArrayBuffer, wrmgBase32Binary, wrmgBase32String, } from "./binary.js"; @@ -578,11 +580,48 @@ describe("filenameSafeBase64String", () => { }); }); +describe("isArrayBufferView", () => { + it("[[Call]] returns true for data views", () => { + assertStrictEquals( + isArrayBufferView(new DataView(new ArrayBuffer())), + true, + ); + }); + + it("[[Call]] returns true for typed arrays", () => { + assertStrictEquals( + isArrayBufferView(new Uint8ClampedArray()), + true, + ); + assertStrictEquals(isArrayBufferView(new BigInt64Array()), true); + }); + + it("[[Call]] returns false for others", () => { + [ + undefined, + null, + true, + Symbol(), + 27, + 98n, + {}, + [], + () => {}, + new Proxy({}, {}), + "string", + new ArrayBuffer(), + new SharedArrayBuffer(), + ].forEach((value) => + assertStrictEquals(isArrayBufferView(value), false) + ); + }); +}); + describe("isBase16", () => { it("[[Call]] returns true for base64 strings", () => { for (const { base16 } of data.values()) { - assert(isBase16(base16)); - assert(isBase16(base16.toLowerCase())); + assertStrictEquals(isBase16(base16), true); + assertStrictEquals(isBase16(base16.toLowerCase()), true); } }); @@ -602,15 +641,15 @@ describe("isBase16", () => { "a", "abc", "abcg", - ].forEach((value) => assert(!isBase16(value))); + ].forEach((value) => assertStrictEquals(isBase16(value), false)); }); }); describe("isBase32", () => { it("[[Call]] returns true for base32 strings", () => { for (const { base32 } of data.values()) { - assert(isBase32(base32)); - assert(isBase32(base32.toLowerCase())); + assertStrictEquals(isBase32(base32), true); + assertStrictEquals(isBase32(base32.toLowerCase()), true); } }); @@ -629,14 +668,14 @@ describe("isBase32", () => { "ABC1", "A=======", "ABCDEFGHI", - ].forEach((value) => assert(!isBase32(value))); + ].forEach((value) => assertStrictEquals(isBase32(value), false)); }); }); describe("isBase64", () => { it("[[Call]] returns true for base64 strings", () => { for (const { base64 } of data.values()) { - assert(isBase64(base64)); + assertStrictEquals(isBase64(base64), true); } }); @@ -655,17 +694,18 @@ describe("isBase64", () => { "abc_", "a", "abc==", - ].forEach((value) => assert(!isBase64(value))); + ].forEach((value) => assertStrictEquals(isBase64(value), false)); }); }); describe("isFilenameSafeBase64", () => { it("[[Call]] returns true for filename‐safe base64 strings", () => { for (const { base64 } of data.values()) { - assert( + assertStrictEquals( isFilenameSafeBase64( base64.replace("+", "-").replace("/", "_"), ), + true, ); } }); @@ -685,32 +725,55 @@ describe("isFilenameSafeBase64", () => { "abc/", "a", "abc==", - ].forEach((value) => assert(!isFilenameSafeBase64(value))); + ].forEach((value) => + assertStrictEquals(isFilenameSafeBase64(value), false) + ); }); }); describe("isWRMGBase32", () => { it("[[Call]] returns true for W·R·M·G base32 strings", () => { for (const { wrmg } of data.values()) { - assert(isWRMGBase32(wrmg)); - assert(isWRMGBase32(wrmg.toLowerCase())); - assert(isWRMGBase32(`--${wrmg}--`)); - assert(isWRMGBase32(wrmg.replaceAll(/1/gu, "I"))); - assert(isWRMGBase32(wrmg.replaceAll(/1/gu, "L"))); - assert(isWRMGBase32(wrmg.replaceAll(/0/gu, "O"))); - assert(isWRMGBase32(wrmg.replaceAll(/1/gu, "i"))); - assert(isWRMGBase32(wrmg.replaceAll(/1/gu, "l"))); - assert(isWRMGBase32(wrmg.replaceAll(/0/gu, "o"))); - assert(isWRMGBase32(wrmg.replaceAll(/./gu, ($) => { - const rand = Math.random(); - return rand < 0.25 - ? $ - : rand < 0.5 - ? `-${$}` - : rand < 0.75 - ? `${$}-` - : `-${$}-`; - }))); + assertStrictEquals(isWRMGBase32(wrmg), true); + assertStrictEquals(isWRMGBase32(wrmg.toLowerCase()), true); + assertStrictEquals(isWRMGBase32(`--${wrmg}--`), true); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/1/gu, "I")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/1/gu, "L")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/0/gu, "O")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/1/gu, "i")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/1/gu, "l")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/0/gu, "o")), + true, + ); + assertStrictEquals( + isWRMGBase32(wrmg.replaceAll(/./gu, ($) => { + const rand = Math.random(); + return rand < 0.25 + ? $ + : rand < 0.5 + ? `-${$}` + : rand < 0.75 + ? `${$}-` + : `-${$}-`; + })), + true, + ); } }); @@ -729,7 +792,50 @@ describe("isWRMGBase32", () => { "ABCU", "A", "ABCDEFGH1", - ].forEach((value) => assert(!isWRMGBase32(value))); + ].forEach((value) => + assertStrictEquals(isWRMGBase32(value), false) + ); + }); +}); + +describe("toArrayBuffer", () => { + it("[[Call]] returns the argument for array buffers", () => { + const buffer = new ArrayBuffer(); + assertStrictEquals(toArrayBuffer(buffer), buffer); + }); + + it("[[Call]] returns the buffer for data views", () => { + const src = Uint8Array.from([2, 3, 1]); + const buffer = toArrayBuffer(new DataView(src.buffer)); + assert(buffer instanceof ArrayBuffer); + assertEquals(new Uint8Array(buffer), src); + }); + + it("[[Call]] returns the buffer for typed arrays", () => { + const src = Uint8Array.from([2, 3, 1]); + const buffer = toArrayBuffer(src); + assert(buffer instanceof ArrayBuffer); + assertEquals(new Uint8Array(buffer), src); + }); + + it("[[Call]] throws for others", () => { + [ + undefined, + null, + true, + Symbol(), + 27, + 98n, + {}, + [], + () => {}, + new Proxy({}, {}), + "string", + ].forEach((value) => + assertThrows(() => { + toArrayBuffer(value); + }) + ); }); });