From ea2e9b84a352d946e8c56bc949ebd87ad279437d Mon Sep 17 00:00:00 2001 From: j83305 Date: Tue, 19 May 2020 12:00:59 +0100 Subject: [PATCH 1/4] [#927] added parity bit operation --- package-lock.json | 6 +- src/core/config/Categories.json | 3 +- src/core/lib/ParityBit.mjs | 41 ++++++++ src/core/operations/ParityBit.mjs | 123 ++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/ParityBit.mjs | 147 +++++++++++++++++++++++++++ 6 files changed, 317 insertions(+), 4 deletions(-) create mode 100644 src/core/lib/ParityBit.mjs create mode 100644 src/core/operations/ParityBit.mjs create mode 100644 tests/operations/tests/ParityBit.mjs diff --git a/package-lock.json b/package-lock.json index caaa90b2..02ed1af7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6709,9 +6709,9 @@ } }, "mkdirp": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", - "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "resolve": { diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 77e3d319..bc109430 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -341,7 +341,8 @@ "CRC-8 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", - "TCP/IP Checksum" + "TCP/IP Checksum", + "Parity Bit" ] }, { diff --git a/src/core/lib/ParityBit.mjs b/src/core/lib/ParityBit.mjs new file mode 100644 index 00000000..342631a1 --- /dev/null +++ b/src/core/lib/ParityBit.mjs @@ -0,0 +1,41 @@ +/** + * Parity Bit functions. + * + * @author j83305 [awz22@protonmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + * + */ + +import OperationError from "../errors/OperationError.mjs"; + +export function calculateParityBit(input, args) { + let count1s = 0; + for (let i = 0; i < input.length; i++){ + const character = input.charAt(i); + if (character === "1"){ + count1s++; + } else if (character !== args[3] && character !== "0" && character !== " "){ + throw new OperationError("unexpected character encountered: \"" + character + "\""); + } + } + let parityBit = "1"; + const flipflop = args[0] === "Even Parity" ? 0 : 1; + if (count1s % 2 === flipflop){ + parityBit = "0"; + } + if (args[1] === "End"){ + return input + parityBit; + }else{ + return parityBit + input; + } +} + +// just removes the parity bit to return the original data +export function decodeParityBit(input, args) { + if (args[1] === "End"){ + return input.slice(0, -1); + }else{ + return input.slice(1); + } +} \ No newline at end of file diff --git a/src/core/operations/ParityBit.mjs b/src/core/operations/ParityBit.mjs new file mode 100644 index 00000000..a27574f2 --- /dev/null +++ b/src/core/operations/ParityBit.mjs @@ -0,0 +1,123 @@ +/** + * @author j83305 [awz22@protonmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import { calculateParityBit, decodeParityBit } from "../lib/ParityBit.mjs"; + +/** + * Parity Bit operation + */ +class ParityBit extends Operation { + + /** + * ParityBit constructor + */ + constructor() { + super(); + + this.name = "Parity Bit"; + this.module = "Default"; + this.description = "A parity bit, or check bit, is the simplest form of error detection. It is a bit which is added to a string of bits and represents if the number of 1's in the binary string is an even number or odd number.

If a delimiter is specified, the parity bit calculation will be performed on each 'block' of the input data, where the blocks are created by slicing the input at each occurence of the delimiter character"; + this.infoURL = "https://wikipedia.org/wiki/Parity_bit"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Mode", + type: "option", + value: [ + "Even Parity", + "Odd Parity" + ] + }, + { + name: "Postion", + type: "option", + value: [ + "Start", + "End" + ] + }, + { + name: "Encode or Decode", + type: "option", + value: [ + "Encode", + "Decode" + ] + }, + { + name: "Delimiter", + type: "shortString", + value: "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + if (input.length === 0){ + return input; + } + const method = (input, args) => args[2] === "Encode" ? calculateParityBit(input, args) : decodeParityBit(input, args); + if (args[3].length > 0){ + let byteStrings = input.split(args[3]); + for (let byteStringsArrayIndex = 0; byteStringsArrayIndex < byteStrings.length; byteStringsArrayIndex++){ + byteStrings[byteStringsArrayIndex] = method(byteStrings[byteStringsArrayIndex], args); + } + return byteStrings.join(args[3]) + } + return method(input, args); + } + + /** + * Highlight Parity Bit + * + * @param {Object[]} pos + * @param {number} pos[].start + * @param {number} pos[].end + * @param {Object[]} args + * @returns {Object[]} pos + */ + highlight(pos, args) { + if (args[3].length === 0){ + if (args[1] === "Prepend"){ + pos[0].start += 1; + pos[0].end += 1; + } + return pos; + } + // need to be able to read input to do the highlighting when there is a delimiter + } + + /** + * Highlight Parity Bit in reverse + * + * @param {Object[]} pos + * @param {number} pos[].start + * @param {number} pos[].end + * @param {Object[]} args + * @returns {Object[]} pos + */ + highlightReverse(pos, args) { + if (args[3].length === 0){ + if (args[1] === "Prepend"){ + if (pos[0].start > 0){ + pos[0].start -= 1; + } + pos[0].end -= 1; + } + return pos; + } + } + +} + +export default ParityBit; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 8d3cd623..c9fb2f88 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -64,6 +64,7 @@ import "./tests/NormaliseUnicode.mjs"; import "./tests/OTP.mjs"; import "./tests/PGP.mjs"; import "./tests/PHP.mjs"; +import "./tests/ParityBit.mjs"; import "./tests/ParseIPRange.mjs"; import "./tests/ParseQRCode.mjs"; import "./tests/PowerSet.mjs"; diff --git a/tests/operations/tests/ParityBit.mjs b/tests/operations/tests/ParityBit.mjs new file mode 100644 index 00000000..bc42b9f8 --- /dev/null +++ b/tests/operations/tests/ParityBit.mjs @@ -0,0 +1,147 @@ +/** + * Parity Bit tests + * + * @author j83305 [awz22@protonmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Parity bit encode in even parity, 1 block of binary of arbitrary length, prepend, even number of 1s", + input: "01010101 10101010", + expectedOutput: "001010101 10101010", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Even Parity", + "Start", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in even parity, 1 block of binary of arbitrary length, prepend, odd number of 1s", + input: "01010101 10101011", + expectedOutput: "101010101 10101011", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Even Parity", + "Start", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in even parity, 1 block of binary of arbitrary length, append, odd number of 1s", + input: "01010101 10101011", + expectedOutput: "01010101 101010111", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Even Parity", + "End", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in odd parity, 1 block of binary of arbitrary length, prepend, even number of 1s", + input: "01010101 10101010", + expectedOutput: "101010101 10101010", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Odd Parity", + "Start", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in odd parity, 1 block of binary of arbitrary length, prepend, odd number of 1s", + input: "01010101 10101011", + expectedOutput: "001010101 10101011", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Odd Parity", + "Start", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in odd parity, 1 block of binary of arbitrary length, append, odd number of 1s", + input: "01010101 10101011", + expectedOutput: "01010101 101010110", + recipeConfig: [ + { + "op": "Parity Bit", + "args": [ + "Odd Parity", + "End", + "Encode", + "" + ] + } + ] + }, + { + name: "Parity bit encode in even parity, binary for 'hello world!', prepend to each byte", + input: "hello world!", + expectedOutput: "101101000 001100101 001101100 001101100 001101111 100100000 001110111 001101111 001110010 001101100 101100100 000100001", + recipeConfig: [ + { + "op": "To Binary", + "args": ["Space"] + }, + { + "op": "Parity Bit", + "args": [ + "Even Parity", + "Start", + "Encode", + " " + ] + } + ] + }, + { + name: "Parity bit encode in odd parity, binary for 'hello world!', append to each byte", + input: "hello world!", + expectedOutput: "011010000 011001011 011011001 011011001 011011111 001000000 011101111 011011111 011100101 011011001 011001000 001000011", + recipeConfig: [ + { + "op": "To Binary", + "args": ["Space"] + }, + { + "op": "Parity Bit", + "args": [ + "Odd Parity", + "End", + "Encode", + " " + ] + } + ] + }, +]); From 150e66fc99d9e14aeac82af261beac07c051fd15 Mon Sep 17 00:00:00 2001 From: j83305 Date: Sun, 31 May 2020 15:52:32 +0100 Subject: [PATCH 2/4] [#913] added groupings to hex dump --- src/core/operations/ToHexdump.mjs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/core/operations/ToHexdump.mjs b/src/core/operations/ToHexdump.mjs index ffe7131c..54cbc607 100644 --- a/src/core/operations/ToHexdump.mjs +++ b/src/core/operations/ToHexdump.mjs @@ -5,6 +5,7 @@ */ import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import Utils from "../Utils.mjs"; /** @@ -39,6 +40,11 @@ class ToHexdump extends Operation { "name": "Include final length", "type": "boolean", "value": false + }, + { + "name": "Output grouping", + "type": "number", + "value": 1 } ]; } @@ -52,13 +58,20 @@ class ToHexdump extends Operation { const data = new Uint8Array(input); const [length, upperCase, includeFinalLength] = args; const padding = 2; + const groupSize = args[3] || 1; + if (length%groupSize !== 0){ + throw new OperationError("The value of the width parameter must be divisible by the value of the output grouping parameter"); + } let output = ""; for (let i = 0; i < data.length; i += length) { const buff = data.slice(i, i+length); let hexa = ""; for (let j = 0; j < buff.length; j++) { - hexa += Utils.hex(buff[j], padding) + " "; + hexa += Utils.hex(buff[j], padding); + if (groupSize === 1 || (j > 0 && (j+1)%groupSize === 0)){ + hexa += " "; + } } let lineNo = Utils.hex(i, 8); @@ -69,7 +82,7 @@ class ToHexdump extends Operation { } output += lineNo + " " + - hexa.padEnd(length*(padding+1), " ") + + hexa.padEnd(length*padding+(length/groupSize), " ") + " |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n"; if (includeFinalLength && i+buff.length === data.length) { From 44cf916f435f8bded303b27ce5e9a3e7309096fc Mon Sep 17 00:00:00 2001 From: j83305 Date: Tue, 2 Jun 2020 11:02:56 +0100 Subject: [PATCH 3/4] Revert "[#913] added groupings to hex dump" and move changes to a new PR This reverts commit 150e66fc99d9e14aeac82af261beac07c051fd15. --- src/core/operations/ToHexdump.mjs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/core/operations/ToHexdump.mjs b/src/core/operations/ToHexdump.mjs index 54cbc607..ffe7131c 100644 --- a/src/core/operations/ToHexdump.mjs +++ b/src/core/operations/ToHexdump.mjs @@ -5,7 +5,6 @@ */ import Operation from "../Operation.mjs"; -import OperationError from "../errors/OperationError.mjs"; import Utils from "../Utils.mjs"; /** @@ -40,11 +39,6 @@ class ToHexdump extends Operation { "name": "Include final length", "type": "boolean", "value": false - }, - { - "name": "Output grouping", - "type": "number", - "value": 1 } ]; } @@ -58,20 +52,13 @@ class ToHexdump extends Operation { const data = new Uint8Array(input); const [length, upperCase, includeFinalLength] = args; const padding = 2; - const groupSize = args[3] || 1; - if (length%groupSize !== 0){ - throw new OperationError("The value of the width parameter must be divisible by the value of the output grouping parameter"); - } let output = ""; for (let i = 0; i < data.length; i += length) { const buff = data.slice(i, i+length); let hexa = ""; for (let j = 0; j < buff.length; j++) { - hexa += Utils.hex(buff[j], padding); - if (groupSize === 1 || (j > 0 && (j+1)%groupSize === 0)){ - hexa += " "; - } + hexa += Utils.hex(buff[j], padding) + " "; } let lineNo = Utils.hex(i, 8); @@ -82,7 +69,7 @@ class ToHexdump extends Operation { } output += lineNo + " " + - hexa.padEnd(length*padding+(length/groupSize), " ") + + hexa.padEnd(length*(padding+1), " ") + " |" + Utils.printable(Utils.byteArrayToChars(buff)).padEnd(buff.length, " ") + "|\n"; if (includeFinalLength && i+buff.length === data.length) { From b842a38c84d560b5b37c16f241ed556683fe61ac Mon Sep 17 00:00:00 2001 From: j83305 Date: Tue, 2 Jun 2020 11:27:29 +0100 Subject: [PATCH 4/4] ran the linter --- src/core/lib/ParityBit.mjs | 29 +++++++++++++++++++---------- src/core/operations/ParityBit.mjs | 25 +++++++++++++++---------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/core/lib/ParityBit.mjs b/src/core/lib/ParityBit.mjs index 342631a1..9675b46c 100644 --- a/src/core/lib/ParityBit.mjs +++ b/src/core/lib/ParityBit.mjs @@ -9,33 +9,42 @@ import OperationError from "../errors/OperationError.mjs"; +/** + * Function to take the user input and encode using the given arguments + * @param input string of binary + * @param args array + */ export function calculateParityBit(input, args) { let count1s = 0; - for (let i = 0; i < input.length; i++){ + for (let i = 0; i < input.length; i++) { const character = input.charAt(i); - if (character === "1"){ + if (character === "1") { count1s++; - } else if (character !== args[3] && character !== "0" && character !== " "){ + } else if (character !== args[3] && character !== "0" && character !== " ") { throw new OperationError("unexpected character encountered: \"" + character + "\""); } } let parityBit = "1"; const flipflop = args[0] === "Even Parity" ? 0 : 1; - if (count1s % 2 === flipflop){ + if (count1s % 2 === flipflop) { parityBit = "0"; } - if (args[1] === "End"){ + if (args[1] === "End") { return input + parityBit; - }else{ + } else { return parityBit + input; } } -// just removes the parity bit to return the original data +/** + * just removes the parity bit to return the original data + * @param input string of binary, encoded + * @param args array + */ export function decodeParityBit(input, args) { - if (args[1] === "End"){ + if (args[1] === "End") { return input.slice(0, -1); - }else{ + } else { return input.slice(1); } -} \ No newline at end of file +} diff --git a/src/core/operations/ParityBit.mjs b/src/core/operations/ParityBit.mjs index a27574f2..c5ac1d1e 100644 --- a/src/core/operations/ParityBit.mjs +++ b/src/core/operations/ParityBit.mjs @@ -63,16 +63,21 @@ class ParityBit extends Operation { * @returns {string} */ run(input, args) { - if (input.length === 0){ + if (input.length === 0) { return input; } + /** + * determines weather to use the encode or decode method based off args[2] + * @param input input to be encoded or decoded + * @param args array + */ const method = (input, args) => args[2] === "Encode" ? calculateParityBit(input, args) : decodeParityBit(input, args); - if (args[3].length > 0){ - let byteStrings = input.split(args[3]); - for (let byteStringsArrayIndex = 0; byteStringsArrayIndex < byteStrings.length; byteStringsArrayIndex++){ + if (args[3].length > 0) { + const byteStrings = input.split(args[3]); + for (let byteStringsArrayIndex = 0; byteStringsArrayIndex < byteStrings.length; byteStringsArrayIndex++) { byteStrings[byteStringsArrayIndex] = method(byteStrings[byteStringsArrayIndex], args); } - return byteStrings.join(args[3]) + return byteStrings.join(args[3]); } return method(input, args); } @@ -87,8 +92,8 @@ class ParityBit extends Operation { * @returns {Object[]} pos */ highlight(pos, args) { - if (args[3].length === 0){ - if (args[1] === "Prepend"){ + if (args[3].length === 0) { + if (args[1] === "Prepend") { pos[0].start += 1; pos[0].end += 1; } @@ -107,9 +112,9 @@ class ParityBit extends Operation { * @returns {Object[]} pos */ highlightReverse(pos, args) { - if (args[3].length === 0){ - if (args[1] === "Prepend"){ - if (pos[0].start > 0){ + if (args[3].length === 0) { + if (args[1] === "Prepend") { + if (pos[0].start > 0) { pos[0].start -= 1; } pos[0].end -= 1;