+/**
+ * Returns the result of decoding the provided base16 string into an
+ * ArrayBuffer.
+ *
+ * ※ This function is not exposed.
+ */
+const decodeBase16 = (source) => {
+ const u4s = map(
+ source,
+ (ucsCharacter) => {
+ const code = getCodeUnit(ucsCharacter, 0);
+ const result = code >= 0x30 && code <= 0x39
+ ? code - 48
+ : code >= 0x41 && code <= 0x46
+ ? code - 55
+ : code >= 0x61 && code <= 0x66
+ ? code - 87
+ : -1;
+ if (result < 0) {
+ throw new RangeError(
+ `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
+ );
+ } else {
+ return result;
+ }
+ },
+ );
+ const { length } = u4s;
+ if (length % 2 == 1) {
+ // The length is such that an entire letter would be dropped during
+ // a forgiving decode.
+ throw new RangeError(
+ `Piscēs: Base16 string has invalid length: ${source}.`,
+ );
+ } else {
+ // 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;) {
+ call(viewSetUint8, dataView, [
+ floor(index / 2),
+ (u4s[index] << 4) | u4s[++index, index++],
+ ]);
+ }
+ return call(getViewBuffer, dataView, []);
+ }
+};
+
+/**
+ * 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;
+ const lengthMod8 = length % 8;
+ 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(
+ `Piscēs: Base32 string has invalid length: ${source}.`,
+ );
+ } else {
+ // 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;) {
+ // 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, []);
+ }
+};
+