]> Lady’s Gitweb - Pisces/commitdiff
Add buffer getters and setters to binary.js 0.4.0
authorLady <redacted>
Tue, 27 Jun 2023 01:07:19 +0000 (18:07 -0700)
committerLady <redacted>
Tue, 27 Jun 2023 01:07:19 +0000 (18:07 -0700)
These are rough equivalents to the `DataView` methods, but can be
called with any kind of array buffer or array buffer view. Internally,
they (weakly) remember a `DataView` instance for each buffer and use
that for accessing values (to ensure correct endianness behaviour).

12 files changed:
binary.js
binary.test.js [changed mode: 0755->0644]
collection.js
function.js
iri.js
numeric.js
numeric.test.js
object.js
string.js
string.test.js
value.js
value.test.js

index 5ee2bda874ae5fcf84a577064c0f8e0c41770895..c149daf82d8245c78c418bda46b1175b1e284b22 100644 (file)
--- a/binary.js
+++ b/binary.js
@@ -17,23 +17,24 @@ import {
   stringFromCodeUnits,
   stringReplace,
 } from "./string.js";
+import { ITERATOR } from "./value.js";
 
 const Buffer = ArrayBuffer;
 const View = DataView;
 const TypedArray = Object.getPrototypeOf(Uint8Array);
 const { prototype: arrayPrototype } = Array;
 const { prototype: bufferPrototype } = Buffer;
-const { iterator: iteratorSymbol } = Symbol;
+const { prototype: sharedBufferPrototype } = SharedArrayBuffer;
 const { prototype: rePrototype } = RegExp;
 const { prototype: typedArrayPrototype } = TypedArray;
 const { prototype: viewPrototype } = View;
 
