2018-08-22 20:24:32 +01:00
/**
* @author PenguinGeorge [george@penguingeorge.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
2019-07-09 12:23:59 +01:00
import Operation from "../Operation.mjs" ;
import OperationError from "../errors/OperationError.mjs" ;
import Utils from "../Utils.mjs" ;
import { alphabetName , ALPHABET_OPTIONS } from "../lib/Base85.mjs" ;
2018-08-22 20:24:32 +01:00
/**
* From Base85 operation
*/
class FromBase85 extends Operation {
/**
* From Base85 constructor
*/
constructor () {
super ();
this . name = "From Base85" ;
this . module = "Default" ;
2018-08-23 22:05:31 +01:00
this . description = "Base85 (also called Ascii85) 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." ;
2018-08-22 20:24:32 +01:00
this . infoURL = "https://wikipedia.org/wiki/Ascii85" ;
this . inputType = "string" ;
this . outputType = "byteArray" ;
this . args = [
{
name : "Alphabet" ,
type : "editableOption" ,
value : ALPHABET_OPTIONS
},
2022-06-14 10:23:13 +01:00
{
name : "Remove non-alphabet chars" ,
type : "boolean" ,
value : true
},
2018-08-22 20:24:32 +01:00
];
2020-05-16 00:42:50 +02:00
this . checks = [
{
2020-05-22 03:30:57 +02:00
pattern :
"^\\s*(?:<~)?" + // Optional whitespace and starting marker
"[\\s!-uz]*" + // Any amount of base85 characters and whitespace
2020-06-10 15:50:26 +02:00
"[!-uz]{15}" + // At least 15 continoues base85 characters without whitespace
2020-05-22 03:30:57 +02:00
"[\\s!-uz]*" + // Any amount of base85 characters and whitespace
"(?:~>)?\\s*$" , // Optional ending marker and whitespace
args : [ "!-u" ],
2020-05-16 00:42:50 +02:00
},
{
2020-05-22 03:30:57 +02:00
pattern :
"^" +
"[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
2020-06-10 15:50:26 +02:00
"[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{15}" + // At least 15 continoues base85 characters without whitespace
2020-05-22 03:30:57 +02:00
"[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
"$" ,
args : [ "0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#" ],
2020-05-16 00:42:50 +02:00
},
{
2020-05-22 03:30:57 +02:00
pattern :
"^" +
"[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
2020-06-10 15:50:26 +02:00
"[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{15}" + // At least 15 continoues base85 characters without whitespace
2020-05-22 03:30:57 +02:00
"[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
"$" ,
args : [ "0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~" ],
2020-05-16 00:42:50 +02:00
},
];
2018-08-22 20:24:32 +01:00
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run ( input , args ) {
2018-08-23 22:05:31 +01:00
const alphabet = Utils . expandAlphRange ( args [ 0 ]). join ( "" ),
2018-08-22 20:24:32 +01:00
encoding = alphabetName ( alphabet ),
2022-06-14 10:23:13 +01:00
removeNonAlphChars = args [ 1 ],
2018-08-22 20:24:32 +01:00
result = [];
if ( alphabet . length !== 85 ||
[]. unique . call ( alphabet ). length !== 85 ) {
throw new OperationError ( "Alphabet must be of length 85" );
}
2022-07-08 15:15:53 +01:00
// Remove delimiters if present
const matches = input . match ( /^<~(.+?)~>$/ );
if ( matches !== null ) input = matches [ 1 ];
2022-06-14 10:23:13 +01:00
// Remove non-alphabet characters
if ( removeNonAlphChars ) {
const re = new RegExp ( "[^" + alphabet . replace ( /[[\]\\\-^$]/g , "\\$&" ) + "]" , "g" );
input = input . replace ( re , "" );
}
2018-08-22 20:24:32 +01:00
if ( input . length === 0 ) return [];
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 ) => {
const digit = alphabet . indexOf ( chr );
if ( digit < 0 || digit > 84 ) {
2022-06-14 10:23:13 +01:00
throw `Invalid character ' ${ chr } ' at index ${ i + idx } ` ;
2018-08-22 20:24:32 +01:00
}
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 ;
}
}
export default FromBase85 ;