+ if (length % 4 == 1) {
+ // The length is such that an entire letter would be dropped during
+ // a forgiving decode.
+ throw new RangeError(
+ `Piscēs: Base64 string has invalid length: ${source}.`,
+ );
+ } else {
+ // 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;) {
+ // 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++],
+ ]);
+ }
+ }
+ return call(getViewBuffer, dataView, []);
+ }
+};
+
+/**
+ * Returns the result of encoding the provided ArrayBuffer into a
+ * base16 string.
+ *
+ * ※ This function is not exposed.
+ */
+const encodeBase16 = (buffer) => {
+ const dataView = new View(buffer);
+ const byteLength = call(getBufferByteLength, buffer, []);
+ const minimumLengthOfResults = byteLength * 2;
+ const resultingCodeUnits = fill(
+ objectCreate(
+ binaryCodeUnitIterablePrototype,
+ { length: { value: minimumLengthOfResults } },
+ ),
+ 0x3D,
+ );
+ for (let index = 0; index < byteLength;) {
+ const codeUnitIndex = index * 2;
+ const datum = call(viewGetUint8, dataView, [index++]);
+ const u4s = [datum >> 4, datum & 0xF];
+ for (let u4i = 0; u4i < 2; ++u4i) {
+ const u4 = u4s[u4i];
+ const result = u4 < 10 ? u4 + 48 : u4 < 16 ? u4 + 55 : -1;
+ if (result < 0) {
+ throw new RangeError(
+ `Piscēs: Unexpected Base16 value: ${u4}.`,
+ );
+ } else {
+ resultingCodeUnits[codeUnitIndex + u4i] = result;
+ }
+ }
+ }
+ 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}.`);