From 59fe8d1c4b533e3e80fa318b4672f7e76e353eb2 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 25 Nov 2022 11:50:27 +0000 Subject: [PATCH] Simplified 'Shuffle' operation to work in the same way as 'Sort' and 'Unique' --- src/core/operations/Shuffle.mjs | 107 ++++------------------------- tests/operations/tests/Shuffle.mjs | 46 ++----------- 2 files changed, 18 insertions(+), 135 deletions(-) diff --git a/src/core/operations/Shuffle.mjs b/src/core/operations/Shuffle.mjs index 99cbd072..14c4ffab 100644 --- a/src/core/operations/Shuffle.mjs +++ b/src/core/operations/Shuffle.mjs @@ -5,6 +5,8 @@ */ import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Shuffle operation @@ -21,34 +23,25 @@ class Shuffle extends Operation { this.module = "Default"; this.description = "Randomly reorders input elements."; this.infoURL = "https://wikipedia.org/wiki/Shuffling"; - this.inputType = "ArrayBuffer"; - this.outputType = "ArrayBuffer"; + this.inputType = "string"; + this.outputType = "string"; this.args = [ { - "name": "By", - "type": "option", - "value": ["Byte", "Character", "Line"], - "defaultIndex": 1 + name: "Delimiter", + type: "option", + value: INPUT_DELIM_OPTIONS } ]; } /** - * @param {ArrayBuffer} input + * @param {string} input * @param {Object[]} args - * @returns {ArrayBuffer} + * @returns {string} */ run(input, args) { - const type = args[0]; - if (input.byteLength === 0) return input; - if (ArrayBuffer.isView(input)) { - if (input.byteOffset === 0 && input.byteLength === input.buffer.byteLength) { - input = input.buffer; - } else { - input = input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength); - } - } - const inputBytes = new Uint8Array(input); + const delim = Utils.charRep(args[0]); + if (input.length === 0) return input; // return a random number in [0, 1) const rng = (typeof crypto) !== "undefined" && crypto.getRandomValues ? (function() { @@ -66,71 +59,10 @@ class Shuffle extends Operation { return Math.floor(rng() * max); }; - const toShuffle = []; - let addLastNewLine = false; - switch (type) { - case "Character": - // split input into UTF-8 code points - for (let i = 0; i < inputBytes.length;) { - const charLength = (function() { - if (inputBytes[i] < 0xc0) return 1; - if (inputBytes[i] < 0xe0) return 2; - if (inputBytes[i] < 0xf0) return 3; - if (inputBytes[i] < 0xf8) return 4; - return 1; - })(); - if (i + charLength <= inputBytes.length) { - let elementLength = charLength; - for (let j = 1; j < charLength; j++) { - if ((inputBytes[i + j] & 0xc0) !== 0x80) { - elementLength = 1; - break; - } - } - toShuffle.push([i, elementLength]); - i += elementLength; - } else { - toShuffle.push([i, 1]); - i++; - } - } - break; - case "Line": - { - // split input by newline characters - let lineBegin = 0; - for (let i = 0; i < inputBytes.length; i++) { - if (inputBytes[i] === 0xd || inputBytes[i] === 0xa) { - if (i + 1 < inputBytes.length && inputBytes[i] === 0xd && inputBytes[i + 1] === 0xa) { - i++; - } - toShuffle.push([lineBegin, i - lineBegin + 1]); - lineBegin = i + 1; - } - } - if (lineBegin < inputBytes.length) { - toShuffle.push([lineBegin, inputBytes.length - lineBegin]); - addLastNewLine = true; - } - } - break; - default: - { - // Creating element information for each bytes looks very wasteful. - // Therefore, directly shuffle here. - const outputBytes = new Uint8Array(inputBytes); - for (let i = outputBytes.length - 1; i > 0; i--) { - const idx = randint(i + 1); - const tmp = outputBytes[idx]; - outputBytes[idx] = outputBytes[i]; - outputBytes[i] = tmp; - } - return outputBytes.buffer; - } - } + // Split input into shuffleable sections + const toShuffle = input.split(delim); // shuffle elements - const lastStart = toShuffle[toShuffle.length - 1][0]; for (let i = toShuffle.length - 1; i > 0; i--) { const idx = randint(i + 1); const tmp = toShuffle[idx]; @@ -138,18 +70,7 @@ class Shuffle extends Operation { toShuffle[i] = tmp; } - // place shuffled elements - const outputBytes = new Uint8Array(inputBytes.length + (addLastNewLine ? 1 : 0)); - let outputPos = 0; - for (let i = 0; i < toShuffle.length; i++) { - outputBytes.set(new Uint8Array(input, toShuffle[i][0], toShuffle[i][1]), outputPos); - outputPos += toShuffle[i][1]; - if (addLastNewLine && toShuffle[i][0] === lastStart) { - outputBytes[outputPos] = 0xa; - outputPos++; - } - } - return outputBytes.buffer; + return toShuffle.join(delim); } } diff --git a/tests/operations/tests/Shuffle.mjs b/tests/operations/tests/Shuffle.mjs index eadc615e..21c878f1 100644 --- a/tests/operations/tests/Shuffle.mjs +++ b/tests/operations/tests/Shuffle.mjs @@ -13,7 +13,7 @@ TestRegister.addTests([ "recipeConfig": [ { "op": "Shuffle", - "args": ["Character"] + "args": ["Comma"] } ] }, @@ -24,7 +24,7 @@ TestRegister.addTests([ "recipeConfig": [ { "op": "Shuffle", - "args": ["Byte"] + "args": ["Nothing (separate chars)"] }, { "op": "To Hex", @@ -36,29 +36,6 @@ TestRegister.addTests([ } ] }, - { - "name": "Shuffle characters", - "input": "1234\uff15\uff16\uff17\uff18", - "expectedOutput": " 0031 0032 0033 0034 FF15 FF16 FF17 FF18", - "recipeConfig": [ - { - "op": "Shuffle", - "args": ["Character"] - }, - { - "op": "Escape Unicode Characters", - "args": ["%u", true, 4, true] - }, - { - "op": "Split", - "args": ["%u", " "] - }, - { - "op": "Sort", - "args": ["Space", false, "Alphabetical (case sensitive)"] - } - ] - }, { "name": "Shuffle lines", "input": "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf\n", @@ -66,27 +43,12 @@ TestRegister.addTests([ "recipeConfig": [ { "op": "Shuffle", - "args": ["Line"] + "args": ["Line feed"] }, { "op": "Sort", "args": ["Line feed", false, "Alphabetical (case sensitive)"] } ] - }, - { - "name": "Shuffle lines (last character is not newline)", - "input": "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf", - "expectedOutput": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf", - "recipeConfig": [ - { - "op": "Shuffle", - "args": ["Line"] - }, - { - "op": "Sort", - "args": ["Line feed", false, "Alphabetical (case sensitive)"] - } - ] - }, + } ]);