diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 66663f4a..b87d0ddb 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -53,7 +53,8 @@ "To MessagePack", "From MessagePack", "To Braille", - "From Braille" + "From Braille", + "From Length Value" ] }, { diff --git a/src/core/lib/LengthValue.mjs b/src/core/lib/LengthValue.mjs new file mode 100644 index 00000000..3ec0c5e5 --- /dev/null +++ b/src/core/lib/LengthValue.mjs @@ -0,0 +1,71 @@ +/** + * @author gchq77703 [] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +const defaults = { + location: 0, + bytesInLength: 1, + basicEncodingRules: false +}; + +/** + * Length Value library + */ +export default class LengthValue { + + /** + * LengthValue constructor + */ + constructor(input, options) { + this.input = input; + Object.assign(this, defaults, options); + } + + /** + * @returns {Number} + */ + getLength() { + if (this.basicEncodingRules) { + const bit = this.input[this.location]; + if (bit & 0x80) { + this.bytesInLength = bit & ~0x80; + } else { + this.location++; + return bit & ~0x80; + } + } + + let length = 0; + + for (let i = 0; i < this.bytesInLength; i++) { + length += this.input[this.location] * Math.pow(Math.pow(2, 8), i); + this.location++; + } + + return length; + } + + /** + * @param {Number} length + * @returns {Number[]} + */ + getValue(length) { + const value = []; + + for (let i = 0; i < length; i++) { + value.push(this.input[this.location]); + this.location++; + } + + return value; + } + + /** + * @returns {Boolean} + */ + atEnd() { + return this.input.length <= this.location; + } +} diff --git a/src/core/operations/LengthValueDecoder.mjs b/src/core/operations/LengthValueDecoder.mjs new file mode 100644 index 00000000..12fe6da2 --- /dev/null +++ b/src/core/operations/LengthValueDecoder.mjs @@ -0,0 +1,78 @@ +/** + * @author gchq77703 [] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import LengthValue from "../lib/LengthValue"; + +/** + * From Length Value operation + */ +class FromLengthValue extends Operation { + + /** + * FromLengthValue constructor + */ + constructor() { + super(); + + this.name = "From Length Value"; + this.module = "Default"; + this.description = "Converts a Length-Value (LV) encoded string into a line-delimited JSON (LDJSON / Streaming JSON) format"; + this.infoURL = ""; + this.inputType = "byteArray"; + this.outputType = "string"; + this.args = [ + { + name: "Bytes in Length Value", + type: "populateOption", + value: [ + { + name: "1 Byte", + value: "1" + }, + { + name: "2 Bytes", + value: "2" + }, + { + name: "4 Bytes", + value: "4" + } + ] + }, + { + name: "Use Basic Encoding Rules", + type: "boolean" + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const bytesInLength = parseInt(args[0].split(" ")[0], 10); + const basicEncodingRules = args[2]; + + const lv = new LengthValue(input, { bytesInLength, basicEncodingRules }); + + const data = []; + + while (!lv.atEnd()) { + const dataLength = lv.getLength(); + const value = lv.getValue(dataLength); + + data.push(value); + } + + return data.map(line => line.map(value => ("00" + value.toString(16)).slice(-2)).join(" ")).join("\n"); + } + +} + +export default FromLengthValue;