]> Lady’s Gitweb - Pisces/commitdiff
Add isArrayBufferView & toArrayBuffer to binary.js
authorLady <redacted>
Sat, 24 Jun 2023 04:32:04 +0000 (21:32 -0700)
committerLady <redacted>
Sat, 24 Jun 2023 04:37:22 +0000 (21:37 -0700)
This also removes a previous less‐than‐ideal dependency on `instanceof`
when converting arguments to array buffers for base⸺ conversions.

binary.js
binary.test.js

index 5ee2bda874ae5fcf84a577064c0f8e0c41770895..dcf32e5f85fc439e8c5e326f17695ed531eb5520 100644 (file)
--- 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.
index 82fa4e030003ba18ca699eae9d742e2183e6e692..1a0920f38a9a43025d64dc2f9173648d0ece4ee2 100644 (file)
@@ -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);
+      })
+    );
   });
 });
 
This page took 0.031311 seconds and 4 git commands to generate.