-const { [iteratorSymbol]: arrayIterator } = arrayPrototype;
+const { [ITERATOR]: arrayIterator } = arrayPrototype;
 const {
   next: arrayIteratorNext,
-} = Object.getPrototypeOf([][iteratorSymbol]());
+} = Object.getPrototypeOf([][ITERATOR]());
 const argumentIterablePrototype = {
-  [iteratorSymbol]() {
+  [ITERATOR]() {
     return {
       next: bind(
         arrayIteratorNext,
@@ -44,7 +45,7 @@ const argumentIterablePrototype = {
   },
 };
 const binaryCodeUnitIterablePrototype = {
-  [iteratorSymbol]() {
+  [ITERATOR]() {
     return {
       next: bind(
         arrayIteratorNext,
@@ -55,31 +56,61 @@ const binaryCodeUnitIterablePrototype = {
   },
 };
 
+const { exec: reExec } = rePrototype;
+const { slice: bufferSlice } = bufferPrototype;
 const getBufferByteLength =
   Object.getOwnPropertyDescriptor(bufferPrototype, "byteLength").get;
+const { slice: sharedBufferSlice } = sharedBufferPrototype;
+const getSharedBufferByteLength =
+  Object.getOwnPropertyDescriptor(sharedBufferPrototype, "byteLength")
+    .get;
 const getTypedArrayBuffer =
   Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
+const getTypedArrayByteLength =
+  Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")
+    .get;
+const getTypedArrayByteOffset =
+  Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset")
+    .get;
 const getViewBuffer =
   Object.getOwnPropertyDescriptor(viewPrototype, "buffer").get;
-const { exec: reExec } = rePrototype;
+const getViewByteLength =
+  Object.getOwnPropertyDescriptor(viewPrototype, "byteLength").get;
+const getViewByteOffset =
+  Object.getOwnPropertyDescriptor(viewPrototype, "byteOffset").get;
 const {
+  getBigInt64: viewGetInt64,
+  getBigUint64: viewGetUint64,
+  getFloat32: viewGetFloat32,
+  getFloat64: viewGetFloat64,
+  getInt8: viewGetInt8,
+  getInt16: viewGetInt16,
+  getInt32: viewGetInt32,
   getUint8: viewGetUint8,
+  getUint16: viewGetUint16,
+  getUint32: viewGetUint32,
+  setFloat32: viewSetFloat32,
+  setFloat64: viewSetFloat64,
   setUint8: viewSetUint8,
   setUint16: viewSetUint16,
+  setUint32: viewSetUint32,
+  setBigUint64: viewSetUint64,
 } = viewPrototype;
 
 /**
  * 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(
@@ -94,7 +125,7 @@ const bufferFromArgs = ($, $s) =>
         ),
         [],
       ))(
-        typeof $ == "string"
+        typeof $ === "string"
           ? $
           : hasOwnProperty($, "raw")
           ? rawString(
@@ -105,6 +136,8 @@ const bufferFromArgs = ($, $s) =>
           )
           : `${$}`,
       );
+  }
+};
 
 /**
  * Returns the result of decoding the provided base16 string into an
@@ -125,16 +158,19 @@ const decodeBase16 = (source) => {
         ? code - 87
         : -1;
       if (result < 0) {
+        // The source contains an invalid character.
         throw new RangeError(
           `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
         );
       } else {
+        // The source contains a valid character with a recognized
+        // mapping.
         return result;
       }
     },
   );
   const { length } = u4s;
-  if (length % 2 == 1) {
+  if (length % 2 === 1) {
     // The length is such that an entire letter would be dropped during
     // a forgiving decode.
     throw new RangeError(
@@ -144,6 +180,8 @@ const decodeBase16 = (source) => {
     // Every letter contributes at least some bits to the result.
     const dataView = new View(new Buffer(floor(length / 2)));
     for (let index = 0; index < length - 1;) {
+      // Iterate over the characters and assign their bits to the
+      // buffer.
       call(viewSetUint8, dataView, [
         floor(index / 2),
         (u4s[index] << 4) | u4s[++index, index++],
@@ -167,7 +205,7 @@ const decodeBase32 = (source, wrmg) => {
   const u5s = map(
     wrmg
       ? stringReplace(source, /-/gu, "")
-      : source.length % 8 == 0
+      : source.length % 8 === 0
       ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
       : source,
     (ucsCharacter) => {
@@ -177,15 +215,15 @@ const decodeBase32 = (source, wrmg) => {
           ? code - 48
           : code >= 0x41 && code <= 0x48
           ? code - 55
-          : code == 0x49
+          : code === 0x49
           ? 1 // I
           : code >= 0x4A && code <= 0x4B
           ? code - 56
-          : code == 0x4C
+          : code === 0x4C
           ? 1 // L
           : code >= 0x4D && code <= 0x4E
           ? code - 57
-          : code == 0x4F
+          : code === 0x4F
           ? 0 // O
           : code >= 0x50 && code <= 0x54
           ? code - 58
@@ -194,15 +232,15 @@ const decodeBase32 = (source, wrmg) => {
           ? code - 59
           : code >= 0x61 && code <= 0x68
           ? code - 87
-          : code == 0x69
+          : code === 0x69
           ? 1 // i
           : code >= 0x6A && code <= 0x6B
           ? code - 88
-          : code == 0x6C
+          : code === 0x6C
           ? 1 // l
           : code >= 0x6D && code <= 0x6E
           ? code - 89
-          : code == 0x6F
+          : code === 0x6F
           ? 0 // o
           : code >= 0x70 && code <= 0x74
           ? code - 90
@@ -218,17 +256,20 @@ const decodeBase32 = (source, wrmg) => {
         ? code - 24 // digits 2–7 map to 26–31
         : -1;
       if (result < 0) {
+        // The source contains an invalid character.
         throw new RangeError(
           `Piscēs: Invalid character in Base32: ${ucsCharacter}.`,
         );
       } else {
+        // The source contains a valid character with a recognized
+        // mapping.
         return result;
       }
     },
   );
   const { length } = u5s;
   const lengthMod8 = length % 8;
-  if (lengthMod8 == 1 || lengthMod8 == 3 || lengthMod8 == 6) {
+  if (lengthMod8 === 1 || lengthMod8 === 3 || lengthMod8 === 6) {
     // The length is such that an entire letter would be dropped during
     // a forgiving decode.
     throw new RangeError(
@@ -238,37 +279,26 @@ const decodeBase32 = (source, wrmg) => {
     // Every letter contributes at least some bits to the result.
     const dataView = new View(new Buffer(floor(length * 5 / 8)));
     for (let index = 0; index < length - 1;) {
+      // Iterate over the characters and assign their bits to the
+      // buffer.
+      //
       // The final index is not handled; if the string is not divisible
       // by 8, some bits might be dropped. This matches the “forgiving
       // decode” behaviour specified by WhatW·G for base64.
       const dataIndex = ceil(index * 5 / 8);
       const remainder = index % 8;
-      if (remainder == 0) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u5s[index] << 3 | u5s[++index] >> 2,
-        ]);
-      } else if (remainder == 1) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u5s[index] << 6 | u5s[++index] << 1 | u5s[++index] >> 4,
-        ]);
-      } else if (remainder == 3) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u5s[index] << 4 | u5s[++index] >> 1,
-        ]);
-      } else if (remainder == 4) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u5s[index] << 7 | u5s[++index] << 2 | u5s[++index] >> 3,
-        ]);
-      } else { // remainder == 6
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u5s[index] << 5 | u5s[++index, index++],
-        ]);
-      }
+      call(viewSetUint8, dataView, [
+        dataIndex,
+        remainder === 0
+          ? u5s[index] << 3 | u5s[++index] >> 2
+          : remainder === 1
+          ? u5s[index] << 6 | u5s[++index] << 1 | u5s[++index] >> 4
+          : remainder === 3
+          ? u5s[index] << 4 | u5s[++index] >> 1
+          : remainder === 4
+          ? u5s[index] << 7 | u5s[++index] << 2 | u5s[++index] >> 3
+          : u5s[index] << 5 | u5s[++index, index++], // remainder === 6
+      ]);
     }
     return call(getViewBuffer, dataView, []);
   }
@@ -282,7 +312,7 @@ const decodeBase32 = (source, wrmg) => {
  */
 const decodeBase64 = (source, safe = false) => {
   const u6s = map(
-    source.length % 4 == 0
+    source.length % 4 === 0
       ? stringReplace(source, /={1,2}$/u, "")
       : source,
     (ucsCharacter) => {
@@ -293,22 +323,25 @@ const decodeBase64 = (source, safe = false) => {
         ? code - 71
         : code >= 0x30 && code <= 0x39
         ? code + 4
-        : code == (safe ? 0x2D : 0x2B)
+        : code === (safe ? 0x2D : 0x2B)
         ? 62
-        : code == (safe ? 0x5F : 0x2F)
+        : code === (safe ? 0x5F : 0x2F)
         ? 63
         : -1;
       if (result < 0) {
+        // The source contains an invalid character.
         throw new RangeError(
           `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
         );
       } else {
+        // The source contains a valid character with a recognized
+        // mapping.
         return result;
       }
     },
   );
   const { length } = u6s;
-  if (length % 4 == 1) {
+  if (length % 4 === 1) {
     // The length is such that an entire letter would be dropped during
     // a forgiving decode.
     throw new RangeError(
@@ -318,27 +351,22 @@ const decodeBase64 = (source, safe = false) => {
     // Every letter contributes at least some bits to the result.
     const dataView = new View(new Buffer(floor(length * 3 / 4)));
     for (let index = 0; index < length - 1;) {
+      // Iterate over the characters and assign their bits to the
+      // buffer.
+      //
       // The final index is not handled; if the string is not divisible
       // by 4, some bits might be dropped. This matches the “forgiving
       // decode” behaviour specified by WhatW·G for base64.
       const dataIndex = ceil(index * 3 / 4);
       const remainder = index % 4;
-      if (remainder == 0) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u6s[index] << 2 | u6s[++index] >> 4,
-        ]);
-      } else if (remainder == 1) {
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u6s[index] << 4 | u6s[++index] >> 2,
-        ]);
-      } else { // remainder == 2
-        call(viewSetUint8, dataView, [
-          dataIndex,
-          u6s[index] << 6 | u6s[++index, index++],
-        ]);
-      }
+      call(viewSetUint8, dataView, [
+        dataIndex,
+        remainder === 0
+          ? u6s[index] << 2 | u6s[++index] >> 4
+          : remainder === 1
+          ? u6s[index] << 4 | u6s[++index] >> 2
+          : u6s[index] << 6 | u6s[++index, index++], // remainder === 2
+      ]);
     }
     return call(getViewBuffer, dataView, []);
   }
@@ -362,17 +390,23 @@ const encodeBase16 = (buffer) => {
     0x3D,
   );
   for (let index = 0; index < byteLength;) {
+    // Iterate over the bytes and generate code units for them.
     const codeUnitIndex = index * 2;
     const datum = call(viewGetUint8, dataView, [index++]);
     const u4s = [datum >> 4, datum & 0xF];
     for (let u4i = 0; u4i < 2; ++u4i) {
+      // Handle the high four bits, then the low four bits.
       const u4 = u4s[u4i];
       const result = u4 < 10 ? u4 + 48 : u4 < 16 ? u4 + 55 : -1;
       if (result < 0) {
+        // No mapping exists for these four bits.
+        //
+        // ※ This shouldn’t be possible!
         throw new RangeError(
           `Piscēs: Unexpected Base16 value: ${u4}.`,
         );
       } else {
+        // A mapping exists for the bits.
         resultingCodeUnits[codeUnitIndex + u4i] = result;
       }
     }
@@ -404,6 +438,7 @@ const encodeBase32 = (buffer, wrmg = false) => {
     fillByte,
   );
   for (let index = 0; index < byteLength;) {
+    // Iterate over the bytes and generate code units for them.
     const codeUnitIndex = ceil(index * 8 / 5);
     const currentIndex = codeUnitIndex + +(
       0b01011 & 1 << index % 5 &&
@@ -416,21 +451,21 @@ const encodeBase32 = (buffer, wrmg = false) => {
         // digits 1, 3, 4 & 6 span multiple bytes
         ? call(viewGetUint8, dataView, [index])
         : 0;
-    const u5 = remainder == 0
+    const u5 = remainder === 0
       ? currentByte >> 3
-      : remainder == 1
+      : remainder === 1
       ? (currentByte & 0b00000111) << 2 | nextByte >> 6
-      : remainder == 2
+      : remainder === 2
       ? (currentByte & 0b00111111) >> 1
-      : remainder == 3
+      : remainder === 3
       ? (currentByte & 0b00000001) << 4 | nextByte >> 4
-      : remainder == 4
+      : remainder === 4
       ? (currentByte & 0b00001111) << 1 | nextByte >> 7
-      : remainder == 5
+      : remainder === 5
       ? (currentByte & 0b01111111) >> 2
-      : remainder == 6
+      : remainder === 6
       ? (currentByte & 0b00000011) << 3 | nextByte >> 5
-      : (++index, currentByte & 0b00011111); // remainder == 7
+      : (++index, currentByte & 0b00011111); // remainder === 7
     const result = wrmg
       ? u5 < 10 ? u5 + 48 : u5 < 18
         ? u5 + 55
@@ -453,8 +488,12 @@ const encodeBase32 = (buffer, wrmg = false) => {
       ? u5 + 24
       : -1;
     if (result < 0) {
+      // No mapping exists for these five bits.
+      //
+      // ※ This shouldn’t be possible!
       throw new RangeError(`Piscēs: Unexpected Base32 value: ${u5}.`);
     } else {
+      // A mapping exists for the bits.
       resultingCodeUnits[currentIndex] = result;
     }
   }
@@ -485,9 +524,10 @@ const encodeBase64 = (buffer, safe = false) => {
     0x3D,
   );
   for (let index = 0; index < byteLength;) {
+    // Iterate over the bytes and generate code units for them.
     const codeUnitIndex = ceil(index * 4 / 3);
     const currentIndex = codeUnitIndex + +(
-      index % 3 == 0 && resultingCodeUnits[codeUnitIndex] != 0x3D
+      index % 3 === 0 && resultingCodeUnits[codeUnitIndex] != 0x3D
     ); // every third byte handles two letters; this is for the second
     const remainder = currentIndex % 4;
     const currentByte = call(viewGetUint8, dataView, [index]);
@@ -495,13 +535,13 @@ const encodeBase64 = (buffer, safe = false) => {
       // digits 1 & 2 span multiple bytes
       ? call(viewGetUint8, dataView, [index])
       : 0;
-    const u6 = remainder == 0
+    const u6 = remainder === 0
       ? currentByte >> 2
-      : remainder == 1
+      : remainder === 1
       ? (currentByte & 0b00000011) << 4 | nextByte >> 4
-      : remainder == 2
+      : remainder === 2
       ? (currentByte & 0b00001111) << 2 | nextByte >> 6
-      : (++index, currentByte & 0b00111111); // remainder == 3
+      : (++index, currentByte & 0b00111111); // remainder === 3
     const result = u6 < 26
       ? u6 + 65
       : u6 < 52
@@ -514,8 +554,12 @@ const encodeBase64 = (buffer, safe = false) => {
       ? (safe ? 0x5F : 0x2F)
       : -1;
     if (result < 0) {
+      // No mapping exists for these six bits.
+      //
+      // ※ This shouldn’t be possible!
       throw new RangeError(`Piscēs: Unexpected Base64 value: ${u6}.`);
     } else {
+      // A mapping exists for the bits.
       resultingCodeUnits[currentIndex] = result;
     }
   }
@@ -530,7 +574,7 @@ const encodeBase64 = (buffer, safe = false) => {
  */
 const sourceFromArgs = ($, $s) =>
   stringReplace(
-    typeof $ == "string" ? $ : hasOwnProperty($, "raw")
+    typeof $ === "string" ? $ : hasOwnProperty($, "raw")
       ? rawString(
         $,
         ...objectCreate(argumentIterablePrototype, {
@@ -542,6 +586,26 @@ const sourceFromArgs = ($, $s) =>
     "",
   );
 
+/**
+ * Returns a slice of the provided value according to the algorithm of
+ * `ArrayBuffer::slice` (or `SharedArrayBuffer::slice`).
+ *
+ * ☡ This function throws if the provided value is not an array buffer.
+ */
+export const arrayBufferSlice = ($, start, end, ...args) =>
+  call(
+    isSharedArrayBuffer($) ? sharedBufferSlice : bufferSlice,
+    $,
+    [
+      start,
+      end,
+      ...objectCreate(
+        argumentIterablePrototype,
+        { args: { value: args } },
+      ),
+    ],
+  );
+
 /**
  * Returns an ArrayBuffer generated from the provided base16 string.
  *
@@ -631,6 +695,548 @@ export const filenameSafeBase64Binary = ($, ...$s) =>
 export const filenameSafeBase64String = ($, ...$s) =>
   encodeBase64(bufferFromArgs($, $s), true);
 
+export const {
+  /**
+   * Returns the signed 8‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getInt8`, but works on all array
+   * buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get8BitSignedIntegralItem,
+
+  /**
+   * Returns the unsigned 8‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getUint8`, but works on all array
+   * buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get8BitUnsignedIntegralItem,
+
+  /**
+   * Returns the signed 16‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getInt16`, but works on all array
+   * buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get16BitSignedIntegralItem,
+
+  /**
+   * Returns the unsigned 16‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getUint16`, but works on all
+   * array buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get16BitUnsignedIntegralItem,
+
+  /**
+   * Returns the 32‐bit floating point value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getFloat32`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get32BitFloatingPointItem,
+
+  /**
+   * Returns the signed 32‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getInt32`, but works on all array
+   * buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get32BitSignedIntegralItem,
+
+  /**
+   * Returns the unsigned 32‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getUint32`, but works on all
+   * array buffers and array buffer views and returns a big·int.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get32BitUnsignedIntegralItem,
+
+  /**
+   * Returns the 64‐bit floating point value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getFloat64`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get64BitFloatingPointItem,
+
+  /**
+   * Returns the signed 64‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getBigInt64`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get64BitSignedIntegralItem,
+
+  /**
+   * Returns the unsigned 64‐bit integral value in the provided array
+   * buffer or array buffer view at the provided byte offset.
+   *
+   * ※ The retrieved value will be big·endian unless a third argument
+   * is specified and truthy.
+   *
+   * ※ This is similar to `DataView::getBigUint64`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  get64BitUnsignedIntegralItem,
+
+  /**
+   * Sets the 8‐bit integral value in the provided array buffer or
+   * array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setInt8`, but works on all array
+   * buffers and array buffer views and accepts both numeric and
+   * big·int values.
+   *
+   * ※ It doesn’t matter whether the provided value is signed or
+   * unsigned, as the algorithm will cast one to the other.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  set8BitIntegralItem,
+
+  /**
+   * Sets the 16‐bit integral value in the provided array buffer or
+   * array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setInt16`, but works on all array
+   * buffers and array buffer views and accepts both numeric and
+   * big·int values.
+   *
+   * ※ It doesn’t matter whether the provided value is signed or
+   * unsigned, as the algorithm will cast one to the other.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  set16BitIntegralItem,
+
+  /**
+   * Sets the 32‐bit floating point value in the provided array buffer
+   * or array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setFloat32`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  set32BitFloatingPointItem,
+
+  /**
+   * Sets the 32‐bit integral value in the provided array buffer or
+   * array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setInt32`, but works on all array
+   * buffers and array buffer views and accepts both numeric and
+   * big·int values.
+   *
+   * ※ It doesn’t matter whether the provided value is signed or
+   * unsigned, as the algorithm will cast one to the other.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  set32BitIntegralItem,
+
+  /**
+   * Sets the 64‐bit floating point value in the provided array buffer
+   * or array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setFloat64`, but works on all
+   * array buffers and array buffer views.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array.
+   */
+  set64BitFloatingPointItem,
+
+  /**
+   * Sets the 64‐bit integral value in the provided array buffer or
+   * array buffer view at the provided byte offset to the provided
+   * value.
+   *
+   * ※ The value will be set as big·endian unless a fourth argument is
+   * specified and truthy.
+   *
+   * ※ This is similar to `DataView::setInt32`, but works on all array
+   * buffers and array buffer views.
+   *
+   * ※ It doesn’t matter whether the provided value is signed or
+   * unsigned, as the algorithm will cast one to the other.
+   *
+   * ☡ This function throws if the first argument is not an array
+   * buffer, data view, or typed array, or if the third argument is not
+   * a big·int.
+   */
+  set64BitIntegralItem,
+} = (() => {
+  const makeBigInt = BigInt;
+  const { asUintN } = BigInt;
+  const makeNumber = Number;
+
+  const viewMap = new WeakMap();
+  const view = ($) => {
+    const buffer = toArrayBuffer($);
+    if (viewMap.has(buffer)) {
+      // A view has already been allocated for this buffer; use it.
+      return viewMap.get(buffer);
+    } else {
+      // No view has been created for this buffer yet.
+      const result = new View(buffer);
+      viewMap.set(buffer, result);
+      return result;
+    }
+  };
+
+  return {
+    get8BitSignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetInt8, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get8BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetUint8, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get16BitSignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetInt16, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get16BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetUint16, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get32BitFloatingPointItem: ($, byteOffset, ...args) =>
+      call(viewGetFloat32, view($), [
+        getByteOffset($) + byteOffset,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    get32BitSignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetInt32, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get32BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
+      makeBigInt(
+        call(viewGetUint32, view($), [
+          getByteOffset($) + byteOffset,
+          ...objectCreate(
+            argumentIterablePrototype,
+            { args: { value: args } },
+          ),
+        ]),
+      ),
+    get64BitFloatingPointItem: ($, byteOffset, ...args) =>
+      call(viewGetFloat64, view($), [
+        getByteOffset($) + byteOffset,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    get64BitSignedIntegralItem: ($, byteOffset, ...args) =>
+      call(viewGetInt64, view($), [
+        getByteOffset($) + byteOffset,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    get64BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
+      call(viewGetUint64, view($), [
+        getByteOffset($) + byteOffset,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set8BitIntegralItem: ($, byteOffset, value, ...args) =>
+      call(viewSetUint8, view($), [
+        getByteOffset($) + byteOffset,
+        makeNumber(value),
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set16BitIntegralItem: ($, byteOffset, value, ...args) =>
+      call(viewSetUint16, view($), [
+        getByteOffset($) + byteOffset,
+        makeNumber(value),
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set32BitFloatingPointItem: ($, byteOffset, value, ...args) =>
+      call(viewSetFloat32, view($), [
+        getByteOffset($) + byteOffset,
+        value,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set32BitIntegralItem: ($, byteOffset, value, ...args) =>
+      call(viewSetUint32, view($), [
+        getByteOffset($) + byteOffset,
+        makeNumber(value),
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set64BitFloatingPointItem: ($, byteOffset, value, ...args) =>
+      call(viewSetFloat64, view($), [
+        getByteOffset($) + byteOffset,
+        value,
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+    set64BitIntegralItem: ($, byteOffset, value, ...args) =>
+      call(viewSetUint64, view($), [
+        getByteOffset($) + byteOffset,
+        asUintN(64, value),
+        ...objectCreate(
+          argumentIterablePrototype,
+          { args: { value: args } },
+        ),
+      ]),
+  };
+})();
+
+/**
+ * Returns the byte length for the provided array buffer or array
+ * buffer view.
+ *
+ * ☡ This function throws if the provided value is not an array buffer,
+ * data view, or typed array.
+ */
+export const getByteLength = ($) => {
+  try {
+    // Attempt to get the byte length from the provided value as an
+    // `ArrayBuffer`.
+    return call(getBufferByteLength, $, []);
+  } catch {
+    // The provided value is not an `ArrayBuffer`.
+    /* do nothing */
+  }
+  try {
+    // Attempt to get the byte length from the provided value as a
+    // `SharedArrayBuffer`.
+    return call(getSharedBufferByteLength, $, []);
+  } catch {
+    // The provided value is not a `SharedArrayBuffer`.
+    /* do nothing */
+  }
+  try {
+    // Attempt to get the byte length from the provided value as a
+    // data view.
+    return call(getViewByteLength, $, []);
+  } catch {
+    // The provided value is not a data view.
+    /* do nothing */
+  }
+  try {
+    // Attempt to get the byte length from the provided value as a
+    // typed array.
+    return call(getTypedArrayByteLength, $, []);
+  } catch {
+    // The provided value is not a typed array.
+    /* do nothing */
+  }
+  throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
+};
+
+/**
+ * Returns the byte offset for the provided array buffer or array
+ * buffer view.
+ *
+ * ※ This function always returns `0` for array buffers.
+ *
+ * ☡ This function throws if the provided value is not an array buffer,
+ * data view, or typed array.
+ */
+export const getByteOffset = ($) => {
+  if (isArrayBuffer($)) {
+    // The provided value is an array buffer.
+    return 0;
+  } else {
+    try {
+      // Attempt to get the byte offset from the provided value as a
+      // data view.
+      return call(getViewByteOffset, $, []);
+    } catch {
+      // The provided value is not a data view.
+      /* do nothing */
+    }
+    try {
+      // Attempt to get the byte offset from the provided value as a
+      // typed array.
+      return call(getTypedArrayByteOffset, $, []);
+    } catch {
+      // The provided value is not a typed array.
+      /* do nothing */
+    }
+    throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
+  }
+};
+
+/**
+ * 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 an array buffer.
+ *
+ * ※ This function returns true for both `ArrayBuffer`s and
+ * `SharedArrayBuffer`s.
+ */
+export const isArrayBuffer = ($) => {
+  try {
+    // Try to see if the provided argument has array buffer internal
+    // slots and return true if so.
+    return call(getBufferByteLength, $, []), true;
+  } catch {
+    // The provided argument does not have array buffer internal slots.
+    /* do nothing */
+  }
+  try {
+    // Try to see if the provided argument has array buffer internal
+    // slots and return true if so.
+    return call(getSharedBufferByteLength, $, []), true;
+  } catch {
+    // The provided argument does not have array buffer internal slots.
+    /* do nothing */
+  }
+  return false;
+};
+
 /**
  * Returns whether the provided value is a base16 string.
  *
@@ -639,11 +1245,13 @@ export const filenameSafeBase64String = ($, ...$s) =>
  */
 export const isBase16 = ($) => {
   if (typeof $ !== "string") {
+    // The provided value is not a string.
     return false;
   } else {
+    // The provided value is a string.
     const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
-    return source.length % 2 != 1 &&
-      call(reExec, /[^0-9A-F]/iu, [source]) == null;
+    return source.length % 2 !== 1 &&
+      call(reExec, /[^0-9A-F]/iu, [source]) === null;
   }
 };
 
@@ -655,14 +1263,16 @@ export const isBase16 = ($) => {
  */
 export const isBase32 = ($) => {
   if (typeof $ !== "string") {
+    // The provided value is not a string.
     return false;
   } else {
+    // The provided value is a string.
     const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
-    const trimmed = source.length % 8 == 0
+    const trimmed = source.length % 8 === 0
       ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
       : source;
-    return trimmed.length % 8 != 1 &&
-      call(reExec, /[^2-7A-Z/]/iu, [trimmed]) == null;
+    return trimmed.length % 8 !== 1 &&
+      call(reExec, /[^2-7A-Z/]/iu, [trimmed]) === null;
   }
 };
 
@@ -674,14 +1284,16 @@ export const isBase32 = ($) => {
  */
 export const isBase64 = ($) => {
   if (typeof $ !== "string") {
+    // The provided value is not a string.
     return false;
   } else {
+    // The provided value is a string.
     const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
-    const trimmed = source.length % 4 == 0
+    const trimmed = source.length % 4 === 0
       ? stringReplace(source, /={1,2}$/u, "")
       : source;
-    return trimmed.length % 4 != 1 &&
-      call(reExec, /[^0-9A-Za-z+\/]/u, [trimmed]) == null;
+    return trimmed.length % 4 !== 1 &&
+      call(reExec, /[^0-9A-Za-z+\/]/u, [trimmed]) === null;
   }
 };
 
@@ -693,14 +1305,40 @@ export const isBase64 = ($) => {
  */
 export const isFilenameSafeBase64 = ($) => {
   if (typeof $ !== "string") {
+    // The provided value is not a string.
     return false;
   } else {
+    // The provided value is a string.
     const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
-    const trimmed = source.length % 4 == 0
+    const trimmed = source.length % 4 === 0
       ? stringReplace(source, /={1,2}$/u, "")
       : source;
-    return trimmed.length % 4 != 1 &&
-      call(reExec, /[^0-9A-Za-z_-]/u, [trimmed]) == null;
+    return trimmed.length % 4 !== 1 &&
+      call(reExec, /[^0-9A-Za-z_-]/u, [trimmed]) === null;
+  }
+};
+
+/** Returns whether the provided value is a shared array buffer. */
+export const isSharedArrayBuffer = ($) => {
+  try {
+    // Try to see if the provided argument has shared array buffer
+    // internal slots and return true if so.
+    return call(getSharedBufferByteLength, $, []), true;
+  } catch {
+    // The provided argument does not have data view internal slots.
+    return false;
+  }
+};
+
+/** Returns whether the provided value is a typed array. */
+export const isTypedArray = ($) => {
+  try {
+    // Try to see if the provided argument has typed array internal
+    // slots and return true if so.
+    return call(getTypedArrayBuffer, $, []), true;
+  } catch {
+    // The provided argument does not have typed array internal slots.
+    return false;
   }
 };
 
@@ -713,12 +1351,42 @@ export const isFilenameSafeBase64 = ($) => {
  */
 export const isWRMGBase32 = ($) => {
   if (typeof $ !== "string") {
+    // The provided value is not a string.
     return false;
   } else {
+    // The provided value is a string.
     const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
     const trimmed = stringReplace(source, /-/gu, "");
-    return trimmed.length % 8 != 1 &&
-      call(reExec, /[^0-9A-TV-Z]/iu, [trimmed]) == null;
+    return trimmed.length % 8 !== 1 &&
+      call(reExec, /[^0-9A-TV-Z]/iu, [trimmed]) === null;
+  }
+};
+
+/**
+ * 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 = ($) => {
+  if (isArrayBuffer($)) {
+    // The provided argument has array buffer internal slots.
+    return $;
+  } else {
+    // The provided argument does not have array buffer internal slots.
+    try {
+      // The provided argument has typed array internal slots.
+      return call(getTypedArrayBuffer, $, []);
+    } catch {
+      /* do nothing */
+    }
+    try {
+      // The provided argument has data view internal slots.
+      return call(getViewBuffer, $, []);
+    } catch {
+      /* do nothing */
+    }
+    throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
   }
 };
 
old mode 100755 (executable)
new mode 100644 (file)
index 82fa4e0..98f441a
@@ -16,6 +16,7 @@ import {
   it,
 } from "./dev-deps.js";
 import {
+  arrayBufferSlice,
   base16Binary,
   base16String,
   base32Binary,
@@ -24,11 +25,32 @@ import {
   base64String,
   filenameSafeBase64Binary,
   filenameSafeBase64String,
+  get16BitSignedIntegralItem,
+  get16BitUnsignedIntegralItem,
+  get32BitFloatingPointItem,
+  get32BitSignedIntegralItem,
+  get32BitUnsignedIntegralItem,
+  get64BitFloatingPointItem,
+  get64BitSignedIntegralItem,
+  get64BitUnsignedIntegralItem,
+  get8BitSignedIntegralItem,
+  get8BitUnsignedIntegralItem,
+  isArrayBuffer,
+  isArrayBufferView,
   isBase16,
   isBase32,
   isBase64,
   isFilenameSafeBase64,
+  isSharedArrayBuffer,
+  isTypedArray,
   isWRMGBase32,
+  set16BitIntegralItem,
+  set32BitFloatingPointItem,
+  set32BitIntegralItem,
+  set64BitFloatingPointItem,
+  set64BitIntegralItem,
+  set8BitIntegralItem,
+  toArrayBuffer,
   wrmgBase32Binary,
   wrmgBase32String,
 } from "./binary.js";
@@ -137,6 +159,45 @@ const data = new Map([
   }],
 ]);
 
+describe("arrayBufferSlice", () => {
+  it("[[Call]] slices an `ArrayBuffer`", () => {
+    const baseBuffer = Uint8Array.from([2, 3, 1, 9, 8, 5]).buffer;
+    assertEquals(
+      new Uint8Array(arrayBufferSlice(baseBuffer, 1, 4)),
+      Uint8Array.from([3, 1, 9]),
+    );
+  });
+
+  it("[[Call]] slices an `SharedArrayBuffer`", () => {
+    const baseBuffer = new SharedArrayBuffer(6);
+    new Uint8Array(baseBuffer).set([2, 3, 1, 9, 8, 5], 0);
+    assertEquals(
+      new Uint8Array(arrayBufferSlice(baseBuffer, 1, 4)),
+      Uint8Array.from([3, 1, 9]),
+    );
+  });
+
+  it("[[Call]] throws for others", () => {
+    [
+      undefined,
+      null,
+      true,
+      Symbol(),
+      27,
+      98n,
+      {},
+      [],
+      () => {},
+      new Proxy({}, {}),
+      "string",
+      new DataView(new ArrayBuffer()),
+      new Uint8Array(),
+    ].forEach((value) =>
+      assertThrows(() => arrayBufferSlice(value, 0, 0))
+    );
+  });
+});
+
 describe("base16Binary", () => {
   it("[[Call]] returns the correct data", () => {
     assertEquals(
@@ -578,11 +639,266 @@ describe("filenameSafeBase64String", () => {
   });
 });
 
+describe("get8BitSignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from([0, -1, 0]).buffer;
+    assertStrictEquals(get8BitSignedIntegralItem(buffer, 1), -1n);
+    assertStrictEquals(
+      get8BitSignedIntegralItem(new DataView(buffer), 1),
+      -1n,
+    );
+    assertStrictEquals(
+      get8BitSignedIntegralItem(new Uint8Array(buffer), 1),
+      -1n,
+    );
+  });
+});
+
+describe("get8BitUnsignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from([0, -1, 0]).buffer;
+    assertStrictEquals(get8BitUnsignedIntegralItem(buffer, 1), 255n);
+    assertStrictEquals(
+      get8BitUnsignedIntegralItem(new DataView(buffer), 1),
+      255n,
+    );
+    assertStrictEquals(
+      get8BitUnsignedIntegralItem(new Int8Array(buffer), 1),
+      255n,
+    );
+  });
+});
+
+describe("get16BitSignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from([0, 0, -1, -1, 0, 0]).buffer;
+    assertStrictEquals(get16BitSignedIntegralItem(buffer, 2), -1n);
+    assertStrictEquals(
+      get16BitSignedIntegralItem(new DataView(buffer), 2),
+      -1n,
+    );
+    assertStrictEquals(
+      get16BitSignedIntegralItem(new Uint16Array(buffer), 2),
+      -1n,
+    );
+  });
+});
+
+describe("get16BitUnsignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from([0, 0, -1, -1, 0, 0]).buffer;
+    assertStrictEquals(
+      get16BitUnsignedIntegralItem(buffer, 2),
+      (1n << 16n) - 1n,
+    );
+    assertStrictEquals(
+      get16BitUnsignedIntegralItem(new DataView(buffer), 2),
+      (1n << 16n) - 1n,
+    );
+    assertStrictEquals(
+      get16BitUnsignedIntegralItem(new Int16Array(buffer), 2),
+      (1n << 16n) - 1n,
+    );
+  });
+});
+
+describe("get32BitFloatingPointItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = new ArrayBuffer(12);
+    const view = new DataView(buffer);
+    view.setFloat32(0, NaN);
+    view.setFloat32(4, -Infinity);
+    view.setFloat32(8, -0);
+    assertStrictEquals(get32BitFloatingPointItem(buffer, 0), NaN);
+    assertStrictEquals(
+      get32BitFloatingPointItem(buffer, 4),
+      -Infinity,
+    );
+    assertStrictEquals(get32BitFloatingPointItem(buffer, 8), -0);
+    assertStrictEquals(
+      get32BitFloatingPointItem(new DataView(buffer), 4),
+      -Infinity,
+    );
+    assertStrictEquals(
+      get32BitFloatingPointItem(new Uint32Array(buffer), 4),
+      -Infinity,
+    );
+  });
+});
+
+describe("get32BitSignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from(
+      [0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0],
+    ).buffer;
+    assertStrictEquals(get32BitSignedIntegralItem(buffer, 4), -1n);
+    assertStrictEquals(
+      get32BitSignedIntegralItem(new DataView(buffer), 4),
+      -1n,
+    );
+    assertStrictEquals(
+      get32BitSignedIntegralItem(new Uint32Array(buffer), 4),
+      -1n,
+    );
+  });
+});
+
+describe("get32BitUnsignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from(
+      [0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0],
+    ).buffer;
+    assertStrictEquals(
+      get32BitUnsignedIntegralItem(buffer, 4),
+      (1n << 32n) - 1n,
+    );
+    assertStrictEquals(
+      get32BitUnsignedIntegralItem(new DataView(buffer), 4),
+      (1n << 32n) - 1n,
+    );
+    assertStrictEquals(
+      get32BitUnsignedIntegralItem(new Int32Array(buffer), 4),
+      (1n << 32n) - 1n,
+    );
+  });
+});
+
+describe("get64BitFloatingPointItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = new ArrayBuffer(24);
+    const view = new DataView(buffer);
+    view.setFloat64(0, NaN);
+    view.setFloat64(8, -Infinity);
+    view.setFloat64(16, -0);
+    assertStrictEquals(get64BitFloatingPointItem(buffer, 0), NaN);
+    assertStrictEquals(
+      get64BitFloatingPointItem(buffer, 8),
+      -Infinity,
+    );
+    assertStrictEquals(get64BitFloatingPointItem(buffer, 16), -0);
+    assertStrictEquals(
+      get64BitFloatingPointItem(new DataView(buffer), 8),
+      -Infinity,
+    );
+    assertStrictEquals(
+      get64BitFloatingPointItem(new BigUint64Array(buffer), 8),
+      -Infinity,
+    );
+  });
+});
+
+describe("get64BitSignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from(
+      [0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1],
+    ).buffer;
+    assertStrictEquals(get64BitSignedIntegralItem(buffer, 8), -1n);
+    assertStrictEquals(
+      get64BitSignedIntegralItem(new DataView(buffer), 8),
+      -1n,
+    );
+    assertStrictEquals(
+      get64BitSignedIntegralItem(new BigUint64Array(buffer), 8),
+      -1n,
+    );
+  });
+});
+
+describe("get64BitUnsignedIntegralItem", () => {
+  it("[[Call]] gets the item", () => {
+    const buffer = Int8Array.from(
+      [0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1],
+    ).buffer;
+    assertStrictEquals(
+      get64BitUnsignedIntegralItem(buffer, 8),
+      (1n << 64n) - 1n,
+    );
+    assertStrictEquals(
+      get64BitUnsignedIntegralItem(new DataView(buffer), 8),
+      (1n << 64n) - 1n,
+    );
+    assertStrictEquals(
+      get64BitUnsignedIntegralItem(new BigInt64Array(buffer), 8),
+      (1n << 64n) - 1n,
+    );
+  });
+});
+
+describe("isArrayBuffer", () => {
+  it("[[Call]] returns true for array buffers", () => {
+    assertStrictEquals(
+      isArrayBuffer(new ArrayBuffer()),
+      true,
+    );
+    assertStrictEquals(
+      isArrayBuffer(new SharedArrayBuffer()),
+      true,
+    );
+  });
+
+  it("[[Call]] returns false for others", () => {
+    [
+      undefined,
+      null,
+      true,
+      Symbol(),
+      27,
+      98n,
+      {},
+      [],
+      () => {},
+      new Proxy({}, {}),
+      "string",
+      new DataView(new ArrayBuffer()),
+      new Uint8Array(),
+    ].forEach((value) =>
+      assertStrictEquals(isArrayBuffer(value), false)
+    );
+  });
+});
+
+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 +918,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 +945,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 +971,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 +1002,119 @@ describe("isFilenameSafeBase64", () => {
       "abc/",
       "a",
       "abc==",
-    ].forEach((value) => assert(!isFilenameSafeBase64(value)));
+    ].forEach((value) =>
+      assertStrictEquals(isFilenameSafeBase64(value), false)
+    );
+  });
+});
+
+describe("isSharedArrayBuffer", () => {
+  it("[[Call]] returns true for shared array buffers", () => {
+    assertStrictEquals(
+      isSharedArrayBuffer(new SharedArrayBuffer()),
+      true,
+    );
+  });
+
+  it("[[Call]] returns false for others", () => {
+    [
+      undefined,
+      null,
+      true,
+      Symbol(),
+      27,
+      98n,
+      {},
+      [],
+      () => {},
+      new Proxy({}, {}),
+      "string",
+      new ArrayBuffer(),
+      new DataView(new ArrayBuffer()),
+      new Uint8Array(),
+    ].forEach((value) =>
+      assertStrictEquals(isSharedArrayBuffer(value), false)
+    );
+  });
+});
+
+describe("isTypedArray", () => {
+  it("[[Call]] returns true for typed arrays", () => {
+    assertStrictEquals(
+      isTypedArray(new Uint8Array()),
+      true,
+    );
+    assertStrictEquals(
+      isTypedArray(new BigInt64Array()),
+      true,
+    );
+  });
+
+  it("[[Call]] returns false for others", () => {
+    [
+      undefined,
+      null,
+      true,
+      Symbol(),
+      27,
+      98n,
+      {},
+      [],
+      () => {},
+      new Proxy({}, {}),
+      "string",
+      new ArrayBuffer(),
+      new SharedArrayBuffer(),
+      new DataView(new ArrayBuffer()),
+    ].forEach((value) =>
+      assertStrictEquals(isTypedArray(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 +1133,133 @@ describe("isWRMGBase32", () => {
       "ABCU",
       "A",
       "ABCDEFGH1",
-    ].forEach((value) => assert(!isWRMGBase32(value)));
+    ].forEach((value) =>
+      assertStrictEquals(isWRMGBase32(value), false)
+    );
+  });
+});
+
+describe("set8BitIntegralItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(3);
+    set8BitIntegralItem(buffer, 1, -1n);
+    set8BitIntegralItem(buffer, 2, (1 << 8) - 1);
+    assertEquals(
+      new Int8Array(buffer),
+      Int8Array.from([0, -1, -1]),
+    );
+  });
+});
+
+describe("set16BitIntegralItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(6);
+    set16BitIntegralItem(buffer, 2, -1n);
+    set16BitIntegralItem(buffer, 4, (1 << 16) - 1);
+    assertEquals(
+      new Int8Array(buffer),
+      Int8Array.from([0, 0, -1, -1, -1, -1]),
+    );
+  });
+});
+
+describe("set32BitFloatingPointItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(12);
+    const expected = new ArrayBuffer(12);
+    const view = new DataView(expected);
+    view.setFloat32(0, NaN);
+    set32BitFloatingPointItem(buffer, 0, NaN);
+    view.setFloat32(4, -Infinity);
+    set32BitFloatingPointItem(buffer, 4, -Infinity);
+    view.setFloat32(8, -0);
+    set32BitFloatingPointItem(buffer, 8, -0);
+    assertEquals(
+      new Uint8Array(buffer),
+      new Uint8Array(expected),
+    );
+  });
+});
+
+describe("set32BitIntegralItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(12);
+    set32BitIntegralItem(buffer, 4, -1n);
+    set32BitIntegralItem(buffer, 8, -1 >>> 0);
+    assertEquals(
+      new Int8Array(buffer),
+      Int8Array.from([0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1]),
+    );
+  });
+});
+
+describe("set64BitFloatingPointItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(24);
+    const expected = new ArrayBuffer(24);
+    const view = new DataView(expected);
+    view.setFloat64(0, NaN);
+    set64BitFloatingPointItem(buffer, 0, NaN);
+    view.setFloat64(4, -Infinity);
+    set64BitFloatingPointItem(buffer, 4, -Infinity);
+    view.setFloat64(8, -0);
+    set64BitFloatingPointItem(buffer, 8, -0);
+    assertEquals(
+      new Uint8Array(buffer),
+      new Uint8Array(expected),
+    );
+  });
+});
+
+describe("set64BitIntegralItem", () => {
+  it("[[Call]] sets the item", () => {
+    const buffer = new ArrayBuffer(12);
+    set64BitIntegralItem(buffer, 4, -1n);
+    assertEquals(
+      new Int8Array(buffer),
+      Int8Array.from([0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1]),
+    );
+  });
+});
+
+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);
+      })
+    );
   });
 });
 
