]> Lady’s Gitweb - Etiquette/blobdiff - memory.js
Always return (the same) objects for langstrings
[Etiquette] / memory.js
index 6773bddfc91b3ee533f7cb5bef46fd864a753534..252418b3f85e4e5c2493b8263399e0acb900267f 100644 (file)
--- a/memory.js
+++ b/memory.js
@@ -29,27 +29,26 @@ const constructorSymbol = Symbol("constructor");
  * ※ This function is not exposed.
  */
 const mintID = ($ = null) => {
-  const [number, buffer] = $ == null
-    ? (() => {
+  const { number, buffer } = (() => {
+    if ($ == null) {
       // No value was provided; generate a random one and store its
       // buffer.
       const values = crypto.getRandomValues(new Uint32Array(1));
-      const view = new DataView(values.buffer);
-      return [
-        view.getUint32(0) >>> 2, // drop the final 2 bits
-        view.buffer,
-      ];
-    })()
-    : [$ >>> 0 & -1 >>> 2, null];
+      const { buffer } = values;
+      return {
+        number: new DataView(buffer).getUint32(0) >>> 2,
+        buffer,
+      };
+    } else {
+      // A value was provided, so a buffer needs to be generated.
+      const number = $ & -1 >>> 2;
+      const buffer = new ArrayBuffer(4);
+      new DataView(buffer).setUint32(0, number << 2, false);
+      return { number, buffer };
+    }
+  })();
   const checksum = number % 37;
-  const wrmg = wrmgBase32String(
-    buffer ?? (() => {
-      // A value was provided, so a buffer still needs to be generated.
-      const view = new DataView(new ArrayBuffer(4));
-      view.setUint32(0, number << 2, false);
-      return view.buffer;
-    })(),
-  );
+  const wrmg = wrmgBase32String(buffer);
   return Object.assign(
     new String(
       `${"0123456789ABCDEFGHJKMNPQRSTVWXYZ*~$=U"[checksum]}${
@@ -61,9 +60,12 @@ const mintID = ($ = null) => {
 };
 
 /**
- * Validates the checksum prefixing the provided W·R·M·G base32
+ * Validates the checksum prefixing the provided 30‐bit W·R·M·G base32
  * identifier and then returns that same identifier in normalized form.
  *
+ * ☡ This function will throw if the provided value is not a 30‐bit
+ * W·R·M·G base32 value with a leading checksum.
+ *
  * ※ This function is not exposed.
  */
 const normalizeID = ($) => {
@@ -72,14 +74,14 @@ const normalizeID = ($) => {
     identifier[0].toUpperCase(),
   );
   if (checksum == -1) {
-    // The checksum character is invalid.
+    // ☡ The checksum character is invalid.
     throw new RangeError(`Invalid checksum: "${identifier[0]}".`);
   } else {
     // There is a valid checksum.
     const binary = wrmgBase32Binary`${identifier.substring(1)}0`;
     const { byteLength } = binary;
     if (byteLength != 4) {
-      // The identifier was of unexpected size.
+      // ☡ The identifier was of unexpected size.
       throw new RangeError(
         `Expected id to fit within 4 bytes, but got ${byteLength}.`,
       );
@@ -87,17 +89,17 @@ const normalizeID = ($) => {
       // The identifier was correctly‐sized.
       const value = new DataView(binary).getUint32(0, false);
       if (value & 0b11) {
-        // The final two bits, which should have been padded with
+        // ☡ The final two bits, which should have been padded with
         // zeroes, have a nonzero value.
         //
-        // This should be impossible and indicates something went very
-        // wrong in base32 decoding.
+        // ※ This should be impossible and indicates something went
+        // very wrong in base32 decoding.
         throw new RangeError("Unexpected values in lower two bits");
       } else {
         // The final two bits are zero as expected.
         const number = value >>> 2;
         if (checksum != number % 37) {
-          // The checksum does not match the number.
+          // ☡ The checksum does not match the number.
           throw new RangeError(
             `Invalid checksum for id: ${identifier} (${number})`,
           );
@@ -156,8 +158,11 @@ export class Storage {
   /**
    * A `Set` of deleted identifiers, to ensure they are not
    * re·assigned.
+   *
+   * The identifier `000-0000` is deleted from the start and can only
+   * be manually set.
    */
-  #deleted = new Set();
+  #deleted = new Set([`${mintID(0)}`]);
 
   /** The `Map` used to actually store the data internally. */
   #store = new Map();
@@ -172,7 +177,7 @@ export class Storage {
       ...object
     } = data;
     if (!(toInstanceSymbol in constructor)) {
-      // There is no method on the constructor for generating an
+      // ☡ There is no method on the constructor for generating an
       // instance.
       throw new TypeError(
         "Constructor must implement Storage.toInstance for object to be retrieved.",
@@ -217,12 +222,18 @@ export class Storage {
           ) {
             // Generate successive identifiers until an available one
             // is reached.
+            //
+            // ※ Successive identifiers are used in order to guarantee
+            // an eventual result; continuing to generate random
+            // identifiers could go on forever.
             id = mintID(id.value + 1);
             literalValue = `${id}`;
           }
           return literalValue;
         } else {
-          // There are no identifiers left to assign.
+          // ☡ There are no identifiers left to assign.
+          //
+          // ※ This is unlikely to ever be possible in practice.
           throw new TypeError("Out of room.");
         }
       })();
@@ -259,9 +270,9 @@ export class Storage {
    */
   delete(id) {
     const store = this.#store;
-    const validID = normalizeID(id);
-    this.#deleted.add(validID);
-    return store.delete(validID);
+    const normalized = normalizeID(id);
+    this.#deleted.add(normalized);
+    return store.delete(normalized);
   }
 
   /** Yields successive identifier~instance pairs from storage. */
@@ -297,15 +308,15 @@ export class Storage {
    */
   get(id) {
     const store = this.#store;
-    const validID = normalizeID(id);
-    const data = store.get(validID);
+    const normalized = normalizeID(id);
+    const data = store.get(normalized);
     if (data == null) {
       // No object was at the provided identifier.
       return null;
     } else {
       // The provided identifier had a stored object; return the
       // constructed instance.
-      return this.#construct(data, validID);
+      return this.#construct(data, normalized);
     }
   }
 
This page took 0.028008 seconds and 4 git commands to generate.