Browse Source

Merge pull request #1553 from sg5506844/base92

Feature: Add Base92 operations
a3957273 1 year ago
parent
commit
c13997bdb1

+ 2 - 0
src/core/config/Categories.json

@@ -29,6 +29,8 @@
             "To Base64",
             "From Base64",
             "Show Base64 offsets",
+            "To Base92",
+            "From Base92",
             "To Base85",
             "From Base85",
             "To Base",

+ 44 - 0
src/core/lib/Base92.mjs

@@ -0,0 +1,44 @@
+/**
+ * Base92 resources.
+ *
+ * @author sg5506844 [sg5506844@gmail.com]
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import OperationError from "../errors/OperationError.mjs";
+
+/**
+ * Base92 alphabet char
+ *
+ * @param {number} val
+ * @returns {number}
+ */
+export function base92Chr(val) {
+    if (val < 0 || val >= 91) {
+        throw new OperationError("Invalid value");
+    }
+    if (val === 0)
+        return "!".charCodeAt(0);
+    else if (val <= 61)
+        return "#".charCodeAt(0) + val - 1;
+    else
+        return "a".charCodeAt(0) + val - 62;
+}
+
+/**
+ * Base92 alphabet ord
+ *
+ * @param {string} val
+ * @returns {number}
+ */
+export function base92Ord(val) {
+    if (val === "!")
+        return 0;
+    else if ("#" <= val && val <= "_")
+        return val.charCodeAt(0) - "#".charCodeAt(0) + 1;
+    else if ("a" <= val && val <= "}")
+        return val.charCodeAt(0) - "a".charCodeAt(0) + 62;
+    throw new OperationError(`${val} is not a base92 character`);
+}
+

+ 55 - 0
src/core/operations/FromBase92.mjs

@@ -0,0 +1,55 @@
+/**
+ * @author sg5506844 [sg5506844@gmail.com]
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import { base92Ord } from "../lib/Base92.mjs";
+import Operation from "../Operation.mjs";
+
+/**
+ * From Base92 operation
+ */
+class FromBase92 extends Operation {
+    /**
+     * FromBase92 constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "From Base92";
+        this.module = "Default";
+        this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
+        this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
+        this.inputType = "string";
+        this.outputType = "byteArray";
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {byteArray}
+     */
+    run(input, args) {
+        const res = [];
+        let bitString = "";
+
+        for (let i = 0; i < input.length; i += 2) {
+            if (i + 1 !== input.length) {
+                const x = base92Ord(input[i]) * 91 + base92Ord(input[i + 1]);
+                bitString += x.toString(2).padStart(13, "0");
+            } else {
+                const x = base92Ord(input[i]);
+                bitString += x.toString(2).padStart(6, "0");
+            }
+            while (bitString.length >= 8) {
+                res.push(parseInt(bitString.slice(0, 8), 2));
+                bitString = bitString.slice(8);
+            }
+        }
+
+        return res;
+    }
+}
+
+export default FromBase92;

+ 67 - 0
src/core/operations/ToBase92.mjs

@@ -0,0 +1,67 @@
+/**
+ * @author sg5506844 [sg5506844@gmail.com]
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import { base92Chr } from "../lib/Base92.mjs";
+import Operation from "../Operation.mjs";
+
+/**
+ * To Base92 operation
+ */
+class ToBase92 extends Operation {
+    /**
+     * ToBase92 constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "To Base92";
+        this.module = "Default";
+        this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
+        this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
+        this.inputType = "string";
+        this.outputType = "byteArray";
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {byteArray}
+     */
+    run(input, args) {
+        const res = [];
+        let bitString = "";
+
+        while (input.length > 0) {
+            while (bitString.length < 13 && input.length > 0) {
+                bitString += input[0].charCodeAt(0).toString(2).padStart(8, "0");
+                input = input.slice(1);
+            }
+            if (bitString.length < 13)
+                break;
+            const i = parseInt(bitString.slice(0, 13), 2);
+            res.push(base92Chr(Math.floor(i / 91)));
+            res.push(base92Chr(i % 91));
+            bitString = bitString.slice(13);
+        }
+
+        if (bitString.length > 0) {
+            if (bitString.length < 7) {
+                bitString = bitString.padEnd(6, "0");
+                res.push(base92Chr(parseInt(bitString, 2)));
+            } else {
+                bitString = bitString.padEnd(13, "0");
+                const i = parseInt(bitString.slice(0, 13), 2);
+                res.push(base92Chr(Math.floor(i / 91)));
+                res.push(base92Chr(i % 91));
+            }
+        }
+
+        return res;
+
+    }
+}
+
+export default ToBase92;

+ 1 - 0
tests/operations/index.mjs

@@ -25,6 +25,7 @@ import "./tests/Base58.mjs";
 import "./tests/Base64.mjs";
 import "./tests/Base62.mjs";
 import "./tests/Base85.mjs";
+import "./tests/Base92.mjs";
 import "./tests/BitwiseOp.mjs";
 import "./tests/ByteRepr.mjs";
 import "./tests/CartesianProduct.mjs";

+ 89 - 0
tests/operations/tests/Base92.mjs

@@ -0,0 +1,89 @@
+/**
+ * Base92 tests.
+ *
+ * @author sg5506844 [sg5506844@gmail.com]
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+    {
+        name: "To Base92: nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "To Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "To Base92: Spec encoding example 1",
+        input: "AB",
+        expectedOutput: "8y2",
+        recipeConfig: [
+            {
+                op: "To Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "To Base92: Spec encoding example 2",
+        input: "Hello!!",
+        expectedOutput: ";K_$aOTo&",
+        recipeConfig: [
+            {
+                op: "To Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "To Base92: Spec encoding example 3",
+        input: "base-92",
+        expectedOutput: "DX2?V<Y(*",
+        recipeConfig: [
+            {
+                op: "To Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "From Base92: nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "From Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "From Base92: Spec decoding example 1",
+        input: "G'_DW[B",
+        expectedOutput: "ietf!",
+        recipeConfig: [
+            {
+                op: "From Base92",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "From Base92: Invalid character",
+        input: "~",
+        expectedOutput: "~ is not a base92 character",
+        recipeConfig: [
+            {
+                op: "From Base92",
+                args: [],
+            },
+        ],
+    },
+]);