index 7319b7314399a71ee5bf028c7ae49ea22593d952..3cfe9385acfbd44423d777c104ad590c8a4d6704 100644 (file)
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ collection.js
 // ====================================================================
 //
-// Copyright © 2020–2022 Lady [@ Lady’s Computer].
+// Copyright © 2020–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -18,6 +18,8 @@ import {
 } from "./numeric.js";
 import { sameValue, type } from "./value.js";
 
+const { prototype: arrayPrototype } = Array;
+
 export const {
   /** Returns an array of the provided values. */
   of: array,
@@ -30,7 +32,7 @@ export const {
 } = Array;
 
 /**
- * Returns -0 if the provided argument is "-0"; returns a number
+ * Returns 0 if the provided argument is "-0"; returns a number
  * representing the index if the provided argument is a canonical
  * numeric index string; otherwise, returns undefined.
  *
@@ -49,34 +51,35 @@ export const canonicalNumericIndexString = ($) => {
 };
 
 /**
- * Returns the result of catenating the provided arraylikes, returning
- * a new collection according to the algorithm of Array::concat.
+ * Returns the result of catenating the provided arraylikes into a new
+ * collection according to the algorithm of `Array::concat`.
  */
-export const catenate = makeCallable(Array.prototype.concat);
+export const catenate = makeCallable(arrayPrototype.concat);
 
 /**
  * Copies the items in the provided object to a new location according
- * to the algorithm of Array::copyWithin.
+ * to the algorithm of `Array::copyWithin`.
  */
-export const copyWithin = makeCallable(Array.prototype.copyWithin);
+export const copyWithin = makeCallable(arrayPrototype.copyWithin);
 
 /**
- * Fills the provided object with the provided value using the
- * algorithm of Array::fill.
+ * Fills the provided object with the provided value according to the
+ * algorithm of `Array::fill`.
  */
-export const fill = makeCallable(Array.prototype.fill);
+export const fill = makeCallable(arrayPrototype.fill);
 
 /**
  * Returns the result of filtering the provided object with the
- * provided callback, using the algorithm of Array::filter.
+ * provided callback, according to the algorithm of `Array::filter`.
  */
-export const filter = makeCallable(Array.prototype.filter);
+export const filter = makeCallable(arrayPrototype.filter);
 
 /**
  * Returns the first index in the provided object whose value satisfies
- * the provided callback using the algorithm of Array::findIndex.
+ * the provided callback according to the algorithm of
+ * `Array::findIndex`.
  */
-export const findIndex = makeCallable(Array.prototype.findIndex);
+export const findIndex = makeCallable(arrayPrototype.findIndex);
 
 /**
  * Returns the first indexed entry in the provided object whose value
@@ -106,74 +109,77 @@ export const findIndexedEntry = (
 
 /**
  * Returns the first indexed value in the provided object which
- * satisfies the provided callback, using the algorithm of Array::find.
+ * satisfies the provided callback, according to the algorithm of
+ * `Array::find`.
  */
-export const findItem = makeCallable(Array.prototype.find);
+export const findItem = makeCallable(arrayPrototype.find);
 
 /**
  * Returns the result of flatmapping the provided value with the
- * provided callback using the algorithm of Array::flatMap.
+ * provided callback according to the algorithm of `Array::flatMap`.
  */
-export const flatmap = makeCallable(Array.prototype.flatMap);
+export const flatmap = makeCallable(arrayPrototype.flatMap);
 
 /**
- * Returns the result of flattening the provided object using the
- * algorithm of Array::flat.
+ * Returns the result of flattening the provided object according to
+ * the algorithm of `Array::flat`.
  */
-export const flatten = makeCallable(Array.prototype.flat);
+export const flatten = makeCallable(arrayPrototype.flat);
 
 /**
  * Returns the first index of the provided object with a value
  * equivalent to the provided value according to the algorithm of
- * Array::indexOf.
+ * `Array::indexOf`.
  */
-export const getFirstIndex = makeCallable(Array.prototype.indexOf);
+export const getFirstIndex = makeCallable(arrayPrototype.indexOf);
 
 /**
- * Returns the item on the provided object at the provided index using
- * the algorithm of Array::at.
+ * Returns the item on the provided object at the provided index
+ * according to the algorithm of `Array::at`.
  */
-export const getItem = makeCallable(Array.prototype.at);
+export const getItem = makeCallable(arrayPrototype.at);
 
 /**
  * Returns the last index of the provided object with a value
  * equivalent to the provided value according to the algorithm of
- * Array::lastIndexOf.
+ * `Array::lastIndexOf`.
  */
-export const getLastIndex = makeCallable(Array.prototype.lastIndexOf);
+export const getLastIndex = makeCallable(arrayPrototype.lastIndexOf);
 
 /**
  * Returns whether every indexed value in the provided object satisfies
- * the provided function, using the algorithm of Array::every.
+ * the provided function, according to the algorithm of `Array::every`.
  */
-export const hasEvery = makeCallable(Array.prototype.every);
+export const hasEvery = makeCallable(arrayPrototype.every);
 
 /**
  * Returns whether the provided object has an indexed value which
- * satisfies the provided function, using the algorithm of Array::some.
+ * satisfies the provided function, according to the algorithm of
+ * `Array::some`.
  */
-export const hasSome = makeCallable(Array.prototype.some);
+export const hasSome = makeCallable(arrayPrototype.some);
 
 /**
  * Returns whether the provided object has an indexed value equivalent
- * to the provided value according to the algorithm of Array::includes.
+ * to the provided value according to the algorithm of
+ * `Array::includes`.
  *
- * > ☡ This algorithm treats missing values as `undefined` rather than
- * skipping them.
+ *  This algorithm treats missing values as `undefined` rather than
+ * skipping them.
  */
-export const includes = makeCallable(Array.prototype.includes);
+export const includes = makeCallable(arrayPrototype.includes);
 
 /**
  * Returns an iterator over the indexed entries in the provided value
- * according to the algorithm of Array::entries.
+ * according to the algorithm of `Array::entries`.
  */
-export const indexedEntries = makeCallable(Array.prototype.entries);
+export const indexedEntries = makeCallable(arrayPrototype.entries);
 
 /**
  * Returns an iterator over the indices in the provided value according
- * to the algorithm of Array::keys.
+ * to the algorithm of `Array::keys`.
  */
-export const indices = makeCallable(Array.prototype.keys);
+export const indices = makeCallable(arrayPrototype.keys);
 
 /** Returns whether the provided value is an array index string. */
 export const isArrayIndexString = ($) => {
@@ -214,7 +220,7 @@ export const isArraylikeObject = ($) => {
  * - It requires the `length` property to be an integer index.
  *
  * - It requires the object to be concat‐spreadable, meaning it must
- *   either be an array or have `[Symbol.isConcatSpreadable]` be true.
+ *   either be an array or have `.[Symbol.isConcatSpreadable]` be true.
  */
 export const isCollection = ($) => {
   if (!(type($) === "object" && "length" in $)) {
@@ -264,9 +270,9 @@ export const isIntegerIndexString = ($) => {
 
 /**
  * Returns an iterator over the items in the provided value according
- * to the algorithm of Array::values.
+ * to the algorithm of `Array::values`.
  */
-export const items = makeCallable(Array.prototype.values);
+export const items = makeCallable(arrayPrototype.values);
 
 /**
  * Returns the length of the provided arraylike object.
@@ -280,49 +286,57 @@ export const lengthOfArraylike = ({ length }) => toLength(length);
 
 /**
  * Returns the result of mapping the provided value with the provided
- * callback using the algorithm of Array::map.
+ * callback according to the algorithm of `Array::map`.
  */
-export const map = makeCallable(Array.prototype.map);
+export const map = makeCallable(arrayPrototype.map);
 
-/** Pops from the provided value using the algorithm of Array::pop. */
-export const pop = makeCallable(Array.prototype.pop);
+/**
+ * Pops from the provided value according to the algorithm of
+ * `Array::pop`.
+ */
+export const pop = makeCallable(arrayPrototype.pop);
 
 /**
- * Pushes onto the provided value using the algorithm of Array::push.
+ * Pushes onto the provided value according to the algorithm of
+ * `Array::push`.
  */
-export const push = makeCallable(Array.prototype.push);
+export const push = makeCallable(arrayPrototype.push);
 
 /**
  * Returns the result of reducing the provided value with the provided
- * callback, using the algorithm of Array::reduce.
+ * callback, according to the algorithm of `Array::reduce`.
  */
-export const reduce = makeCallable(Array.prototype.reduce);
+export const reduce = makeCallable(arrayPrototype.reduce);
 
 /**
- * Reverses the provided value using the algorithm of Array::reverse.
+ * Reverses the provided value according to the algorithm of
+ * `Array::reverse`.
  */
-export const reverse = makeCallable(Array.prototype.reverse);
+export const reverse = makeCallable(arrayPrototype.reverse);
 
-/** Shifts the provided value using the algorithm of Array::shift. */
-export const shift = makeCallable(Array.prototype.shift);
+/**
+ * Shifts the provided value according to the algorithm of
+ * `Array::shift`.
+ */
+export const shift = makeCallable(arrayPrototype.shift);
 
 /**
- * Returns a slice of the provided value using the algorithm of
- * Array::slice.
+ * Returns a slice of the provided value according to the algorithm of
+ * `Array::slice`.
  */
-export const slice = makeCallable(Array.prototype.slice);
+export const slice = makeCallable(arrayPrototype.slice);
 
 /**
- * Sorts the provided value in‐place using the algorithm of
- * Array::sort.
+ * Sorts the provided value in‐place according to the algorithm of
+ * `Array::sort`.
  */
-export const sort = makeCallable(Array.prototype.sort);
+export const sort = makeCallable(arrayPrototype.sort);
 
 /**
- * Splices into and out of the provided value using the algorithm of
- * Array::splice.
+ * Splices into and out of the provided value according to the
+ * algorithm of `Array::splice`.
  */
-export const splice = makeCallable(Array.prototype.splice);
+export const splice = makeCallable(arrayPrototype.splice);
 
 /**
  * Returns the result of converting the provided value to an array
@@ -355,6 +369,7 @@ export const toLength = ($) => {
 };
 
 /**
- * Unshifts the provided value using the algorithm of Array::unshift.
+ * Unshifts the provided value according to the algorithm of
+ * `Array::unshift`.
  */
-export const unshift = makeCallable(Array.prototype.unshift);
+export const unshift = makeCallable(arrayPrototype.unshift);
index 46cab860fb27e655a44fba66d211b253758637da..5f397a92d2708331cddc63bef2b82d0cdc72eb68 100644 (file)
@@ -1,19 +1,21 @@
 // ♓🌟 Piscēs ∷ function.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022‐2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
+import { ITERATOR } from "./value.js";
+
 export const {
   /**
    * Creates a bound function from the provided function using the
    * provided this value and arguments list.
    *
-   * ☡ As with call and construct, the arguments must be passed as an
-   * array.
+   * ☡ As with `call` and `construct`, the arguments must be passed as
+   * an array.
    */
   bind,
 
@@ -22,7 +24,7 @@ export const {
    * first argument as the `this` value and the remaining arguments
    * passed through.
    *
-   * ※ This is effectively an alias for Function.prototype.call.bind.
+   * ※ This is effectively an alias for `Function::call.bind`.
    */
   makeCallable,
 } = (() => {
@@ -40,13 +42,12 @@ export const {
     defineProperty: defineOwnProperty,
     getPrototypeOf: getPrototype,
   } = Object;
-  const { iterator: iteratorSymbol } = Symbol;
-  const { [iteratorSymbol]: arrayIterator } = Array.prototype;
+  const { [ITERATOR]: arrayIterator } = Array.prototype;
   const {
     next: arrayIteratorNext,
-  } = getPrototype([][iteratorSymbol]());
+  } = getPrototype([][ITERATOR]());
   const argumentIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: callBind(
           arrayIteratorNext,
@@ -74,23 +75,25 @@ export const {
   };
 })();
 
-/**
- * Calls the provided function with the provided this value and
- * arguments list.
- *
- * ☡ This is an alias for Reflect.apply—the arguments must be passed
- * as an array.
- */
-export const call = Reflect.apply;
+export const {
+  /**
+   * Calls the provided function with the provided this value and
+   * arguments list.
+   *
+   * ☡ This is an alias for `Reflect.apply`—the arguments must be
+   * passed as an arraylike.
+   */
+  apply: call,
 
-/**
- * Constructs the provided function with the provided arguments list
- * and new target.
- *
- * ☡ This is an alias for Reflect.construct—the arguments must be
- * passed as an array.
- */
-export const construct = Reflect.construct;
+  /**
+   * Constructs the provided function with the provided arguments list
+   * and new target.
+   *
+   * ☡ This is an alias for `Reflect.construct`—the arguments must be
+   * passed as an arraylike.
+   */
+  construct,
+} = Reflect;
 
 /**
  * Returns the provided value.
@@ -111,13 +114,17 @@ export const isCallable = ($) => typeof $ === "function";
 export const isConstructor = ($) => {
   // The provided value is an object.
   try {
+    // Try constructing a new object with the provided value as its
+    // `new.target`. This will throw if the provided value is not a
+    // constructor.
     construct(
       function () {},
       [],
       $,
-    ); // will throw if $ is not a constructor
+    );
     return true;
   } catch {
+    // The provided value was not a constructor.
     return false;
   }
 };
diff --git a/iri.js b/iri.js
index 47b1fb83edf81dfa56e4594a8f3e4ae17c5f1c34..5dd9dd098ed778e7e8984e8e20484f0c0f9b6d1d 100644 (file)
--- a/iri.js
+++ b/iri.js
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ iri.js
 // ====================================================================
 //
-// Copyright © 2020, 2022 Lady [@ Lady’s Computer].
+// Copyright © 2020, 2022–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -20,6 +20,7 @@ import {
   stringStartsWith,
   substring,
 } from "./string.js";
+import { ITERATOR } from "./value.js";
 
 const sub·delims = rawString`[!\$&'()*+,;=]`;
 const gen·delims = rawString`[:/?#\[\]@]`;
@@ -198,24 +199,23 @@ export const {
   removeDotSegments,
 } = (() => {
   const TE = TextEncoder;
-  const { iterator: iteratorSymbol } = Symbol;
   const { toString: numberToString } = Number.prototype;
   const { encode: teEncode } = TE.prototype;
 
-  const { [iteratorSymbol]: arrayIterator } = Array.prototype;
+  const { [ITERATOR]: arrayIterator } = Array.prototype;
   const {
     next: arrayIteratorNext,
-  } = Object.getPrototypeOf([][iteratorSymbol]());
+  } = Object.getPrototypeOf([][ITERATOR]());
   const {
     next: generatorIteratorNext,
   } = Object.getPrototypeOf(function* () {}.prototype);
-  const { [iteratorSymbol]: stringIterator } = String.prototype;
+  const { [ITERATOR]: stringIterator } = String.prototype;
   const {
     next: stringIteratorNext,
-  } = Object.getPrototypeOf(""[iteratorSymbol]());
+  } = Object.getPrototypeOf(""[ITERATOR]());
 
   const iriCharacterIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: bind(
           stringIteratorNext,
@@ -226,14 +226,14 @@ export const {
     },
   };
   const iriGeneratorIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: bind(generatorIteratorNext, this.generator(), []),
       };
     },
   };
   const iriSegmentIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: bind(
           arrayIteratorNext,
index 4a066f77098eca61d57a908cc75bf3eddd1112b5..5a3d6ea0a9f8259d2df6374e56f31832739a8e7e 100644 (file)
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ numeric.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -21,49 +21,49 @@ export const {
   /**
    * ln(10).
    *
-   * ※ This is an alias for Math.LN10.
+   * ※ This is an alias for `Math.LN10`.
    */
   LN10,
 
   /**
    * ln(2).
    *
-   * ※ This is an alias for Math.LN2.
+   * ※ This is an alias for `Math.LN2`.
    */
   LN2,
 
   /**
    * log10(ℇ).
    *
-   * ※ This is an alias for Math.LOG10E.
+   * ※ This is an alias for `Math.LOG10E`.
    */
   LOG10E: LOG10ℇ,
 
   /**
    * log2(ℇ).
    *
-   * ※ This is an alias for Math.LOG2E.
+   * ※ This is an alias for `Math.LOG2E`.
    */
   LOG2E: LOG2ℇ,
 
   /**
-   * sqrt(.5).
+   * sqrt(½).
    *
-   * ※ This is an alias for Math.SQRT1_2.
+   * ※ This is an alias for `Math.SQRT1_2`.
    */
   SQRT1_2: RECIPROCAL_SQRT2,
 
   /**
    * sqrt(2).
    *
-   * ※ This is an alias for Math.SQRT2.
+   * ※ This is an alias for `Math.SQRT2`.
    */
   SQRT2,
 
   /**
    * Returns the arccos of the provided value.
    *
-   * ※ This is an alias for Math.acos.
+   * ※ This is an alias for `Math.acos`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -72,7 +72,7 @@ export const {
   /**
    * Returns the arccosh of the provided value.
    *
-   * ※ This is an alias for Math.acosh.
+   * ※ This is an alias for `Math.acosh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -81,7 +81,7 @@ export const {
   /**
    * Returns the arcsin of the provided value.
    *
-   * ※ This is an alias for Math.asin.
+   * ※ This is an alias for `Math.asin`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -90,7 +90,7 @@ export const {
   /**
    * Returns the arcsinh of the provided value.
    *
-   * ※ This is an alias for Math.asinh.
+   * ※ This is an alias for `Math.asinh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -99,7 +99,7 @@ export const {
   /**
    * Returns the arctan of the provided value.
    *
-   * ※ This is an alias for Math.atan.
+   * ※ This is an alias for `Math.atan`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -108,7 +108,7 @@ export const {
   /**
    * Returns the arctanh of the provided value.
    *
-   * ※ This is an alias for Math.atanh.
+   * ※ This is an alias for `Math.atanh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -117,7 +117,7 @@ export const {
   /**
    * Returns the cube root of the provided value.
    *
-   * ※ This is an alias for Math.cbrt.
+   * ※ This is an alias for `Math.cbrt`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -126,7 +126,7 @@ export const {
   /**
    * Returns the ceiling of the provided value.
    *
-   * ※ This is an alias for Math.ceil.
+   * ※ This is an alias for `Math.ceil`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -135,7 +135,7 @@ export const {
   /**
    * Returns the cos of the provided value.
    *
-   * ※ This is an alias for Math.cos.
+   * ※ This is an alias for `Math.cos`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -144,7 +144,7 @@ export const {
   /**
    * Returns the cosh of the provided value.
    *
-   * ※ This is an alias for Math.cosh.
+   * ※ This is an alias for `Math.cosh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -153,7 +153,7 @@ export const {
   /**
    * Returns the Euler number raised to the provided value.
    *
-   * ※ This is an alias for Math.exp.
+   * ※ This is an alias for `Math.exp`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -162,7 +162,7 @@ export const {
   /**
    * Returns the Euler number raised to the provided value, minus one.
    *
-   * ※ This is an alias for Math.expm1.
+   * ※ This is an alias for `Math.expm1`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -171,7 +171,7 @@ export const {
   /**
    * Returns the floor of the provided value.
    *
-   * ※ This is an alias for Math.floor.
+   * ※ This is an alias for `Math.floor`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -181,7 +181,7 @@ export const {
    * Returns the square root of the sum of the squares of the provided
    * arguments.
    *
-   * ※ This is an alias for Math.hypot.
+   * ※ This is an alias for `Math.hypot`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -190,7 +190,7 @@ export const {
   /**
    * Returns the ln of the provided value.
    *
-   * ※ This is an alias for Math.log.
+   * ※ This is an alias for `Math.log`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -199,7 +199,7 @@ export const {
   /**
    * Returns the log10 of the provided value.
    *
-   * ※ This is an alias for Math.log10.
+   * ※ This is an alias for `Math.log10`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -208,7 +208,7 @@ export const {
   /**
    * Returns the ln of one plus the provided value.
    *
-   * ※ This is an alias for Math.log1p.
+   * ※ This is an alias for `Math.log1p`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -217,7 +217,7 @@ export const {
   /**
    * Returns the log2 of the provided value.
    *
-   * ※ This is an alias for Math.log2.
+   * ※ This is an alias for `Math.log2`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -226,14 +226,14 @@ export const {
   /**
    * Returns a pseudo·random value in the range [0, 1).
    *
-   * ※ This is an alias for Math.random.
+   * ※ This is an alias for `Math.random`.
    */
   random: rand,
 
   /**
    * Returns the round of the provided value.
    *
-   * ※ This is an alias for Math.round.
+   * ※ This is an alias for `Math.round`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -242,7 +242,7 @@ export const {
   /**
    * Returns the sinh of the provided value.
    *
-   * ※ This is an alias for Math.sinh.
+   * ※ This is an alias for `Math.sinh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -251,7 +251,7 @@ export const {
   /**
    * Returns the square root of the provided value.
    *
-   * ※ This is an alias for Math.sqrt.
+   * ※ This is an alias for `Math.sqrt`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -260,7 +260,7 @@ export const {
   /**
    * Returns the tan of the provided value.
    *
-   * ※ This is an alias for Math.tan.
+   * ※ This is an alias for `Math.tan`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -269,7 +269,7 @@ export const {
   /**
    * Returns the tanh of the provided value.
    *
-   * ※ This is an alias for Math.tanh.
+   * ※ This is an alias for `Math.tanh`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -278,7 +278,7 @@ export const {
   /**
    * Returns the trunc of the provided value.
    *
-   * ※ This is an alias for Math.trunc.
+   * ※ This is an alias for `Math.trunc`.
    *
    * ☡ This function does not allow big·int arguments.
    */
@@ -287,14 +287,14 @@ export const {
   /**
    * The mathematical constant π.
    *
-   * ※ This is an alias for Math.PI.
+   * ※ This is an alias for `Math.PI`.
    */
   PI: Π,
 
   /**
    * The Euler number.
    *
-   * ※ This is an alias for Math.E.
+   * ※ This is an alias for `Math.E`.
    */
   E: ℇ,
 } = Math;
@@ -303,84 +303,84 @@ export const {
   /**
    * The largest number value less than infinity.
    *
-   * ※ This is an alias for Number.MAX_VALUE.
+   * ※ This is an alias for `Number.MAX_VALUE`.
    */
   MAX_VALUE: MAXIMUM_NUMBER,
 
   /**
    * 2**53 - 1.
    *
-   * ※ This is an alias for Number.MAX_SAFE_INTEGER.
+   * ※ This is an alias for `Number.MAX_SAFE_INTEGER`.
    */
   MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
 
   /**
    * The smallest number value greater than negative infinity.
    *
-   * ※ This is an alias for Number.MIN_VALUE.
+   * ※ This is an alias for `Number.MIN_VALUE`.
    */
   MIN_VALUE: MINIMUM_NUMBER,
 
   /**
    * -(2**53 - 1).
    *
-   * ※ This is an alias for Number.MIN_SAFE_INTEGER.
+   * ※ This is an alias for `Number.MIN_SAFE_INTEGER`.
    */
   MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER,
 
   /**
    * Negative infinity.
    *
-   * ※ This is an alias for Number.NEGATIVE_INFINITY.
+   * ※ This is an alias for `Number.NEGATIVE_INFINITY`.
    */
   NEGATIVE_INFINITY,
 
   /**
    * Nan.
    *
-   * ※ This is an alias for Number.NaN.
+   * ※ This is an alias for `Number.NaN`.
    */
   NaN: NAN,
 
   /**
    * Positive infinity.
    *
-   * ※ This is an alias for Number.POSITIVE_INFINITY.
+   * ※ This is an alias for `Number.POSITIVE_INFINITY`.
    */
   POSITIVE_INFINITY,
 
   /**
    * The difference between 1 and the smallest number greater than 1.
    *
-   * ※ This is an alias for Number.EPSILON.
+   * ※ This is an alias for `Number.EPSILON`.
    */
   EPSILON: Ε,
 
   /**
    * Returns whether the provided value is a finite number.
    *
-   * ※ This is an alias for Number.isFinite.
+   * ※ This is an alias for `Number.isFinite`.
    */
   isFinite: isFiniteNumber,
 
   /**
    * Returns whether the provided value is an integral number.
    *
-   * ※ This is an alias for Number.isInteger.
+   * ※ This is an alias for `Number.isInteger`.
    */
   isInteger: isIntegralNumber,
 
   /**
    * Returns whether the provided value is nan.
    *
-   * ※ This is an alias for Number.isNaN.
+   * ※ This is an alias for `Number.isNaN`.
    */
   isNaN: isNan,
 
   /**
    * Returns whether the provided value is a safe integral number.
    *
-   * ※ This is an alias for Number.isSafeInteger.
+   * ※ This is an alias for `Number.isSafeInteger`.
    */
   isSafeInteger: isSafeIntegralNumber,
 } = Number;
@@ -394,7 +394,7 @@ export const NEGATIVE_ZERO = -0;
 /**
  * Returns the magnitude (absolute value) of the provided value.
  *
- * ※ Unlike Math.abs, this function can take big·int arguments.
+ * ※ Unlike `Math.abs`, this function can take big·int arguments.
  */
 export const abs = ($) => {
   const n = toNumeric($);
@@ -415,17 +415,17 @@ export const {
   /**
    * Returns the arctangent of the dividend of the provided values.
    *
-   * ※ Unlike Math.atan2, this function can take big·int arguments.
+   * ※ Unlike `Math.atan2`, this function can take big·int arguments.
    * However, the result will always be a number.
    */
   atan2,
 
   /**
-   * Returns the number of leading zeroes in the 32‐bit representation of
-   * the provided value.
+   * Returns the number of leading zeroes in the 32‐bit representation
+   * of the provided value.
    *
-   * ※ Unlike Math.clz32, this function accepts either number or big·int
-   * values.
+   * ※ Unlike `Math.clz32`, this function accepts either number or
+   * big·int values.
    */
   clz32,
 
@@ -433,7 +433,7 @@ export const {
    * Returns the 32‐bit float which best approximate the provided
    * value.
    *
-   * ※ Unlike Math.fround, this function can take big·int arguments.
+   * ※ Unlike `Math.fround`, this function can take big·int arguments.
    * However, the result will always be a number.
    */
   toFloat32,
@@ -444,7 +444,9 @@ export const {
     clz32: ($) => {
       const n = toNumeric($);
       return clz32(
-        typeof n === "bigint" ? toNumber(toUintN(32, n)) : n,
+        typeof n === "bigint"
+          ? toNumber(toUnsignedIntegralNumeric(32, n))
+          : n,
       );
     },
     toFloat32: ($) => fround(toNumber($)),
@@ -455,7 +457,7 @@ export const {
  * Returns the highest value of the provided arguments, or negative
  * infinity if no argument is provided.
  *
- * ※ Unlike Math.max, this function accepts either number or big·int
+ * ※ Unlike `Math.max`, this function accepts either number or big·int
  * values. All values must be of the same type, or this function will
  * throw an error.
  *
@@ -504,7 +506,7 @@ export const max = (...$s) => {
  * Returns the lowest value of the provided arguments, or positive
  * infinity if no argument is provided.
  *
- * ※ Unlike Math.min, this function accepts either number or big·int
+ * ※ Unlike `Math.min`, this function accepts either number or big·int
  * values. All values must be of the same type, or this function will
  * throw an error.
  *
@@ -556,17 +558,17 @@ export const min = (...$s) => {
  * signed) zero.
  *
  * For big·ints, the return value of this function is 0n if the
- * provided value is 0n, -1n if the provided value is negative, and +1n
+ * provided value is 0n, 1n if the provided value is negative, and +1n
  * otherwise.
  *
- * For numbers, the return value is nan, -0, or +0 if the provided
- * value is nan, -0, or +0, respectively, and -1 if the provided value
+ * For numbers, the return value is nan, 0, or +0 if the provided
+ * value is nan, −0, or +0, respectively, and −1 if the provided value
  * is negative and +1 if the provided value is positive otherwise. Note
- * that positive and negative infinity will return +1 and -1
+ * that positive and negative infinity will return +1 and 1
  * respectively.
  *
- * ※ Unlike Math.sign, this function accepts either number or big·int
- * values.
+ * ※ Unlike `Math.sign`, this function accepts either number or
+ * big·int values.
  */
 export const sgn = ($) => {
   const n = toNumeric($);
@@ -585,7 +587,7 @@ export const sgn = ($) => {
  *
  * ※ This method is safe to use with numbers.
  *
- * ※ This is effectively an alias for BigInt.
+ * ※ This is effectively an alias for `BigInt`.
  */
 export const { toBigInt } = (() => {
   const makeBigInt = BigInt;
@@ -679,68 +681,6 @@ export const {
   };
 })();
 
-export const {
-  /**
-   * Returns the result of converting the provided value to fit within
-   * the provided number of bits as a signed integer.
-   *
-   * ※ Unlike BigInt.asIntN, this function accepts both big·int and
-   * number values.
-   *
-   * ☡ The first argument, the number of bits, must be a number.
-   */
-  toIntN,
-
-  /**
-   * Returns the result of converting the provided value to fit within
-   * the provided number of bits as an unsigned integer.
-   *
-   * ※ Unlike BigInt.asUintN, this function accepts both big·int and
-   * number values.
-   *
-   * ☡ The first argument, the number of bits, must be a number.
-   */
-  toUintN,
-} = (() => {
-  const { asIntN, asUintN } = BigInt;
-  return {
-    toIntN: (n, $) => {
-      const prim = toPrimitive($);
-      if (typeof prim === "bigint") {
-        // The primitive value is a big·int.
-        return asIntN(n, prim);
-      } else {
-        // The primitive value is not a big·int.
-        const int = trunc(prim);
-        if (!isFiniteNumber(int) || int == 0) {
-          // The truncated value is zero or not finite.
-          return 0;
-        } else {
-          // The truncated value is finite.
-          return toNumber(asIntN(n, toBigInt(int)));
-        }
-      }
-    },
-    toUintN: (n, $) => {
-      const prim = toPrimitive($);
-      if (typeof prim === "bigint") {
-        // The primitive value is a big·int.
-        return asUintN(n, prim);
-      } else {
-        // The primitive value is not a big·int.
-        const int = trunc(prim);
-        if (!isFiniteNumber(int) || int == 0) {
-          // The truncated value is zero or not finite.
-          return 0;
-        } else {
-          // The truncated value is finite.
-          return toNumber(asUintN(n, toBigInt(int)));
-        }
-      }
-    },
-  };
-})();
-
 /**
  * Returns the result of converting the provided number to an integral
  * number.
@@ -784,7 +724,7 @@ export const toIntegralNumberOrInfinity = ($) => {
  *
  * ※ This function is safe to use with big·ints.
  *
- * ※ This is effectively a nonconstructible version of the Number
+ * ※ This is effectively a nonconstructible version of the `Number`
  * constructor.
  */
 export const { toNumber } = (() => {
@@ -803,3 +743,65 @@ export const toNumeric = ($) => {
   const primValue = toPrimitive($, "number");
   return typeof primValue === "bigint" ? primValue : +primValue;
 };
+
+export const {
+  /**
+   * Returns the result of converting the provided value to fit within
+   * the provided number of bits as a signed integer.
+   *
+   * ※ Unlike `BigInt.asIntN`, this function accepts both big·int and
+   * number values.
+   *
+   * ☡ The first argument, the number of bits, must be a number.
+   */
+  toSignedIntegralNumeric,
+
+  /**
+   * Returns the result of converting the provided value to fit within
+   * the provided number of bits as an unsigned integer.
+   *
+   * ※ Unlike `BigInt.asUintN`, this function accepts both big·int and
+   * number values.
+   *
+   * ☡ The first argument, the number of bits, must be a number.
+   */
+  toUnsignedIntegralNumeric,
+} = (() => {
+  const { asIntN, asUintN } = BigInt;
+  return {
+    toSignedIntegralNumeric: (n, $) => {
+      const prim = toPrimitive($);
+      if (typeof prim === "bigint") {
+        // The primitive value is a big·int.
+        return asIntN(n, prim);
+      } else {
+        // The primitive value is not a big·int.
+        const int = trunc(prim);
+        if (!isFiniteNumber(int) || int == 0) {
+          // The truncated value is zero or not finite.
+          return 0;
+        } else {
+          // The truncated value is finite.
+          return toNumber(asIntN(n, toBigInt(int)));
+        }
+      }
+    },
+    toUnsignedIntegralNumeric: (n, $) => {
+      const prim = toPrimitive($);
+      if (typeof prim === "bigint") {
+        // The primitive value is a big·int.
+        return asUintN(n, prim);
+      } else {
+        // The primitive value is not a big·int.
+        const int = trunc(prim);
+        if (!isFiniteNumber(int) || int == 0) {
+          // The truncated value is zero or not finite.
+          return 0;
+        } else {
+          // The truncated value is finite.
+          return toNumber(asUintN(n, toBigInt(int)));
+        }
+      }
+    },
+  };
+})();
index 27776cde54c4daef8aaa8f27d9356e7d954f88cb..4d3d2cb8616cdccacd4594935e80ba8c82e5469b 100644 (file)
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ numeric.test.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -28,10 +28,10 @@ import {
   toFloat32,
   toIntegralNumber,
   toIntegralNumberOrInfinity,
-  toIntN,
   toNumber,
   toNumeric,
-  toUintN,
+  toSignedIntegralNumeric,
+  toUnsignedIntegralNumeric,
 } from "./numeric.js";
 
 describe("NEGATIVE_ZERO", () => {
@@ -206,18 +206,18 @@ describe("toFloat32", () => {
   });
 });
 
-describe("toIntN", () => {
+describe("toSignedIntegralNumeric", () => {
   it("[[Call]] converts to an int·n", () => {
-    assertStrictEquals(toIntN(2, 7n), -1n);
+    assertStrictEquals(toSignedIntegralNumeric(2, 7n), -1n);
   });
 
   it("[[Call]] works with numbers", () => {
-    assertStrictEquals(toIntN(2, 7), -1);
+    assertStrictEquals(toSignedIntegralNumeric(2, 7), -1);
   });
 
   it("[[Call]] works with non‐integers", () => {
-    assertStrictEquals(toIntN(2, 7.21), -1);
-    assertStrictEquals(toIntN(2, Infinity), 0);
+    assertStrictEquals(toSignedIntegralNumeric(2, 7.21), -1);
+    assertStrictEquals(toSignedIntegralNumeric(2, Infinity), 0);
   });
 });
 
@@ -306,17 +306,17 @@ describe("toNumeric", () => {
   });
 });
 
-describe("toUintN", () => {
+describe("toUnsignedIntegralNumeric", () => {
   it("[[Call]] converts to an int·n", () => {
-    assertStrictEquals(toUintN(2, 7n), 3n);
+    assertStrictEquals(toUnsignedIntegralNumeric(2, 7n), 3n);
   });
 
   it("[[Call]] works with numbers", () => {
-    assertStrictEquals(toUintN(2, 7), 3);
+    assertStrictEquals(toUnsignedIntegralNumeric(2, 7), 3);
   });
 
   it("[[Call]] works with non‐integers", () => {
-    assertStrictEquals(toUintN(2, 7.21), 3);
-    assertStrictEquals(toUintN(2, Infinity), 0);
+    assertStrictEquals(toUnsignedIntegralNumeric(2, 7.21), 3);
+    assertStrictEquals(toUnsignedIntegralNumeric(2, Infinity), 0);
   });
 });
index 9986291eb77d1c4cc57143ee9d93760aded80393..a4037951fcd77fa746e8aabe23a974948e29ed9a 100644 (file)
--- a/object.js
+++ b/object.js
@@ -1,14 +1,14 @@
 // ♓🌟 Piscēs ∷ object.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
 import { bind, call } from "./function.js";
-import { toPrimitive, type } from "./value.js";
+import { ITERATOR, SPECIES, toPrimitive, type } from "./value.js";
 
 /**
  * An object whose properties are lazy‐loaded from the methods on the
@@ -28,12 +28,12 @@ import { toPrimitive, type } from "./value.js";
  * Methods will be called with the resulting object as their this
  * value.
  *
- * LazyLoader objects have the same prototype as the passed methods
+ * `LazyLoader` objects have the same prototype as the passed methods
  * object.
  */
 export class LazyLoader extends null {
   /**
-   * Constructs a new LazyLoader object.
+   * Constructs a new `LazyLoader` object.
    *
    * ☡ This function throws if the provided value is not an object.
    */
@@ -96,7 +96,7 @@ export const { PropertyDescriptor } = (() => {
      * object.
      *
      * The resulting object is proxied to enforce types (for example,
-     * its `enumerable` property, if defined, will always be a
+     * its `.enumerable` property, if defined, will always be a
      * boolean).
      */
     constructor(O) {
@@ -395,7 +395,7 @@ export const {
    * descriptors on the enumerable own properties of the provided
    * additional objects.
    *
-   * ※ This differs from Object.defineProperties in that it can take
+   * ※ This differs from `Object.defineProperties` in that it can take
    * multiple source objects.
    */
   defineOwnProperties,
@@ -419,7 +419,7 @@ export const {
    * Defines an own property on the provided object on the provided
    * property key using the provided property descriptor.
    *
-   * ※ This is an alias for Object.defineProperty.
+   * ※ This is an alias for `Object.defineProperty`.
    */
   defineProperty: defineOwnProperty,
 
@@ -428,7 +428,7 @@ export const {
    * properties as nonconfigurable and (if data properties)
    * nonwritable, and returns the object.
    *
-   * ※ This is an alias for Object.freeze.
+   * ※ This is an alias for `Object.freeze`.
    */
   freeze,
 
@@ -437,7 +437,7 @@ export const {
    * provided property key on the provided object, or null if none
    * exists.
    *
-   * ※ This is an alias for Object.getOwnPropertyDescriptor.
+   * ※ This is an alias for `Object.getOwnPropertyDescriptor`.
    */
   getOwnPropertyDescriptor,
 
@@ -445,7 +445,7 @@ export const {
    * Returns the property descriptors for the own properties on the
    * provided object.
    *
-   * ※ This is an alias for Object.getOwnPropertyDescriptors.
+   * ※ This is an alias for `Object.getOwnPropertyDescriptors`.
    */
   getOwnPropertyDescriptors,
 
@@ -455,7 +455,7 @@ export const {
    *
    * ☡ This includes both enumerable and non·enumerable properties.
    *
-   * ※ This is an alias for Object.getOwnPropertyNames.
+   * ※ This is an alias for `Object.getOwnPropertyNames`.
    */
   getOwnPropertyNames: getOwnPropertyStrings,
 
@@ -465,14 +465,14 @@ export const {
    *
    * ☡ This includes both enumerable and non·enumerable properties.
    *
-   * ※ This is an alias for Object.getOwnPropertySymbols.
+   * ※ This is an alias for `Object.getOwnPropertySymbols`.
    */
   getOwnPropertySymbols,
 
   /**
    * Returns the prototype of the provided object.
    *
-   * ※ This is an alias for Object.getPrototypeOf.
+   * ※ This is an alias for `Object.getPrototypeOf`.
    */
   getPrototypeOf: getPrototype,
 
@@ -480,36 +480,36 @@ export const {
    * Returns whether the provided object has an own property with the
    * provided property key.
    *
-   * ※ This is an alias for Object.hasOwn.
+   * ※ This is an alias for `Object.hasOwn`.
    */
   hasOwn: hasOwnProperty,
 
   /**
    * Returns whether the provided object is extensible.
    *
-   * ※ This is an alias for Object.isExtensible.
+   * ※ This is an alias for `Object.isExtensible`.
    */
-  isExtensible,
+  isExtensible: isExtensibleObject,
 
   /**
    * Returns whether the provided object is frozen.
    *
-   * ※ This is an alias for Object.isFrozen.
+   * ※ This is an alias for `Object.isFrozen`.
    */
-  isFrozen,
+  isFrozen: isFrozenObject,
 
   /**
    * Returns whether the provided object is sealed.
    *
-   * ※ This is an alias for Object.isSealed.
+   * ※ This is an alias for `Object.isSealed`.
    */
-  isSealed,
+  isSealed: isSealedObject,
 
   /**
    * Returns an array of key~value pairs for the enumerable,
    * string‐valued property keys on the provided object.
    *
-   * ※ This is an alias for Object.entries.
+   * ※ This is an alias for `Object.entries`.
    */
   entries: namedEntries,
 
@@ -517,7 +517,7 @@ export const {
    * Returns an array of the enumerable, string‐valued property keys on
    * the provided object.
    *
-   * ※ This is an alias for Object.keys.
+   * ※ This is an alias for `Object.keys`.
    */
   keys: namedKeys,
 
@@ -525,7 +525,7 @@ export const {
    * Returns an array of property values for the enumerable,
    * string‐valued property keys on the provided object.
    *
-   * ※ This is an alias for Object.values.
+   * ※ This is an alias for `Object.values`.
    */
   values: namedValues,
 
@@ -533,14 +533,14 @@ export const {
    * Returns a new object with the provided prototype and property
    * descriptors.
    *
-   * ※ This is an alias for Object.create.
+   * ※ This is an alias for `Object.create`.
    */
   create: objectCreate,
 
   /**
    * Returns a new object with the provided property keys and values.
    *
-   * ※ This is an alias for Object.fromEntries.
+   * ※ This is an alias for `Object.fromEntries`.
    */
   fromEntries: objectFromEntries,
 
@@ -548,7 +548,7 @@ export const {
    * Marks the provided object as non·extensible, and returns the
    * object.
    *
-   * ※ This is an alias for Object.preventExtensions.
+   * ※ This is an alias for `Object.preventExtensions`.
    */
   preventExtensions,
 
@@ -556,7 +556,7 @@ export const {
    * Marks the provided object as non·extensible and marks all its
    * properties as nonconfigurable, and returns the object.
    *
-   * ※ This is an alias for Object.seal.
+   * ※ This is an alias for `Object.seal`.
    */
   seal,
 
@@ -564,7 +564,7 @@ export const {
    * Sets the values of the enumerable own properties of the provided
    * additional objects on the provided object.
    *
-   * ※ This is an alias for Object.assign.
+   * ※ This is an alias for `Object.assign`.
    */
   assign: setPropertyValues,
 
@@ -572,7 +572,7 @@ export const {
    * Sets the prototype of the provided object to the provided value
    * and returns the object.
    *
-   * ※ This is an alias for Object.setPrototypeOf.
+   * ※ This is an alias for `Object.setPrototypeOf`.
    */
   setPrototypeOf: setPrototype,
 } = Object;
@@ -582,7 +582,7 @@ export const {
    * Removes the provided property key from the provided object and
    * returns the object.
    *
-   * ※ This function differs from Reflect.deleteProperty and the
+   * ※ This function differs from `Reflect.deleteProperty` and the
    * `delete` operator in that it throws if the deletion is
    * unsuccessful.
    *
@@ -593,7 +593,7 @@ export const {
   /**
    * Returns an array of property keys on the provided object.
    *
-   * ※ This is effectively an alias for Reflect.ownKeys, except that
+   * ※ This is effectively an alias for `Reflect.ownKeys`, except that
    * it does not require that the argument be an object.
    */
   getOwnPropertyKeys,
@@ -602,7 +602,7 @@ export const {
    * Returns the value of the provided property key on the provided
    * object.
    *
-   * ※ This is effectively an alias for Reflect.get, except that it
+   * ※ This is effectively an alias for `Reflect.get`, except that it
    * does not require that the argument be an object.
    */
   getPropertyValue,
@@ -611,7 +611,7 @@ export const {
    * Returns whether the provided property key exists on the provided
    * object.
    *
-   * ※ This is effectively an alias for Reflect.has, except that it
+   * ※ This is effectively an alias for `Reflect.has`, except that it
    * does not require that the argument be an object.
    *
    * ※ This includes properties present on the prototype chain.
@@ -622,8 +622,8 @@ export const {
    * Sets the provided property key to the provided value on the
    * provided object and returns the object.
    *
-   * ※ This function differs from Reflect.set in that it throws if the
-   * setting is unsuccessful.
+   * ※ This function differs from `Reflect.set` in that it throws if
+   * the setting is unsuccessful.
    *
    * ☡ This function throws if the first argument is not an object.
    */
@@ -677,26 +677,22 @@ export const {
    *   property with the same getter *and* setter.
    *
    * The prototype for the resulting object will be taken from the
-   * `prototype` property of the provided constructor, or the
-   * `prototype` of the `constructor` of the provided object if the
+   * `.prototype` property of the provided constructor, or the
+   * `.prototype` of the `.constructor` of the provided object if the
    * provided constructor is undefined. If the used constructor has a
-   * nonnullish `Symbol.species`, that will be used instead. If the
+   * nonnullish `.[Symbol.species]`, that will be used instead. If the
    * used constructor or species is nullish or does not have a
-   * `prototype` property, the prototype is set to null.
+   * `.prototype` property, the prototype is set to null.
    *
    * ※ The prototype of the provided object itself is ignored.
    */
   frozenCopy,
 } = (() => {
-  const {
-    iterator: iteratorSymbol,
-    species: speciesSymbol,
-  } = Symbol;
   const {
     next: generatorIteratorNext,
   } = getPrototype(function* () {}.prototype);
   const propertyDescriptorEntryIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: bind(generatorIteratorNext, this.generator(), []),
       };
@@ -713,8 +709,8 @@ export const {
         // O is not null or undefined.
         //
         // (If not provided, the constructor will be the value of
-        // getting the `constructor` property of O.)
-        const species = constructor?.[speciesSymbol] ?? constructor;
+        // getting the `.constructor` property of O.)
+        const species = constructor?.[SPECIES] ?? constructor;
         return preventExtensions(
           objectCreate(
             species == null || !("prototype" in species)
index eb6f6fb4db0fa5444496190ce61ba766a81cbbd8..e697a1fcad38f51cfba5e0fe8ae6d2dc3d34d7e1 100644 (file)
--- a/string.js
+++ b/string.js
@@ -15,24 +15,29 @@ import {
   objectCreate,
   setPrototype,
 } from "./object.js";
-import { type } from "./value.js";
+import { ITERATOR, TO_STRING_TAG } from "./value.js";
+
+const RE = RegExp;
+const { prototype: rePrototype } = RE;
+const { prototype: arrayPrototype } = Array;
+const { prototype: stringPrototype } = String;
+
+const { exec: reExec } = rePrototype;
 
 export const {
   /**
-   * A RegExp·like object which only matches entire strings, and may
+   * A `RegExp`like object which only matches entire strings, and may
    * have additional constraints specified.
    *
    * Matchers are callable objects and will return true if they are
    * called with a string that they match, and false otherwise.
    * Matchers will always return false if called with nonstrings,
-   * although other methods like `exec` coerce their arguments and may
-   * still return true.
+   * although other methods like `::exec` coerce their arguments and
+   * may still return true.
    */
   Matcher,
 } = (() => {
-  const RE = RegExp;
-  const { prototype: rePrototype } = RE;
-  const { exec: reExec, toString: reToString } = rePrototype;
+  const { toString: reToString } = rePrototype;
   const getDotAll =
     Object.getOwnPropertyDescriptor(rePrototype, "dotAll").get;
   const getFlags =
@@ -57,7 +62,7 @@ export const {
     #regExp;
 
     /**
-     * Constructs a new Matcher from the provided source.
+     * Constructs a new `Matcher` from the provided source.
      *
      * If the provided source is a regular expression, then it must
      * have the unicode flag set. Otherwise, it is interpreted as the
@@ -71,9 +76,9 @@ export const {
      * A callable constraint on acceptable inputs may be provided as a
      * third argument. If provided, it will be called with three
      * arguments whenever a match appears successful: first, the string
-     * being matched, second, the match result, and third, the Matcher
-     * object itself. If the return value of this call is falsey, then
-     * the match will be considered a failure.
+     * being matched, second, the match result, and third, the
+     * `Matcher` object itself. If the return value of this call is
+     * falsey, then the match will be considered a failure.
      *
      * ☡ If the provided source regular expression uses nongreedy
      * quantifiers, it may not match the whole string even if a match
@@ -88,7 +93,7 @@ export const {
             // The provided value is not a string.
             return false;
           } else {
-            // The provided value is a string. Set the `lastIndex` of
+            // The provided value is a string. Set the `.lastIndex` of
             // the regular expression to 0 and see if the first attempt
             // at a match matches the whole string and passes the
             // provided constraint (if present).
@@ -142,13 +147,13 @@ export const {
       }
     }
 
-    /** Gets whether the dotAll flag is present on this Matcher. */
+    /** Gets whether the dot‐all flag is present on this `Matcher`. */
     get dotAll() {
       return call(getDotAll, this.#regExp, []);
     }
 
     /**
-     * Executes this Matcher on the provided value and returns the
+     * Executes this `Matcher` on the provided value and returns the
      * result if there is a match, or null otherwise.
      *
      * Matchers only match if they can match the entire value on the
@@ -179,47 +184,53 @@ export const {
     }
 
     /**
-     * Gets the flags present on this Matcher.
+     * Gets the flags present on this `Matcher`.
      *
-     * ※ This needs to be defined because the internal RegExp object
+     * ※ This needs to be defined because the internal `RegExp` object
      * may have flags which are not yet recognized by ♓🌟 Piscēs.
      */
     get flags() {
       return call(getFlags, this.#regExp, []);
     }
 
-    /** Gets whether the global flag is present on this Matcher. */
+    /** Gets whether the global flag is present on this `Matcher`. */
     get global() {
       return call(getGlobal, this.#regExp, []);
     }
 
-    /** Gets whether the hasIndices flag is present on this Matcher. */
+    /**
+     * Gets whether the has‐indices flag is present on this `Matcher`.
+     */
     get hasIndices() {
       return call(getHasIndices, this.#regExp, []);
     }
 
-    /** Gets whether the ignoreCase flag is present on this Matcher. */
+    /**
+     * Gets whether the ignore‐case flag is present on this `Matcher`.
+     */
     get ignoreCase() {
       return call(getIgnoreCase, this.#regExp, []);
     }
 
-    /** Gets whether the multiline flag is present on this Matcher. */
+    /**
+     * Gets whether the multiline flag is present on this `Matcher`.
+     */
     get multiline() {
       return call(getMultiline, this.#regExp, []);
     }
 
-    /** Gets the regular expression source for this Matcher. */
+    /** Gets the regular expression source for this `Matcher`. */
     get source() {
       return call(getSource, this.#regExp, []);
     }
 
-    /** Gets whether the sticky flag is present on this Matcher. */
+    /** Gets whether the sticky flag is present on this `Matcher`. */
     get sticky() {
       return call(getSticky, this.#regExp, []);
     }
 
     /**
-     * Gets whether the unicode flag is present on this Matcher.
+     * Gets whether the unicode flag is present on this `Matcher`.
      *
      * ※ This will always be true.
      */
@@ -264,7 +275,7 @@ export const {
   const {
     toLowerCase: stringToLowercase,
     toUpperCase: stringToUppercase,
-  } = String.prototype;
+  } = stringPrototype;
   return {
     asciiLowercase: ($) =>
       stringReplaceAll(
@@ -310,21 +321,17 @@ export const {
    */
   scalarValueString,
 } = (() => {
-  const {
-    iterator: iteratorSymbol,
-    toStringTag: toStringTagSymbol,
-  } = Symbol;
-  const { [iteratorSymbol]: arrayIterator } = Array.prototype;
+  const { [ITERATOR]: arrayIterator } = arrayPrototype;
   const arrayIteratorPrototype = Object.getPrototypeOf(
-    [][iteratorSymbol](),
+    [][ITERATOR](),
   );
   const { next: arrayIteratorNext } = arrayIteratorPrototype;
   const iteratorPrototype = Object.getPrototypeOf(
     arrayIteratorPrototype,
   );
-  const { [iteratorSymbol]: stringIterator } = String.prototype;
+  const { [ITERATOR]: stringIterator } = stringPrototype;
   const stringIteratorPrototype = Object.getPrototypeOf(
-    ""[iteratorSymbol](),
+    ""[ITERATOR](),
   );
   const { next: stringIteratorNext } = stringIteratorPrototype;
 
@@ -413,7 +420,7 @@ export const {
         value: stringCodeValueIteratorNext,
         writable: true,
       },
-      [toStringTagSymbol]: {
+      [TO_STRING_TAG]: {
         configurable: true,
         enumerable: false,
         value: "String Code Value Iterator",
@@ -422,7 +429,7 @@ export const {
     },
   );
   const scalarValueIterablePrototype = {
-    [iteratorSymbol]() {
+    [ITERATOR]() {
       return {
         next: bind(
           stringCodeValueIteratorNext,
@@ -460,16 +467,16 @@ export const {
 /**
  * Returns an iterator over the codepoints in the string representation
  * of the provided value according to the algorithm of
- * String::[Symbol.iterator].
+ * `String::[Symbol.iterator]`.
  */
 export const characters = makeCallable(
-  String.prototype[Symbol.iterator],
+  stringPrototype[ITERATOR],
 );
 
 /**
  * Returns the character at the provided position in the string
  * representation of the provided value according to the algorithm of
- * String::codePointAt.
+ * `String::codePointAt`.
  */
 export const getCharacter = ($, pos) => {
   const codepoint = getCodepoint($, pos);
@@ -481,33 +488,33 @@ export const getCharacter = ($, pos) => {
 /**
  * Returns the code unit at the provided position in the string
  * representation of the provided value according to the algorithm of
- * String::charAt.
+ * `String::charAt`.
  */
-export const getCodeUnit = makeCallable(String.prototype.charCodeAt);
+export const getCodeUnit = makeCallable(stringPrototype.charCodeAt);
 
 /**
  * Returns the codepoint at the provided position in the string
  * representation of the provided value according to the algorithm of
- * String::codePointAt.
+ * `String::codePointAt`.
  */
-export const getCodepoint = makeCallable(String.prototype.codePointAt);
+export const getCodepoint = makeCallable(stringPrototype.codePointAt);
 
 /**
  * Returns the index of the first occurrence of the search string in
  * the string representation of the provided value according to the
- * algorithm of String::indexOf.
+ * algorithm of `String::indexOf`.
  */
 export const getFirstSubstringIndex = makeCallable(
-  String.prototype.indexOf,
+  stringPrototype.indexOf,
 );
 
 /**
  * Returns the index of the last occurrence of the search string in the
  * string representation of the provided value according to the
- * algorithm of String::lastIndexOf.
+ * algorithm of `String::lastIndexOf`.
  */
 export const getLastSubstringIndex = makeCallable(
-  String.prototype.lastIndexOf,
+  stringPrototype.lastIndexOf,
 );
 
 /**
@@ -518,7 +525,7 @@ export const getLastSubstringIndex = makeCallable(
  * If a value is nullish, it will be stringified as the empty string.
  */
 export const join = (() => {
-  const { join: arrayJoin } = Array.prototype;
+  const { join: arrayJoin } = arrayPrototype;
   const join = ($, separator = ",") =>
     call(arrayJoin, [...$], [`${separator}`]);
   return join;
@@ -529,21 +536,21 @@ export const {
    * Returns a string created from the raw value of the tagged template
    * literal.
    *
-   * ※ This is an alias for String.raw.
+   * ※ This is an alias for `String.raw`.
    */
   raw: rawString,
 
   /**
    * Returns a string created from the provided code units.
    *
-   * ※ This is an alias for String.fromCharCode.
+   * ※ This is an alias for `String.fromCharCode`.
    */
   fromCharCode: stringFromCodeUnits,
 
   /**
    * Returns a string created from the provided codepoints.
    *
-   * ※ This is an alias for String.fromCodePoint.
+   * ※ This is an alias for `String.fromCodePoint`.
    */
   fromCodePoint: stringFromCodepoints,
 } = String;
@@ -574,110 +581,110 @@ export const splitOnCommas = ($) =>
 /**
  * Returns the result of catenating the string representations of the
  * provided values, returning a new string according to the algorithm
- * of String::concat.
+ * of `String::concat`.
  */
-export const stringCatenate = makeCallable(String.prototype.concat);
+export const stringCatenate = makeCallable(stringPrototype.concat);
 
 /**
  * Returns whether the string representation of the provided value ends
  * with the provided search string according to the algorithm of
- * String::endsWith.
+ * `String::endsWith`.
  */
-export const stringEndsWith = makeCallable(String.prototype.endsWith);
+export const stringEndsWith = makeCallable(stringPrototype.endsWith);
 
 /**
  * Returns whether the string representation of the provided value
  * contains the provided search string according to the algorithm of
- * String::includes.
+ * `String::includes`.
  */
-export const stringIncludes = makeCallable(String.prototype.includes);
+export const stringIncludes = makeCallable(stringPrototype.includes);
 
 /**
  * Returns the result of matching the string representation of the
  * provided value with the provided matcher according to the algorithm
- * of String::match.
+ * of `String::match`.
  */
-export const stringMatch = makeCallable(String.prototype.match);
+export const stringMatch = makeCallable(stringPrototype.match);
 
 /**
  * Returns the result of matching the string representation of the
  * provided value with the provided matcher according to the algorithm
- * of String::matchAll.
+ * of `String::matchAll`.
  */
-export const stringMatchAll = makeCallable(String.prototype.matchAll);
+export const stringMatchAll = makeCallable(stringPrototype.matchAll);
 
 /**
  * Returns the normalized form of the string representation of the
- * provided value according to the algorithm of String::matchAll.
+ * provided value according to the algorithm of `String::matchAll`.
  */
 export const stringNormalize = makeCallable(
-  String.prototype.normalize,
+  stringPrototype.normalize,
 );
 
 /**
  * Returns the result of padding the end of the string representation
  * of the provided value padded until it is the desired length
- * according to the algorithm of String::padEnd.
+ * according to the algorithm of `String::padEnd`.
  */
-export const stringPadEnd = makeCallable(String.prototype.padEnd);
+export const stringPadEnd = makeCallable(stringPrototype.padEnd);
 
 /**
  * Returns the result of padding the start of the string representation
  * of the provided value padded until it is the desired length
- * according to the algorithm of String::padStart.
+ * according to the algorithm of `String::padStart`.
  */
-export const stringPadStart = makeCallable(String.prototype.padStart);
+export const stringPadStart = makeCallable(stringPrototype.padStart);
 
 /**
  * Returns the result of repeating the string representation of the
  * provided value the provided number of times according to the
- * algorithm of String::repeat.
+ * algorithm of `String::repeat`.
  */
-export const stringRepeat = makeCallable(String.prototype.repeat);
+export const stringRepeat = makeCallable(stringPrototype.repeat);
 
 /**
  * Returns the result of replacing the string representation of the
  * provided value with the provided replacement, using the provided
- * matcher and according to the algorithm of String::replace.
+ * matcher and according to the algorithm of `String::replace`.
  */
-export const stringReplace = makeCallable(String.prototype.replace);
+export const stringReplace = makeCallable(stringPrototype.replace);
 
 /**
  * Returns the result of replacing the string representation of the
  * provided value with the provided replacement, using the provided
- * matcher and according to the algorithm of String::replaceAll.
+ * matcher and according to the algorithm of `String::replaceAll`.
  */
 export const stringReplaceAll = makeCallable(
-  String.prototype.replaceAll,
+  stringPrototype.replaceAll,
 );
 
 /**
  * Returns the result of searching the string representation of the
  * provided value using the provided matcher and according to the
- * algorithm of String::search.
+ * algorithm of `String::search`.
  */
-export const stringSearch = makeCallable(String.prototype.search);
+export const stringSearch = makeCallable(stringPrototype.search);
 
 /**
  * Returns a slice of the string representation of the provided value
- * according to the algorithm of String::slice.
+ * according to the algorithm of `String::slice`.
  */
-export const stringSlice = makeCallable(String.prototype.slice);
+export const stringSlice = makeCallable(stringPrototype.slice);
 
 /**
  * Returns the result of splitting of the string representation of the
  * provided value on the provided separator according to the algorithm
- * of String::split.
+ * of `String::split`.
  */
-export const stringSplit = makeCallable(String.prototype.split);
+export const stringSplit = makeCallable(stringPrototype.split);
 
 /**
  * Returns whether the string representation of the provided value
  * starts with the provided search string according to the algorithm of
- * String::startsWith.
+ * `String::startsWith`.
  */
 export const stringStartsWith = makeCallable(
-  String.prototype.startsWith,
+  stringPrototype.startsWith,
 );
 
 /**
@@ -686,7 +693,7 @@ export const stringStartsWith = makeCallable(
  * ☡ This function will throw if the provided object does not have a
  * `[[StringData]]` internal slot.
  */
-export const stringValue = makeCallable(String.prototype.valueOf);
+export const stringValue = makeCallable(stringPrototype.valueOf);
 
 /**
  * Returns the result of stripping leading and trailing A·S·C·I·I
@@ -706,17 +713,14 @@ export const stripAndCollapseASCIIWhitespace = ($) =>
  * Returns the result of stripping leading and trailing A·S·C·I·I
  * whitespace from the string representation of the provided value.
  */
-export const stripLeadingAndTrailingASCIIWhitespace = (() => {
-  const { exec: reExec } = RegExp.prototype;
-  return ($) =>
-    call(reExec, /^[\n\r\t\f ]*([^]*?)[\n\r\t\f ]*$/u, [$])[1];
-})();
+export const stripLeadingAndTrailingASCIIWhitespace = ($) =>
+  call(reExec, /^[\n\r\t\f ]*([^]*?)[\n\r\t\f ]*$/u, [$])[1];
 
 /**
  * Returns a substring of the string representation of the provided
- * value according to the algorithm of String::substring.
+ * value according to the algorithm of `String::substring`.
  */
-export const substring = makeCallable(String.prototype.substring);
+export const substring = makeCallable(stringPrototype.substring);
 
 /**
  * Returns the result of converting the provided value to a string.
index 95c715e3636b5bee2b1fef6e775a2ccfcf5a3549..e3d10968b0bb3bd401a2e564846a32b9314d4b60 100644 (file)
@@ -10,7 +10,6 @@
 import {
   assert,
   assertEquals,
-  assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
   assertThrows,
@@ -101,6 +100,7 @@ describe("Matcher", () => {
           return "etaoin";
         },
       });
+      assertEquals([...result], ["etaoin", "e"]);
       assertSpyCalls(constraint, 1);
       assertStrictEquals(constraint.calls[0].args[0], "etaoin");
       assertEquals([...constraint.calls[0].args[1]], ["etaoin", "e"]);
index 77cb6b40448f5ca4ae7d08cf001625242c1e09a3..3f0c6fa1b9483d2e45358f9907bbfd66452e1d13 100644 (file)
--- a/value.js
+++ b/value.js
@@ -1,13 +1,49 @@
 // ♓🌟 Piscēs ∷ value.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022‐2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
-import { call } from "./function.js";
+export const {
+  /** The welknown `@@asyncIterator` symbol. */
+  asyncIterator: ASYNC_ITERATOR,
+
+  /** The welknown `@@hasInstance` symbol. */
+  hasInstance: HAS_INSTANCE,
+
+  /** The welknown `@@isConcatSpreadable` symbol. */
+  isConcatSpreadable: IS_CONCAT_SPREADABLE,
+
+  /** The welknown `@@iterator` symbol. */
+  iterator: ITERATOR,
+
+  /** The welknown `@@match` symbol. */
+  match: MATCH,
+
+  /** The welknown `@@matchAll` symbol. */
+  matchAll: MATCH_ALL,
+
+  /** The welknown `@@replace` symbol. */
+  replace: REPLACE,
+
+  /** The welknown `@@species` symbol. */
+  species: SPECIES,
+
+  /** The welknown `@@split` symbol. */
+  split: SPLIT,
+
+  /** The welknown `@@toPrimitive` symbol. */
+  toPrimitive: TO_PRIMITIVE,
+
+  /** The welknown `@@toStringTag` symbol. */
+  toStringTag: TO_STRING_TAG,
+
+  /** The welknown `@@unscopables` symbol. */
+  unscopables: UNSCOPABLES,
+} = Symbol;
 
 /** The null primitive. */
 export const NULL = null;
@@ -18,10 +54,10 @@ export const UNDEFINED = undefined;
 export const {
   /**
    * Returns the primitive value of the provided object per its
-   * `toString` and `valueOf` methods.
+   * `.toString` and `.valueOf` methods.
    *
-   * If the provided hint is "string", then `toString` takes
-   * precedence; otherwise, `valueOf` does.
+   * If the provided hint is "string", then `.toString` takes
+   * precedence; otherwise, `.valueOf` does.
    *
    * Throws an error if both of these methods are not callable or do
    * not return a primitive.
@@ -34,13 +70,13 @@ export const {
    *
    * The provided preferred type, if specified, should be "string",
    * "number", or "default". If the provided input has a
-   * `[Symbol.toPrimitive]` method, this function will throw rather
+   * `.[Symbol.toPrimitive]` method, this function will throw rather
    * than calling that method with a preferred type other than one of
    * the above.
    */
   toPrimitive,
 } = (() => {
-  const { toPrimitive: toPrimitiveSymbol } = Symbol;
+  const { apply: call } = Reflect;
 
   return {
     ordinaryToPrimitive: (O, hint) => {
@@ -80,14 +116,14 @@ export const {
         );
       } else if (type($) === "object") {
         // The provided value is an object.
-        const exoticToPrim = $[toPrimitiveSymbol] ?? undefined;
+        const exoticToPrim = $[TO_PRIMITIVE] ?? undefined;
         if (exoticToPrim !== undefined) {
           // The provided value has an exotic primitive conversion
           // method.
           if (typeof exoticToPrim !== "function") {
             // The method is not callable.
             throw new TypeError(
-              "Piscēs: `[Symbol.toPrimitive]` was neither nullish nor callable.",
+              "Piscēs: `.[Symbol.toPrimitive]` was neither nullish nor callable.",
             );
           } else {
             // The method is callable.
index 6729b85cbc43eacf93f18b43ffe98f44d2d9690e..df8677801934ecb7a09b878e36fe3fe6c2a155be 100644 (file)
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ value.test.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022–2023 Lady [@ Lady’s Computer].
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,27 +15,114 @@ import {
   it,
 } from "./dev-deps.js";
 import {
+  ASYNC_ITERATOR,
+  HAS_INSTANCE,
+  IS_CONCAT_SPREADABLE,
+  ITERATOR,
+  MATCH,
+  MATCH_ALL,
   NULL,
   ordinaryToPrimitive,
+  REPLACE,
   sameValue,
   sameValueZero,
+  SPECIES,
+  SPLIT,
+  TO_PRIMITIVE,
+  TO_STRING_TAG,
   toPrimitive,
   type,
   UNDEFINED,
+  UNSCOPABLES,
 } from "./value.js";
 
+describe("ASYNC_ITERATOR", () => {
+  it("[[Get]] is @@asyncIterator", () => {
+    assertStrictEquals(ASYNC_ITERATOR, Symbol.asyncIterator);
+  });
+});
+
+describe("HAS_INSTANCE", () => {
+  it("[[Get]] is @@hasInstance", () => {
+    assertStrictEquals(HAS_INSTANCE, Symbol.hasInstance);
+  });
+});
+
+describe("IS_CONCAT_SPREADABLE", () => {
+  it("[[Get]] is @@isConcatSpreadable", () => {
+    assertStrictEquals(
+      IS_CONCAT_SPREADABLE,
+      Symbol.isConcatSpreadable,
+    );
+  });
+});
+
+describe("ITERATOR", () => {
+  it("[[Get]] is @@iterator", () => {
+    assertStrictEquals(ITERATOR, Symbol.iterator);
+  });
+});
+
+describe("MATCH", () => {
+  it("[[Get]] is @@match", () => {
+    assertStrictEquals(MATCH, Symbol.match);
+  });
+});
+
+describe("MATCH_ALL", () => {
+  it("[[Get]] is @@matchAll", () => {
+    assertStrictEquals(MATCH_ALL, Symbol.matchAll);
+  });
+});
+
 describe("NULL", () => {
   it("[[Get]] is null", () => {
     assertStrictEquals(NULL, null);
   });
 });
 
+describe("REPLACE", () => {
+  it("[[Get]] is @@replace", () => {
+    assertStrictEquals(REPLACE, Symbol.replace);
+  });
+});
+
+describe("SPECIES", () => {
+  it("[[Get]] is @@species", () => {
+    assertStrictEquals(SPECIES, Symbol.species);
+  });
+});
+
+describe("SPLIT", () => {
+  it("[[Get]] is @@split", () => {
+    assertStrictEquals(SPLIT, Symbol.split);
+  });
+});
+
+describe("TO_PRIMITIVE", () => {
+  it("[[Get]] is @@toPrimitive", () => {
+    assertStrictEquals(TO_PRIMITIVE, Symbol.toPrimitive);
+  });
+});
+
+describe("TO_STRING_TAG", () => {
+  it("[[Get]] is @@toStringTag", () => {
+    assertStrictEquals(TO_STRING_TAG, Symbol.toStringTag);
+  });
+});
+
 describe("UNDEFINED", () => {
   it("[[Get]] is undefined", () => {
     assertStrictEquals(UNDEFINED, void {});
   });
 });
 
+describe("UNSCOPABLES", () => {
+  it("[[Get]] is @@unscopables", () => {
+    assertStrictEquals(UNSCOPABLES, Symbol.unscopables);
+  });
+});
+
 describe("ordinaryToPrimitive", () => {
   it("[[Call]] prefers `valueOf` by default", () => {
     const obj = {
This page took 0.16355 seconds and 4 git commands to generate.