From 66c768fe31ca379d83d546491dc2bddf412c6f23 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 14 May 2018 15:55:17 +0000 Subject: [PATCH] ESM: Ported Tidy operations. Updated portOperation script to attempt to find the run function and list related constants. --- src/core/config/scripts/portOperation.mjs | 38 ++++++++-- src/core/operations/DropBytes.mjs | 92 +++++++++++++++++++++++ src/core/operations/PadLines.mjs | 70 +++++++++++++++++ src/core/operations/RemoveNullBytes.mjs | 43 +++++++++++ src/core/operations/RemoveWhitespace.mjs | 86 +++++++++++++++++++++ src/core/operations/TakeBytes.mjs | 86 +++++++++++++++++++++ 6 files changed, 409 insertions(+), 6 deletions(-) create mode 100644 src/core/operations/DropBytes.mjs create mode 100644 src/core/operations/PadLines.mjs create mode 100644 src/core/operations/RemoveNullBytes.mjs create mode 100644 src/core/operations/RemoveWhitespace.mjs create mode 100644 src/core/operations/TakeBytes.mjs diff --git a/src/core/config/scripts/portOperation.mjs b/src/core/config/scripts/portOperation.mjs index a810f7d9..d746be1f 100644 --- a/src/core/config/scripts/portOperation.mjs +++ b/src/core/config/scripts/portOperation.mjs @@ -62,6 +62,25 @@ function main() { const esc = new EscapeString(); const desc = esc.run(op.description, ["Special chars", "Double"]); + // Attempt to find the operation run function based on the JSDoc comment + const regex = `\\* ${opName} operation[^:]+:(?: function ?\\(input, args\\))? ?{([\\s\\S]+?)\n }`; + let runFunc = "\n"; + try { + runFunc = legacyFile.match(new RegExp(regex, "im"))[1]; + } catch (err) {} + + + // List all constants in legacyFile + const constants = []; + try { + const constantsRegex = /\* @constant[^/]+\/\s+([^\n]+)/gim; + let m; + + while ((m = constantsRegex.exec(legacyFile)) !== null) { + constants.push(m[1]); + } + } catch (err) {} + const template = `/** * ${author} * ${copyright} @@ -94,8 +113,7 @@ class ${moduleName} extends Operation { * @param {Object[]} args * @returns {${op.outputType}} */ - run(input, args) { - + run(input, args) {${runFunc} } ${op.highlight ? ` /** @@ -130,18 +148,26 @@ export default ${moduleName}; `; console.log("\nLegacy operation config\n-----------------------\n"); - console.log(template); console.log(JSON.stringify(op, null, 4)); console.log("\n-----------------------\n"); + console.log("\nPotentially related constants\n-----------------------\n"); + console.log(constants.join("\n")); + console.log("\n-----------------------\n"); const filename = path.join(dir, `../operations/${moduleName}.mjs`); if (fs.existsSync(filename)) { - console.log(`\u274c ${filename} already exists. It has NOT been overwritten.`); + console.log(`\x1b[31m\u274c ${filename} already exists. It has NOT been overwritten.\x1b[0m`); process.exit(0); } fs.writeFileSync(filename, template); - console.log("\u2714 Written to " + filename); - console.log(`Open ${legacyFilename} and copy the relevant code over. Make sure you check imports, args and highlights.`); + + console.log("\x1b[32m\u2714\x1b[0m Operation written to \x1b[32m" + filename + "\x1b[0m"); + if (runFunc === "\n") { + console.log("\x1b[31m\u274c The run function could not be located automatically.\x1b[0m You will have to copy it accross manually."); + } else { + console.log("\x1b[32m\u2714\x1b[0m The run function was copied across. Double check that it was copied correctly. It may rely on other functions which have not been copied."); + } + console.log(`\nOpen \x1b[32m${legacyFilename}\x1b[0m and copy any relevant code over. Make sure you check imports, args and highlights. Code required by multiple operations should be stored in /src/core/lib/`); } diff --git a/src/core/operations/DropBytes.mjs b/src/core/operations/DropBytes.mjs new file mode 100644 index 00000000..f62ac1bf --- /dev/null +++ b/src/core/operations/DropBytes.mjs @@ -0,0 +1,92 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * Drop bytes operation + */ +class DropBytes extends Operation { + + /** + * DropBytes constructor + */ + constructor() { + super(); + + this.name = "Drop bytes"; + this.module = "Default"; + this.description = "Cuts a slice of the specified number of bytes out of the data."; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.args = [ + { + "name": "Start", + "type": "number", + "value": 0 + }, + { + "name": "Length", + "type": "number", + "value": 5 + }, + { + "name": "Apply to each line", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {ArrayBuffer} + */ + run(input, args) { + const start = args[0], + length = args[1], + applyToEachLine = args[2]; + + if (start < 0 || length < 0) + throw "Error: Invalid value"; + + if (!applyToEachLine) { + const left = input.slice(0, start), + right = input.slice(start + length, input.byteLength); + const result = new Uint8Array(left.byteLength + right.byteLength); + result.set(new Uint8Array(left), 0); + result.set(new Uint8Array(right), left.byteLength); + return result.buffer; + } + + // Split input into lines + const data = new Uint8Array(input); + const lines = []; + let line = [], + i; + + for (i = 0; i < data.length; i++) { + if (data[i] === 0x0a) { + lines.push(line); + line = []; + } else { + line.push(data[i]); + } + } + lines.push(line); + + let output = []; + for (i = 0; i < lines.length; i++) { + output = output.concat(lines[i].slice(0, start).concat(lines[i].slice(start+length, lines[i].length))); + output.push(0x0a); + } + return new Uint8Array(output.slice(0, output.length-1)).buffer; + } + +} + +export default DropBytes; diff --git a/src/core/operations/PadLines.mjs b/src/core/operations/PadLines.mjs new file mode 100644 index 00000000..e9e2b45a --- /dev/null +++ b/src/core/operations/PadLines.mjs @@ -0,0 +1,70 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * Pad lines operation + */ +class PadLines extends Operation { + + /** + * PadLines constructor + */ + constructor() { + super(); + + this.name = "Pad lines"; + this.module = "Default"; + this.description = "Add the specified number of the specified character to the beginning or end of each line"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Position", + "type": "option", + "value": ["Start", "End"] + }, + { + "name": "Length", + "type": "number", + "value": 5 + }, + { + "name": "Character", + "type": "binaryShortString", + "value": " " + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [position, len, chr] = args, + lines = input.split("\n"); + let output = "", + i = 0; + + if (position === "Start") { + for (i = 0; i < lines.length; i++) { + output += lines[i].padStart(lines[i].length+len, chr) + "\n"; + } + } else if (position === "End") { + for (i = 0; i < lines.length; i++) { + output += lines[i].padEnd(lines[i].length+len, chr) + "\n"; + } + } + + return output.slice(0, output.length-1); + } + +} + +export default PadLines; diff --git a/src/core/operations/RemoveNullBytes.mjs b/src/core/operations/RemoveNullBytes.mjs new file mode 100644 index 00000000..dcbf9251 --- /dev/null +++ b/src/core/operations/RemoveNullBytes.mjs @@ -0,0 +1,43 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * Remove null bytes operation + */ +class RemoveNullBytes extends Operation { + + /** + * RemoveNullBytes constructor + */ + constructor() { + super(); + + this.name = "Remove null bytes"; + this.module = "Default"; + this.description = "Removes all null bytes (0x00) from the input."; + this.inputType = "byteArray"; + this.outputType = "byteArray"; + this.args = []; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + const output = []; + for (let i = 0; i < input.length; i++) { + if (input[i] !== 0) output.push(input[i]); + } + return output; + } + +} + +export default RemoveNullBytes; diff --git a/src/core/operations/RemoveWhitespace.mjs b/src/core/operations/RemoveWhitespace.mjs new file mode 100644 index 00000000..a6564809 --- /dev/null +++ b/src/core/operations/RemoveWhitespace.mjs @@ -0,0 +1,86 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * Remove whitespace operation + */ +class RemoveWhitespace extends Operation { + + /** + * RemoveWhitespace constructor + */ + constructor() { + super(); + + this.name = "Remove whitespace"; + this.module = "Default"; + this.description = "Optionally removes all spaces, carriage returns, line feeds, tabs and form feeds from the input data.

This operation also supports the removal of full stops which are sometimes used to represent non-printable bytes in ASCII output."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Spaces", + "type": "boolean", + "value": true + }, + { + "name": "Carriage returns (\\r)", + "type": "boolean", + "value": true + }, + { + "name": "Line feeds (\\n)", + "type": "boolean", + "value": true + }, + { + "name": "Tabs", + "type": "boolean", + "value": true + }, + { + "name": "Form feeds (\\f)", + "type": "boolean", + "value": true + }, + { + "name": "Full stops", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [ + removeSpaces, + removeCariageReturns, + removeLineFeeds, + removeTabs, + removeFormFeeds, + removeFullStops + ] = args; + let data = input; + + if (removeSpaces) data = data.replace(/ /g, ""); + if (removeCariageReturns) data = data.replace(/\r/g, ""); + if (removeLineFeeds) data = data.replace(/\n/g, ""); + if (removeTabs) data = data.replace(/\t/g, ""); + if (removeFormFeeds) data = data.replace(/\f/g, ""); + if (removeFullStops) data = data.replace(/\./g, ""); + return data; + } + +} + +export default RemoveWhitespace; diff --git a/src/core/operations/TakeBytes.mjs b/src/core/operations/TakeBytes.mjs new file mode 100644 index 00000000..1fec3997 --- /dev/null +++ b/src/core/operations/TakeBytes.mjs @@ -0,0 +1,86 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * Take bytes operation + */ +class TakeBytes extends Operation { + + /** + * TakeBytes constructor + */ + constructor() { + super(); + + this.name = "Take bytes"; + this.module = "Default"; + this.description = "Takes a slice of the specified number of bytes from the data."; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.args = [ + { + "name": "Start", + "type": "number", + "value": 0 + }, + { + "name": "Length", + "type": "number", + "value": 5 + }, + { + "name": "Apply to each line", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {ArrayBuffer} + */ + run(input, args) { + const start = args[0], + length = args[1], + applyToEachLine = args[2]; + + if (start < 0 || length < 0) + throw "Error: Invalid value"; + + if (!applyToEachLine) + return input.slice(start, start+length); + + // Split input into lines + const data = new Uint8Array(input); + const lines = []; + let line = [], + i; + + for (i = 0; i < data.length; i++) { + if (data[i] === 0x0a) { + lines.push(line); + line = []; + } else { + line.push(data[i]); + } + } + lines.push(line); + + let output = []; + for (i = 0; i < lines.length; i++) { + output = output.concat(lines[i].slice(start, start+length)); + output.push(0x0a); + } + return new Uint8Array(output.slice(0, output.length-1)).buffer; + } + +} + +export default TakeBytes;