Added Zstd Compress / Zstd Decompress operations.
This commit is contained in:
parent
1eccfa729b
commit
8bb4748c5e
7
package-lock.json
generated
7
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@astronautlabs/amf": "^0.0.6",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@blu3r4y/lzma": "^2.3.3",
|
||||
"@bokuweb/zstd-wasm": "^0.0.27",
|
||||
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
||||
"@xmldom/xmldom": "^0.8.11",
|
||||
"argon2-browser": "^1.18.0",
|
||||
@ -1838,6 +1839,12 @@
|
||||
"lzma.js": "bin/lzma.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@bokuweb/zstd-wasm": {
|
||||
"version": "0.0.27",
|
||||
"resolved": "https://registry.npmjs.org/@bokuweb/zstd-wasm/-/zstd-wasm-0.0.27.tgz",
|
||||
"integrity": "sha512-GDm2uOTK3ESjnYmSeLQifJnBsRCWajKLvN32D2ZcQaaCIJI/Hse9s74f7APXjHit95S10UImsRGkTsbwHmrtmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.2.tgz",
|
||||
|
||||
@ -99,6 +99,7 @@
|
||||
"@astronautlabs/amf": "^0.0.6",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@blu3r4y/lzma": "^2.3.3",
|
||||
"@bokuweb/zstd-wasm": "^0.0.27",
|
||||
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
||||
"@xmldom/xmldom": "^0.8.11",
|
||||
"argon2-browser": "^1.18.0",
|
||||
|
||||
@ -402,7 +402,9 @@
|
||||
"LZMA Compress",
|
||||
"LZ4 Decompress",
|
||||
"LZ4 Compress",
|
||||
"LZNT1 Decompress"
|
||||
"LZNT1 Decompress",
|
||||
"Zstd Compress",
|
||||
"Zstd Decompress"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
30
src/core/lib/Zstd.mjs
Normal file
30
src/core/lib/Zstd.mjs
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Zstd shared initialisation.
|
||||
*
|
||||
* Both ZstdCompress and ZstdDecompress import from here so that
|
||||
* WebAssembly.instantiate is called exactly once, regardless of how many
|
||||
* operations use it or in what order they run.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { init, compress, decompress } from "@bokuweb/zstd-wasm";
|
||||
|
||||
let initPromise = null;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves once the Zstd WASM module is ready.
|
||||
* Safe to call multiple times — the module is only instantiated once.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function zstdInit() {
|
||||
if (!initPromise) {
|
||||
initPromise = init();
|
||||
}
|
||||
return initPromise;
|
||||
}
|
||||
|
||||
export { compress, decompress };
|
||||
61
src/core/operations/ZstdCompress.mjs
Normal file
61
src/core/operations/ZstdCompress.mjs
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @author Leon Zandman [leon@wirwar.com]
|
||||
* @copyright Crown Copyright 2027
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { zstdInit, compress } from "../lib/Zstd.mjs";
|
||||
|
||||
/**
|
||||
* Zstd Compress operation
|
||||
*/
|
||||
class ZstdCompress extends Operation {
|
||||
|
||||
/**
|
||||
* ZstdCompress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Zstd Compress";
|
||||
this.module = "Compression";
|
||||
this.description = "Compresses data using the Zstandard (Zstd) algorithm. Zstd offers high compression ratios at fast speeds and is widely used in Linux, databases, container images, and network protocols.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Zstandard";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Compression level",
|
||||
type: "number",
|
||||
value: 3,
|
||||
min: 1,
|
||||
max: 22
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [level] = args;
|
||||
if (input.byteLength === 0) throw new OperationError("Please provide an input.");
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Zstd...");
|
||||
await zstdInit();
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Compressing data...");
|
||||
try {
|
||||
const result = compress(new Uint8Array(input), level);
|
||||
return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Failed to compress: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ZstdCompress;
|
||||
59
src/core/operations/ZstdDecompress.mjs
Normal file
59
src/core/operations/ZstdDecompress.mjs
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @author Leon Zandman [leon@wirwar.com]
|
||||
* @copyright Crown Copyright 2027
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { zstdInit, decompress } from "../lib/Zstd.mjs";
|
||||
|
||||
/**
|
||||
* Zstd Decompress operation
|
||||
*/
|
||||
class ZstdDecompress extends Operation {
|
||||
|
||||
/**
|
||||
* ZstdDecompress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Zstd Decompress";
|
||||
this.module = "Compression";
|
||||
this.description = "Decompresses data compressed with the Zstandard (Zstd) algorithm.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Zstandard";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\x28\\xb5\\x2f\\xfd",
|
||||
flags: "",
|
||||
args: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (input.byteLength === 0) throw new OperationError("Please provide an input.");
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Zstd...");
|
||||
await zstdInit();
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Decompressing data...");
|
||||
try {
|
||||
const result = decompress(new Uint8Array(input));
|
||||
return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Failed to decompress: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ZstdDecompress;
|
||||
@ -182,6 +182,7 @@ import "./tests/JSONtoYAML.mjs";
|
||||
import "./tests/YARA.mjs";
|
||||
import "./tests/ParseCSR.mjs";
|
||||
import "./tests/XXTEA.mjs";
|
||||
import "./tests/Zstd.mjs";
|
||||
|
||||
const testStatus = {
|
||||
allTestsPassing: true,
|
||||
|
||||
80
tests/operations/tests/Zstd.mjs
Normal file
80
tests/operations/tests/Zstd.mjs
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Zstd tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Zstd compress & decompress: string",
|
||||
input: "The cat sat on the mat.",
|
||||
expectedOutput: "The cat sat on the mat.",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Zstd Compress",
|
||||
args: [3]
|
||||
},
|
||||
{
|
||||
op: "Zstd Decompress",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// Generated using: node --input-type=module -e "import {init,compress} from '@bokuweb/zstd-wasm'; await init(); const r=compress(new TextEncoder().encode('The cat sat on the mat.'),3); console.log(Buffer.from(r).toString('hex'));"
|
||||
name: "Zstd compress: level 3",
|
||||
input: "The cat sat on the mat.",
|
||||
expectedOutput: "28b52ffd2017b900005468652063617420736174206f6e20746865206d61742e",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Zstd Compress",
|
||||
args: [3]
|
||||
},
|
||||
{
|
||||
op: "To Hex",
|
||||
args: ["None", 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// Generated using: node --input-type=module -e "import {init,compress} from '@bokuweb/zstd-wasm'; await init(); const r=compress(new TextEncoder().encode('The cat sat on the mat.'),3); console.log(Buffer.from(r).toString('hex'));"
|
||||
name: "Zstd decompress: known vector",
|
||||
input: "28b52ffd2017b900005468652063617420736174206f6e20746865206d61742e",
|
||||
expectedOutput: "The cat sat on the mat.",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Zstd Decompress",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Zstd compress: empty input error",
|
||||
input: "",
|
||||
expectedOutput: "Please provide an input.",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Zstd Compress",
|
||||
args: [3]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Zstd decompress: empty input error",
|
||||
input: "",
|
||||
expectedOutput: "Please provide an input.",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Zstd Decompress",
|
||||
args: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
Loading…
x
Reference in New Issue
Block a user