+++ /dev/null
-// ♓🌟 Piscēs ∷ base64.test.js
-// ====================================================================
-//
-// Copyright © 2020–2022 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 {
- assert,
- assertEquals,
- assertStrictEquals,
- assertThrows,
- describe,
- it,
-} from "./dev-deps.js";
-import { a2b, b2a, isBase64 } from "./base64.js";
-
-const base64s = {
- "AGIAYQBzAGUANgA0": "base64",
- "R/Q=": new Uint16Array([62535]),
- "S0lCSQ==": new Uint8ClampedArray([75, 73, 66, 73]).buffer,
- YmFzZTY0: new DataView(
- new Uint8Array([98, 97, 115, 101, 54, 52]).buffer,
- ),
-};
-
-describe("a2b", () => {
- it("[[Call]] returns the correct data", () => {
- assertEquals(
- a2b("AGIAYQBzAGUANgA0"),
- new Uint8Array(
- Array.prototype.map.call(
- "\u{0}b\u{0}a\u{0}s\u{0}e\u{0}6\u{0}4",
- ($) => $.charCodeAt(0),
- ),
- ).buffer,
- "AGIAYQBzAGUANgA0",
- );
- assertEquals(
- a2b("R/Q="),
- new Uint16Array([62535]).buffer,
- "R/Q=",
- );
- assertEquals(
- a2b("S0lCSQ=="),
- new Uint8ClampedArray([75, 73, 66, 73]).buffer,
- "S0lCSQ==",
- );
- assertEquals(
- a2b("YmFzZTY0"),
- new Uint8Array([98, 97, 115, 101, 54, 52]).buffer,
- "YmFzZTY0",
- );
- });
-
- it("[[Call]] throws when provided with an invalid character", () => {
- assertThrows(() => a2b("abc_"));
- });
-
- it("[[Call]] throws when provided with an invalid equals", () => {
- assertThrows(() => a2b("abc=="));
- });
-
- it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => {
- assertThrows(() => a2b("abcde"));
- assertThrows(() => a2b("abcde==="));
- });
-});
-
-describe("b2a", () => {
- it("[[Call]] returns the correct string", () => {
- Object.entries(base64s).forEach(([key, value]) =>
- assertStrictEquals(b2a(value), key)
- );
- });
-});
-
-describe("isBase64", () => {
- it("[[Call]] returns true for base64 strings", () => {
- Object.keys(base64s).forEach((key) => assert(isBase64(key)));
- });
-
- it("[[Call]] returns false for others", () => {
- [
- undefined,
- null,
- true,
- Symbol(),
- 27,
- 98n,
- {},
- [],
- () => {},
- new Proxy({}, {}),
- "abc_",
- "a",
- "abc==",
- ].forEach((value) => assert(!isBase64(value)));
- });
-});
-// ♓🌟 Piscēs ∷ base64.js
+// ♓🌟 Piscēs ∷ binary.js
// ====================================================================
//
// Copyright © 2020–2022 Lady [@ Lady’s Computer].
setUint16: viewSetUint16,
} = viewPrototype;
-const base64CodeUnitIterablePrototype = {
+const bufferFromArgs = ($, $s) =>
+ $ instanceof Buffer
+ ? $
+ : $ instanceof View
+ ? call(getViewBuffer, $, [])
+ : $ instanceof TypedArray
+ ? call(getTypedArrayBuffer, $, [])
+ : ((string) =>
+ call(
+ getViewBuffer,
+ reduce(
+ string,
+ (result, ucsCharacter, index) => (
+ call(viewSetUint16, result, [
+ index * 2,
+ getCodeUnit(ucsCharacter, 0),
+ ]), result
+ ),
+ new View(new Buffer(string.length * 2)),
+ ),
+ [],
+ ))(
+ typeof $ == "string"
+ ? $
+ : hasOwnProperty($, "raw")
+ ? rawString($, ...$s)
+ : `${$}`,
+ );
+
+const binaryCodeUnitIterablePrototype = {
[iteratorSymbol]() {
return {
next: bind(
},
};
-/**
- * Returns an ArrayBuffer generated from the provided Base64.
- *
- * This function can also be used as a tag for a template literal. The
- * literal will be interpreted akin to `String.raw`.
- */
-export const a2b = ($, ...$s) => {
- const source = stringReplace(
- typeof $ == "string"
- ? $
- : hasOwnProperty($, "raw")
- ? rawString($, ...$s)
- : `${$}`,
- /[\t\n\f\r ]+/gu,
- "",
- );
+const decodeBase64 = (source, safe = false) => {
const u6s = map(
source.length % 4 == 0
? stringReplace(source, /={1,2}$/u, "")
? code - 71
: code >= 0x30 && code <= 0x39
? code + 4
- : code == 0x2B
+ : code == (safe ? 0x2D : 0x2B)
? 62
- : code == 0x2F
+ : code == (safe ? 0x5F : 0x2F)
? 63
: -1;
if (result < 0) {
return call(getViewBuffer, dataView, []);
};
-/**
- * Returns a (big‐endian) base64 string created from a typed array,
- * buffer, or 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 b2a = ($, ...$s) => {
- const buffer = $ instanceof Buffer
- ? $
- : $ instanceof View
- ? call(getViewBuffer, $, [])
- : $ instanceof TypedArray
- ? call(getTypedArrayBuffer, $, [])
- : ((string) =>
- call(
- getViewBuffer,
- reduce(
- string,
- (result, ucsCharacter, index) => (
- call(viewSetUint16, result, [
- index * 2,
- getCodeUnit(ucsCharacter, 0),
- ]), result
- ),
- new View(new Buffer(string.length * 2)),
- ),
- [],
- ))(
- typeof $ == "string"
- ? $
- : hasOwnProperty($, "raw")
- ? rawString($, ...$s)
- : `${$}`,
- );
+const encodeBase64 = (buffer, safe = false) => {
const dataView = new View(buffer);
const byteLength = call(getBufferByteLength, buffer, []);
const minimumLengthOfResults = ceil(byteLength * 4 / 3);
const resultingCodeUnits = fill(
objectCreate(
- base64CodeUnitIterablePrototype,
+ binaryCodeUnitIterablePrototype,
{
length: {
value: minimumLengthOfResults +
: u6 < 62
? u6 - 4
: u6 < 63
- ? 43
+ ? (safe ? 0x2D : 0x2B)
: u6 < 64
- ? 47
+ ? (safe ? 0x5F : 0x2F)
: -1;
if (result < 0) {
throw new RangeError(`Piscēs: Unexpected Base64 value: ${u6}.`);
return stringFromCodeUnits(...resultingCodeUnits);
};
+const sourceFromArgs = ($, $s) =>
+ stringReplace(
+ typeof $ == "string"
+ ? $
+ : hasOwnProperty($, "raw")
+ ? rawString($, ...$s)
+ : `${$}`,
+ /[\t\n\f\r ]+/gu,
+ "",
+ );
+
+/**
+ * Returns an ArrayBuffer generated from the provided Base64.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ */
+export const base64Binary = ($, ...$s) =>
+ decodeBase64(sourceFromArgs($, $s));
+
+/**
+ * Returns a (big‐endian) base64 string created from a 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 base64String = ($, ...$s) =>
+ encodeBase64(bufferFromArgs($, $s));
+
+/**
+ * Returns an ArrayBuffer generated from the provided filename‐safe
+ * Base64.
+ *
+ * This function can also be used as a tag for a template literal. The
+ * literal will be interpreted akin to `String.raw`.
+ */
+export const filenameSafeBase64Binary = ($, ...$s) =>
+ decodeBase64(sourceFromArgs($, $s), true);
+
+/**
+ * Returns a (big‐endian) filename‐safe base64 string created from a
+ * 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 filenameSafeBase64String = ($, ...$s) =>
+ encodeBase64(bufferFromArgs($, $s), true);
+
/**
* 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 filename‐safe base64 string.
+ *
+ * Returns false if the provided value is not a string primitive.
+ */
+export const isFilenameSafeBase64 = ($) => {
+ if (typeof $ !== "string") {
+ return false;
+ } else {
+ const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
+ 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;
+ }
+};
--- /dev/null
+// ♓🌟 Piscēs ∷ binary.test.js
+// ====================================================================
+//
+// Copyright © 2020–2022 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 {
+ assert,
+ assertEquals,
+ assertStrictEquals,
+ assertThrows,
+ describe,
+ it,
+} from "./dev-deps.js";
+import {
+ base64Binary,
+ base64String,
+ filenameSafeBase64Binary,
+ filenameSafeBase64String,
+ isBase64,
+ isFilenameSafeBase64,
+} from "./binary.js";
+
+const base64s = {
+ "AGIAYQBzAGUANgA0": "base64",
+ "R/Q=": new Uint16Array([62535]),
+ "S0lCSQ==": new Uint8ClampedArray([75, 73, 66, 73]).buffer,
+ YmFzZTY0: new DataView(
+ new Uint8Array([98, 97, 115, 101, 54, 52]).buffer,
+ ),
+};
+
+describe("base64Binary", () => {
+ it("[[Call]] returns the correct data", () => {
+ assertEquals(
+ base64Binary("AGIAYQBzAGUANgA0"),
+ new Uint8Array(
+ Array.prototype.map.call(
+ "\u{0}b\u{0}a\u{0}s\u{0}e\u{0}6\u{0}4",
+ ($) => $.charCodeAt(0),
+ ),
+ ).buffer,
+ "AGIAYQBzAGUANgA0",
+ );
+ assertEquals(
+ base64Binary("R/Q="),
+ new Uint16Array([62535]).buffer,
+ "R/Q=",
+ );
+ assertEquals(
+ base64Binary("S0lCSQ=="),
+ new Uint8ClampedArray([75, 73, 66, 73]).buffer,
+ "S0lCSQ==",
+ );
+ assertEquals(
+ base64Binary("YmFzZTY0"),
+ new Uint8Array([98, 97, 115, 101, 54, 52]).buffer,
+ "YmFzZTY0",
+ );
+ });
+
+ it("[[Call]] throws when provided with an invalid character", () => {
+ assertThrows(() => base64Binary("abc_"));
+ });
+
+ it("[[Call]] throws when provided with an invalid equals", () => {
+ assertThrows(() => base64Binary("abc=="));
+ });
+
+ it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => {
+ assertThrows(() => base64Binary("abcde"));
+ assertThrows(() => base64Binary("abcde==="));
+ });
+});
+
+describe("base64String", () => {
+ it("[[Call]] returns the correct string", () => {
+ Object.entries(base64s).forEach(([key, value]) =>
+ assertStrictEquals(base64String(value), key)
+ );
+ });
+});
+
+describe("filenameSafeBase64Binary", () => {
+ it("[[Call]] returns the correct data", () => {
+ assertEquals(
+ filenameSafeBase64Binary("AGIAYQBzAGUANgA0"),
+ new Uint8Array(
+ Array.prototype.map.call(
+ "\u{0}b\u{0}a\u{0}s\u{0}e\u{0}6\u{0}4",
+ ($) => $.charCodeAt(0),
+ ),
+ ).buffer,
+ "AGIAYQBzAGUANgA0",
+ );
+ assertEquals(
+ filenameSafeBase64Binary("R_Q="),
+ new Uint16Array([62535]).buffer,
+ "R/Q=",
+ );
+ assertEquals(
+ filenameSafeBase64Binary("S0lCSQ=="),
+ new Uint8ClampedArray([75, 73, 66, 73]).buffer,
+ "S0lCSQ==",
+ );
+ assertEquals(
+ filenameSafeBase64Binary("YmFzZTY0"),
+ new Uint8Array([98, 97, 115, 101, 54, 52]).buffer,
+ "YmFzZTY0",
+ );
+ });
+
+ it("[[Call]] throws when provided with an invalid character", () => {
+ assertThrows(() => filenameSafeBase64Binary("abc/"));
+ });
+
+ it("[[Call]] throws when provided with an invalid equals", () => {
+ assertThrows(() => filenameSafeBase64Binary("abc=="));
+ });
+
+ it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => {
+ assertThrows(() => filenameSafeBase64Binary("abcde"));
+ assertThrows(() => filenameSafeBase64Binary("abcde==="));
+ });
+});
+
+describe("filenameSafeBase64String", () => {
+ it("[[Call]] returns the correct string", () => {
+ Object.entries(base64s).forEach(([key, value]) =>
+ assertStrictEquals(
+ filenameSafeBase64String(value),
+ key.replace("+", "-").replace("/", "_"),
+ )
+ );
+ });
+});
+
+describe("isBase64", () => {
+ it("[[Call]] returns true for base64 strings", () => {
+ Object.keys(base64s).forEach((key) => assert(isBase64(key)));
+ });
+
+ it("[[Call]] returns false for others", () => {
+ [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 27,
+ 98n,
+ {},
+ [],
+ () => {},
+ new Proxy({}, {}),
+ "abc_",
+ "a",
+ "abc==",
+ ].forEach((value) => assert(!isBase64(value)));
+ });
+});
+
+describe("isFilenameSafeBase64", () => {
+ it("[[Call]] returns true for filename‐safe base64 strings", () => {
+ Object.keys(base64s).forEach((key) =>
+ assert(
+ isFilenameSafeBase64(key.replace("+", "-").replace("/", "_")),
+ )
+ );
+ });
+
+ it("[[Call]] returns false for others", () => {
+ [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 27,
+ 98n,
+ {},
+ [],
+ () => {},
+ new Proxy({}, {}),
+ "abc/",
+ "a",
+ "abc==",
+ ].forEach((value) => assert(!isFilenameSafeBase64(value)));
+ });
+});
// 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/>.
-export * from "./base64.js";
+export * from "./binary.js";
export * from "./collection.js";
export * from "./function.js";
export * from "./iri.js";