}
};
+/**
+ * Returns the result of decoding the provided base32 string into an
+ * ArrayBuffer.
+ *
+ * If the second argument is truthy, uses Crockford’s encoding rather
+ * than the RFC’s (see <https://www.crockford.com/base32.html>). This
+ * is more human‐friendly and tolerant. Check digits are not supported.
+ *
+ * ※ This function is not exposed.
+ */
+const decodeBase32 = (source, wrmg) => {
+ const u5s = map(
+ wrmg
+ ? stringReplace(source, /-/gu, "")
+ : source.length % 8 == 0
+ ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
+ : source,
+ (ucsCharacter) => {
+ const code = getCodeUnit(ucsCharacter, 0);
+ const result = wrmg
+ ? code >= 0x30 && code <= 0x39
+ ? code - 48
+ : code >= 0x41 && code <= 0x48
+ ? code - 55
+ : code == 0x49
+ ? 1 // I
+ : code >= 0x4A && code <= 0x4B
+ ? code - 56
+ : code == 0x4C
+ ? 1 // L
+ : code >= 0x4D && code <= 0x4E
+ ? code - 57
+ : code == 0x4F
+ ? 0 // O
+ : code >= 0x50 && code <= 0x54
+ ? code - 58
+ // U is skipped
+ : code >= 0x56 && code <= 0x5A
+ ? code - 59
+ : code >= 0x61 && code <= 0x68
+ ? code - 87
+ : code == 0x69
+ ? 1 // i
+ : code >= 0x6A && code <= 0x6B
+ ? code - 88
+ : code == 0x6C
+ ? 1 // l
+ : code >= 0x6D && code <= 0x6E
+ ? code - 89
+ : code == 0x6F
+ ? 0 // o
+ : code >= 0x70 && code <= 0x74
+ ? code - 90
+ // u is skipped
+ : code >= 0x76 && code <= 0x7A
+ ? code - 91
+ : -1
+ : code >= 0x41 && code <= 0x5A
+ ? code - 65
+ : code >= 0x61 && code <= 0x7A
+ ? code - 97 // same result as above; case insensitive
+ : code >= 0x32 && code <= 0x37
+ ? code - 24 // digits 2–7 map to 26–31
+ : -1;
+ if (result < 0) {
+ throw new RangeError(
+ `Piscēs: Invalid character in Base32: ${ucsCharacter}.`,
+ );
+ } else {
+ return result;
+ }
+ },
+ );
+ const { length } = u5s;
+ if (length % 8 == 1) {
+ throw new RangeError(
+ `Piscēs: Base32 string has invalid length: ${source}.`,
+ );
+ } else {
+ const dataView = new View(new Buffer(floor(length * 5 / 8)));
+ for (let index = 0; index < length - 1;) {
+ // 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++],
+ ]);
+ }
+ }
+ return call(getViewBuffer, dataView, []);
+ }
+};
+
/**
* Returns the result of decoding the provided base64 string into an
* ArrayBuffer.
return stringFromCodeUnits(...resultingCodeUnits);
};
+/**
+ * Returns the result of encoding the provided ArrayBuffer into a
+ * base32 string.
+ *
+ * ※ This function is not exposed.
+ */
+const encodeBase32 = (buffer, wrmg = false) => {
+ const dataView = new View(buffer);
+ const byteLength = call(getBufferByteLength, buffer, []);
+ const minimumLengthOfResults = ceil(byteLength * 8 / 5);
+ const fillByte = wrmg ? 0x2D : 0x3D;
+ const resultingCodeUnits = fill(
+ objectCreate(
+ binaryCodeUnitIterablePrototype,
+ {
+ length: {
+ value: minimumLengthOfResults +
+ (8 - (minimumLengthOfResults % 8)) % 8,
+ },
+ },
+ ),
+ fillByte,
+ );
+ for (let index = 0; index < byteLength;) {
+ const codeUnitIndex = ceil(index * 8 / 5);
+ const currentIndex = codeUnitIndex + +(
+ 0b01011 & 1 << index % 5 &&
+ resultingCodeUnits[codeUnitIndex] != fillByte
+ ); // bytes 0, 1 & 3 handle two letters; this is for the second
+ const remainder = currentIndex % 8;
+ const currentByte = call(viewGetUint8, dataView, [index]);
+ const nextByte =
+ 0b01011010 & 1 << remainder && ++index < byteLength
+ // digits 1, 3, 4 & 6 span multiple bytes
+ ? call(viewGetUint8, dataView, [index])
+ : 0;
+ const u5 = remainder == 0
+ ? currentByte >> 3
+ : remainder == 1
+ ? (currentByte & 0b00000111) << 2 | nextByte >> 6
+ : remainder == 2
+ ? (currentByte & 0b00111111) >> 1
+ : remainder == 3
+ ? (currentByte & 0b00000001) << 4 | nextByte >> 4
+ : remainder == 4
+ ? (currentByte & 0b00001111) << 1 | nextByte >> 7
+ : remainder == 5
+ ? (currentByte & 0b01111111) >> 2
+ : remainder == 6
+ ? (currentByte & 0b00000011) << 3 | nextByte >> 5
+ : (++index, currentByte & 0b00011111); // remainder == 7
+ const result = wrmg
+ ? u5 < 10 ? u5 + 48 : u5 < 18
+ ? u5 + 55
+ // skip I
+ : u5 < 20
+ ? u5 + 56
+ // skip L
+ : u5 < 22
+ ? u5 + 57
+ // skip O
+ : u5 < 27
+ ? u5 + 58
+ // skip U
+ : u5 < 32
+ ? u5 + 59
+ : -1
+ : u5 < 26
+ ? u5 + 65
+ : u5 < 32
+ ? u5 + 24
+ : -1;
+ if (result < 0) {
+ throw new RangeError(`Piscēs: Unexpected Base32 value: ${u5}.`);
+ } else {
+ resultingCodeUnits[currentIndex] = result;
+ }
+ }
+ const answer = stringFromCodeUnits(...resultingCodeUnits);
+ return wrmg ? answer.replace(/-+$/u, "") : answer;
+};
+
/**
* Returns the result of encoding the provided ArrayBuffer into a
* base64 string.
export const base16String = ($, ...$s) =>
encodeBase16(bufferFromArgs($, $s));
+/**
+ * Returns an ArrayBuffer generated from the provided base32 string.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ *
+ * ☡ This function throws if the provided string is not a valid base32
+ * string.
+ */
+export const base32Binary = ($, ...$s) =>
+ decodeBase32(sourceFromArgs($, $s));
+
+/**
+ * Returns a (big‐endian) base32 string created from the provided typed
+ * array, buffer, or (16‐bit) string.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ */
+export const base32String = ($, ...$s) =>
+ encodeBase32(bufferFromArgs($, $s));
+
/**
* Returns an ArrayBuffer generated from the provided base64 string.
*
}
};
+/**
+ * Returns whether the provided value is a base32 string.
+ *
+ * ※ This function returns false if the provided value is not a string
+ * primitive.
+ */
+export const isBase32 = ($) => {
+ if (typeof $ !== "string") {
+ return false;
+ } else {
+ const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
+ 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;
+ }
+};
+
/**
* Returns whether the provided value is a Base64 string.
*
call(reExec, /[^0-9A-Za-z_-]/u, [trimmed]) == null;
}
};
+
+/**
+ * Returns whether the provided value is a W·R·M·G (Crockford) base32
+ * string. Check digits are not supported.
+ *
+ * ※ This function returns false if the provided value is not a string
+ * primitive.
+ */
+export const isWRMGBase32 = ($) => {
+ if (typeof $ !== "string") {
+ return false;
+ } else {
+ 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;
+ }
+};
+
+/**
+ * Returns an ArrayBuffer generated from the provided W·R·M·G
+ * (Crockford) base32 string.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ *
+ * ☡ This function throws if the provided string is not a valid W·R·M·G
+ * base32 string.
+ */
+export const wrmgBase32Binary = ($, ...$s) =>
+ decodeBase32(sourceFromArgs($, $s), true);
+
+/**
+ * Returns a (big‐endian) W·R·M·G (Crockford) base32 string created
+ * from the provided typed array, buffer, or (16‐bit) string.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ */
+export const wrmgBase32String = ($, ...$s) =>
+ encodeBase32(bufferFromArgs($, $s), true);
import {
base16Binary,
base16String,
+ base32Binary,
+ base32String,
base64Binary,
base64String,
filenameSafeBase64Binary,
filenameSafeBase64String,
isBase16,
+ isBase32,
isBase64,
isFilenameSafeBase64,
+ isWRMGBase32,
+ wrmgBase32Binary,
+ wrmgBase32String,
} from "./binary.js";
// These tests assume a LITTLE‐ENDIAN environment.
const data = new Map([
["", {
base16: "",
+ base32: "",
base64: "",
+ wrmg: "",
}],
["base64", {
base16: "006200610073006500360034",
+ base32: "ABRAAYIAOMAGKABWAA2A====",
base64: "AGIAYQBzAGUANgA0",
+ wrmg: "01H00R80EC06A01P00T0",
}],
[new Uint16Array([62535]), {
base16: "47F4",
+ base32: "I72A====",
base64: "R/Q=",
+ wrmg: "8ZT0",
}],
[new Uint8ClampedArray([75, 73, 66, 73]).buffer, {
base16: "4B494249",
+ base32: "JNEUESI=",
base64: "S0lCSQ==",
+ wrmg: "9D4M4J8",
}],
[new DataView(new Uint8Array([98, 97, 115, 101, 54, 52]).buffer), {
base16: "626173653634",
+ base32: "MJQXGZJWGQ======",
base64: "YmFzZTY0",
+ wrmg: "C9GQ6S9P6G",
}],
// The following three examples are from RFC 3548.
[new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9, 0x7E]), {
base16: "14FB9C03D97E",
+ base32: "CT5ZYA6ZPY======",
base64: "FPucA9l+",
+ wrmg: "2KXSR0YSFR",
}],
[new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9]), {
base16: "14FB9C03D9",
+ base32: "CT5ZYA6Z",
base64: "FPucA9k=",
+ wrmg: "2KXSR0YS",
}],
[new Uint8Array([0x14, 0xFB, 0x9C, 0x03]), {
base16: "14FB9C03",
+ base32: "CT5ZYAY=",
base64: "FPucAw==",
+ wrmg: "2KXSR0R",
}],
// The following examples are from the Ruby base32 gem.
[new Uint8Array([0x28]), {
base16: "28",
+ base32: "FA======",
base64: "KA==",
+ wrmg: "50",
}],
[new Uint8Array([0xD6]), {
base16: "D6",
+ base32: "2Y======",
base64: "1g==",
+ wrmg: "TR",
}],
[new Uint16Array([0xF8D6]), {
base16: "D6F8",
+ base32: "234A====",
base64: "1vg=",
+ wrmg: "TVW0",
}],
[new Uint8Array([0xD6, 0xF8, 0x00]), {
base16: "D6F800",
+ base32: "234AA===",
base64: "1vgA",
+ wrmg: "TVW00",
}],
[new Uint8Array([0xD6, 0xF8, 0x10]), {
base16: "D6F810",
+ base32: "234BA===",
base64: "1vgQ",
+ wrmg: "TVW10",
}],
[new Uint32Array([0x0C11F8D6]), {
base16: "D6F8110C",
+ base32: "234BCDA=",
base64: "1vgRDA==",
+ wrmg: "TVW1230",
}],
[new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]), {
base16: "D6F8110C80",
+ base32: "234BCDEA",
base64: "1vgRDIA=",
+ wrmg: "TVW12340",
}],
[new Uint16Array([0xF8D6, 0x0C11, 0x3085]), {
base16: "D6F8110C8530",
+ base32: "234BCDEFGA======",
base64: "1vgRDIUw",
+ wrmg: "TVW1234560",
}],
]);
});
});
+describe("base32Binary", () => {
+ it("[[Call]] returns the correct data", () => {
+ assertEquals(
+ new Uint8Array(base32Binary("")),
+ new Uint8Array([]),
+ "<empty>",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("ABRAAYIAOMAGKABWAA2A====")),
+ Uint8Array.from(
+ "\u{0}b\u{0}a\u{0}s\u{0}e\u{0}6\u{0}4",
+ ($) => $.charCodeAt(0),
+ ),
+ "ABRAAYIAOMAGKABWAA2A====",
+ );
+ assertEquals(
+ new Uint16Array(base32Binary("I72A====")),
+ new Uint16Array([62535]),
+ "I72A====",
+ );
+ assertEquals(
+ new Uint8ClampedArray(base32Binary("JNEUESI=")),
+ new Uint8ClampedArray([75, 73, 66, 73]),
+ "JNEUESI=",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("MJQXGZJWGQ======")),
+ new Uint8Array([98, 97, 115, 101, 54, 52]),
+ "MJQXGZJWGQ======",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("CT5ZYA6ZPY======")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9, 0x7E]),
+ "CT5ZYA6ZPY======",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("CT5ZYA6Z")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9]),
+ "CT5ZYA6Z",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("CT5ZYAY=")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03]),
+ "CT5ZYAY=",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("FA======")),
+ new Uint8Array([0x28]),
+ "FA======",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("2Y======")),
+ new Uint8Array([0xD6]),
+ "2Y======",
+ );
+ assertEquals(
+ new Uint16Array(base32Binary("234A====")),
+ new Uint16Array([0xF8D6]),
+ "234A====",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("234AA===")),
+ new Uint8Array([0xD6, 0xF8, 0x00]),
+ "234AA===",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("234BA===")),
+ new Uint8Array([0xD6, 0xF8, 0x10]),
+ "234BA===",
+ );
+ assertEquals(
+ new Uint32Array(base32Binary("234BCDA=")),
+ new Uint32Array([0x0C11F8D6]),
+ "234BCDA=",
+ );
+ assertEquals(
+ new Uint8Array(base32Binary("234BCDEA")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "234BCDEA",
+ );
+ assertEquals(
+ new Uint16Array(base32Binary("234BCDEFGA======")),
+ new Uint16Array([0xF8D6, 0x0C11, 0x3085]),
+ "234BCDEFGA======",
+ );
+ });
+
+ it("[[Call]] is case‐insensitive", () => {
+ assertEquals(
+ new Uint8Array(base32Binary("234bcdEA")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "234bcdEA",
+ );
+ });
+
+ it("[[Call]] throws when provided with an invalid character", () => {
+ assertThrows(() => base32Binary("ABC0"));
+ assertThrows(() => base32Binary("ABC1"));
+ assertThrows(() => base32Binary("ABC8"));
+ assertThrows(() => base32Binary("ABC9"));
+ });
+
+ it("[[Call]] throws when provided with an invalid equals", () => {
+ assertThrows(() => base32Binary("CT3ZYAY=="));
+ });
+
+ it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => {
+ assertThrows(() => base32Binary("234BCDEAA"));
+ assertThrows(() => base32Binary("234BCDEAA======="));
+ });
+});
+
+describe("base32String", () => {
+ it("[[Call]] returns the correct string", () => {
+ for (const [source, { base32 }] of data) {
+ assertStrictEquals(base32String(source), base32);
+ }
+ });
+});
+
describe("base64Binary", () => {
it("[[Call]] returns the correct data", () => {
assertEquals(
});
});
+describe("isBase32", () => {
+ it("[[Call]] returns true for base32 strings", () => {
+ for (const { base32 } of data.values()) {
+ assert(isBase32(base32));
+ assert(isBase32(base32.toLowerCase()));
+ }
+ });
+
+ it("[[Call]] returns false for others", () => {
+ [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 27,
+ 98n,
+ {},
+ [],
+ () => {},
+ new Proxy({}, {}),
+ "ABC1",
+ "A=======",
+ "ABCDEFGHI",
+ ].forEach((value) => assert(!isBase32(value)));
+ });
+});
+
describe("isBase64", () => {
it("[[Call]] returns true for base64 strings", () => {
for (const { base64 } of data.values()) {
].forEach((value) => assert(!isFilenameSafeBase64(value)));
});
});
+
+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
+ ? `${$}-`
+ : `-${$}-`;
+ })));
+ }
+ });
+
+ it("[[Call]] returns false for others", () => {
+ [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 27,
+ 98n,
+ {},
+ [],
+ () => {},
+ new Proxy({}, {}),
+ "ABCU",
+ "A",
+ "ABCDEFGH1",
+ ].forEach((value) => assert(!isWRMGBase32(value)));
+ });
+});
+
+describe("wrmgBase32Binary", () => {
+ it("[[Call]] returns the correct data", () => {
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("")),
+ new Uint8Array([]),
+ "<empty>",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("01H00R80EC06A01P00T0")),
+ Uint8Array.from(
+ "\u{0}b\u{0}a\u{0}s\u{0}e\u{0}6\u{0}4",
+ ($) => $.charCodeAt(0),
+ ),
+ "01H00R80EC06A01P00T0",
+ );
+ assertEquals(
+ new Uint16Array(wrmgBase32Binary("8ZT0")),
+ new Uint16Array([62535]),
+ "8ZT0",
+ );
+ assertEquals(
+ new Uint8ClampedArray(wrmgBase32Binary("9D4M4J8")),
+ new Uint8ClampedArray([75, 73, 66, 73]),
+ "9D4M4J8",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("C9GQ6S9P6G")),
+ new Uint8Array([98, 97, 115, 101, 54, 52]),
+ "C9GQ6S9P6G",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("2KXSR0YSFR")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9, 0x7E]),
+ "2KXSR0YSFR",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("2KXSR0YS")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03, 0xD9]),
+ "2KXSR0YS",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("2KXSR0R")),
+ new Uint8Array([0x14, 0xFB, 0x9C, 0x03]),
+ "2KXSR0R",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("50")),
+ new Uint8Array([0x28]),
+ "50",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TR")),
+ new Uint8Array([0xD6]),
+ "TR",
+ );
+ assertEquals(
+ new Uint16Array(wrmgBase32Binary("TVW0")),
+ new Uint16Array([0xF8D6]),
+ "TVW0",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVW00")),
+ new Uint8Array([0xD6, 0xF8, 0x00]),
+ "TVW00",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVW10")),
+ new Uint8Array([0xD6, 0xF8, 0x10]),
+ "TVW10",
+ );
+ assertEquals(
+ new Uint32Array(wrmgBase32Binary("TVW1230")),
+ new Uint32Array([0x0C11F8D6]),
+ "TVW1230",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVW12340")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "TVW12340",
+ );
+ assertEquals(
+ new Uint16Array(wrmgBase32Binary("TVW1234560")),
+ new Uint16Array([0xF8D6, 0x0C11, 0x3085]),
+ "TVW1234560",
+ );
+ });
+
+ it("[[Call]] is case‐insensitive", () => {
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("tVw12340")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "tVw12340",
+ );
+ });
+
+ it("[[Call]] casts I, L & O", () => {
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVWI234O")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "TVWI234O",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVWi234o")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "TVWi234o",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVWL234O")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "TVWL234O",
+ );
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("TVWl234o")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "TVWl234o",
+ );
+ });
+
+ it("[[Call]] ignores hyphens", () => {
+ assertEquals(
+ new Uint8Array(wrmgBase32Binary("--TVW---123-40-----")),
+ new Uint8Array([0xD6, 0xF8, 0x11, 0x0C, 0x80]),
+ "--TVW---123-40-----",
+ );
+ });
+
+ it("[[Call]] throws when provided with an invalid character", () => {
+ assertThrows(() => wrmgBase32Binary("ABCu"));
+ assertThrows(() => wrmgBase32Binary("ABCU"));
+ assertThrows(() => wrmgBase32Binary("ABC="));
+ });
+
+ it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => {
+ assertThrows(() => wrmgBase32Binary("234BCDEAA"));
+ assertThrows(() => wrmgBase32Binary("2-34-B--CD-EA-A-"));
+ });
+});
+
+describe("wrmgBase32String", () => {
+ it("[[Call]] returns the correct string", () => {
+ for (const [source, { wrmg }] of data) {
+ assertStrictEquals(wrmgBase32String(source), wrmg);
+ }
+ });
+});