This commit is contained in:
George J 2018-05-14 10:05:16 +00:00 committed by GitHub
commit ca86cdb544
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 292 additions and 0 deletions

View file

@ -44,6 +44,8 @@ const Categories = [
"From Base32",
"To Base58",
"From Base58",
"To Base85",
"From Base85",
"To Base",
"From Base",
"To BCD",

View file

@ -2,6 +2,7 @@ import Arithmetic from "../operations/Arithmetic.js";
import Base from "../operations/Base.js";
import Base58 from "../operations/Base58.js";
import Base64 from "../operations/Base64.js";
import Base85 from "../operations/Base85.js";
import BCD from "../operations/BCD.js";
import BitwiseOp from "../operations/BitwiseOp.js";
import ByteRepr from "../operations/ByteRepr.js";
@ -320,6 +321,37 @@ const OperationConfig = {
}
]
},
"To Base85": {
module: "Default",
description: "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.<br><br>This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).<br><br>e.g. <code>hello world</code> becomes <code>BOu!rD]j7BEbo7</code><br><br>Base85 is commonly used in Adobe's PostScript and PDF file formats.<br><br><strong>Options</strong><br><u>Alphabet</u><ul><li>Standard - The standard alphabet, referred to as Ascii85</li><li>Z85 (ZeroMQ) - A string-safe variant of Base85, which avoids quote marks and backslash characters</li><li>IPv6 - A variant of Base85 suitable for encoding IPv6 addresses (RFC 1924)</li></ul><u>Include delimiter</u><br>Adds a '<~' and '~>' delimiter to the start and end of the data. This is standard for Adobe's implementation of Base85.",
inputType: "byteArray",
outputType: "string",
args: [
{
name: "Alphabet",
type: "editableOption",
value: Base85.ALPHABET_OPTIONS
},
{
name: "Include delimiter",
type: "boolean",
value: Base85.INCLUDE_DELIMITER
},
]
},
"From Base85": {
module: "Default",
description: "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.<br><br>This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included).<br><br>e.g. <code>BOu!rD]j7BEbo7</code> becomes <code>hello world</code><br><br>Base85 is commonly used in Adobe's PostScript and PDF file formats.",
inputType: "string",
outputType: "byteArray",
args: [
{
name: "Alphabet",
type: "editableOption",
value: Base85.ALPHABET_OPTIONS
},
]
},
"Show Base64 offsets": {
module: "Default",
description: "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.<br><br>This operation shows all possible offsets for a given string so that each possible encoding can be considered.",

View file

@ -2,6 +2,7 @@ import FlowControl from "../../FlowControl.js";
import Arithmetic from "../../operations/Arithmetic.js";
import Base from "../../operations/Base.js";
import Base58 from "../../operations/Base58.js";
import Base85 from "../../operations/Base85.js";
import Base64 from "../../operations/Base64.js";
import BCD from "../../operations/BCD.js";
import BitwiseOp from "../../operations/BitwiseOp.js";
@ -74,6 +75,8 @@ OpModules.Default = {
"From Base32": Base64.runFrom32,
"To Base58": Base58.runTo,
"From Base58": Base58.runFrom,
"To Base85": Base85.runTo,
"From Base85": Base85.runFrom,
"To Base": Base.runTo,
"From Base": Base.runFrom,
"To BCD": BCD.runToBCD,

View file

@ -0,0 +1,165 @@
/**
* Base85 operations.
*
* @author George J [george@penguingeorge.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
const Base85 = {
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{
name: "Standard",
value: "!\"#$%&&apos;()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu",
},
{
name: "Z85 (ZeroMQ)",
value: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#",
},
{
name: "IPv6",
value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|~}",
},
],
/**
* Includes a '<~' and '~>' at the beginning and end of the Base85 data.
* @constant
* @default
*/
INCLUDE_DELIMITER: false,
/**
* To Base85 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
let alphabet = args[0] || Base85.ALPHABET_OPTIONS[0].value,
encoding = Base85._alphabetName(alphabet),
result = "";
if (alphabet.length !== 85 || [].unique.call(alphabet).length !== 85) {
throw ("Alphabet must be of length 85");
}
let block;
for (let i = 0; i < input.length; i += 4) {
block = (
((input[i]) << 24) +
((input[i + 1] || 0) << 16) +
((input[i + 2] || 0) << 8) +
((input[i + 3] || 0))
) >>> 0;
if (encoding !== "Standard" || block > 0) {
let digits = [];
for (let j = 0; j < 5; j++) {
digits.push(block % 85);
block = Math.floor(block / 85);
}
digits = digits.reverse();
if (input.length < i + 4) {
digits.splice(input.length - (i + 4), 4);
}
result += digits.map(digit => alphabet[digit]).join("");
} else {
result += (encoding === "Standard") ? "z" : null;
}
}
if (args[1] || Base85.INCLUDE_DELIMITER) result = "<~" + result + "~>";
return result;
},
/**
* From Base85 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base85.ALPHABET_OPTIONS[0].value,
encoding = Base85._alphabetName(alphabet),
result = [];
if (alphabet.length !== 85 || [].unique.call(alphabet).length !== 85) {
throw ("Alphabet must be of length 85");
}
let matches = input.match(/<~(.+?)~>/);
if (matches !== null) input = matches[1];
let i = 0;
let block, blockBytes;
while (i < input.length) {
if (encoding === "Standard" && input[i] === "z") {
result.push(0, 0, 0, 0);
i++;
} else {
let digits = [];
digits = input
.substr(i, 5)
.split("")
.map((chr, idx) => {
let digit = alphabet.indexOf(chr);
if (digit < 0 || digit > 84) {
throw "Invalid character '" + chr + "' at index " + idx;
}
return digit;
});
block =
digits[0] * 52200625 +
digits[1] * 614125 +
(i + 2 < input.length ? digits[2] : 84) * 7225 +
(i + 3 < input.length ? digits[3] : 84) * 85 +
(i + 4 < input.length ? digits[4] : 84);
blockBytes = [
(block >> 24) & 0xff,
(block >> 16) & 0xff,
(block >> 8) & 0xff,
block & 0xff
];
if (input.length < i + 5) {
blockBytes.splice(input.length - (i + 5), 5);
}
result.push.apply(result, blockBytes);
i += 5;
}
}
return result;
},
/**
* Returns the name of the alphabet, when given the alphabet.
*/
_alphabetName: function(alphabet) {
alphabet = alphabet.replace("'", "&apos;");
let name;
Base85.ALPHABET_OPTIONS.forEach(function(a) {
if (escape(alphabet) === escape(a.value)) name = a.name;
});
return name;
}
};
export default Base85;

View file

@ -15,6 +15,7 @@ import "babel-polyfill";
import TestRegister from "./TestRegister.js";
import "./tests/operations/Base58.js";
import "./tests/operations/Base64.js";
import "./tests/operations/Base85.js";
import "./tests/operations/BCD.js";
import "./tests/operations/BitwiseOp.js";
import "./tests/operations/BSON.js";

View file

@ -0,0 +1,89 @@
/**
* Base85 tests.
*
* @author George J [george@penguingeorge.com]
*
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "To Base85 (Standard): nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base85",
args: [null, false],
},
],
},
{
name: "To Base85 (Standard (Ascii85)): Hello, World!",
input: "Hello, World!",
expectedOutput: "87cURD_*#4DfTZ)+T",
recipeConfig: [
{
op: "To Base85",
args: [null, false],
},
],
},
{
name: "To Base85 (Standard (Ascii85)): UTF-8",
input: "ნუ პანიკას",
expectedOutput: "iIdZZK;0RJK:_%SOPth^iIdNVK:1\\NOPthc",
recipeConfig: [
{
op: "To Base85",
args: [null, false],
},
],
},
{
name: "To Base85 (Z85 (ZeroMQ)): Hello, World!",
input: "Hello, World!",
expectedOutput: "nm2QNz.92jz5PV8aP",
recipeConfig: [
{
op: "To Base85",
args: ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#", false],
},
],
},
{
name: "From Base85 (Standard): nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base85",
args: [null, false],
},
],
},
{
name: "From Base85 (Standard): Hello, World!",
input: "87cURD_*#4DfTZ)+T",
expectedOutput: "Hello, World!",
recipeConfig: [
{
op: "From Base85",
args: [null, false],
},
],
},
{
name: "From Base85 (Standard): UTF-8",
input: "iIdZZK;0RJK:_%SOPth^iIdNVK:1\\NOPthc",
expectedOutput: "ნუ პანიკას",
recipeConfig: [
{
op: "From Base85",
args: [null, false],
},
],
},
]);