Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import textToShellHandler from "./texttoshell.ts";
import batchHandler from "./batch.ts";
import bsorHandler from "./bsor.ts";
import fontHandler from "./font.ts";
import zstdHandler from "./zstd.ts";

const handlers: FormatHandler[] = [];
try { handlers.push(new svgTraceHandler()) } catch (_) { };
Expand Down Expand Up @@ -85,5 +86,6 @@ try { handlers.push(new textToShellHandler()) } catch (_) { };
try { handlers.push(new batchHandler()) } catch (_) { };
try { handlers.push(new bsorHandler()) } catch (_) { };
try { handlers.push(new fontHandler()) } catch (_) { };
try { handlers.push(new zstdHandler()) } catch (_) { };

export default handlers;
54 changes: 54 additions & 0 deletions src/handlers/zstd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import { ZSTDDecoder } from "three/examples/jsm/libs/zstddec.module.js";

const decoder = new ZSTDDecoder();

class zstdHandler implements FormatHandler {
public name = "zstd";

public supportedFormats: FileFormat[] = [
{
name: "Zstandard Compressed Data",
format: "zst",
extension: "zst",
mime: "application/zstd",
from: true,
to: false,
internal: "zstd",
category: "archive",
lossless: true
},
{
name: "Raw Binary Data",
format: "bin",
extension: "bin",
mime: "application/octet-stream",
from: false,
to: true,
internal: "raw",
category: "data",
lossless: true
}
];

public ready = false;

async init() {
await decoder.init();
this.ready = true;
}

async doConvert(inputFiles: FileData[], inputFormat: FileFormat, outputFormat: FileFormat): Promise<FileData[]> {
if (inputFormat.internal === "zstd" && outputFormat.internal === "raw") {
return inputFiles.map((inputFile) => {
const bytes = decoder.decode(inputFile.bytes);
const name = inputFile.name.replace(/\.(zst|zstd)$/i, "") || `${inputFile.name}.bin`;
return { name, bytes };
});
}

throw "Invalid conversion path.";
}
}

export default zstdHandler;
2 changes: 2 additions & 0 deletions src/normalizeMimeType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ function normalizeMimeType (mime: string) {
case "audio/x-wav": return "audio/wav";
case "audio/vnd.wave": return "audio/wav";
case "application/x-gzip": return "application/gzip";
case "application/x-zstd": return "application/zstd";
case "application/zst": return "application/zstd";
case "image/x-icon": return "image/vnd.microsoft.icon";
case "image/vtf": return "image/x-vtf";
case "image/qoi": return "image/x-qoi";
Expand Down
41 changes: 41 additions & 0 deletions test/zstd.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, test } from "bun:test";
import zstdHandler from "../src/handlers/zstd.ts";
import type { FileFormat } from "../src/FormatHandler.ts";

const zstdFormat: FileFormat = {
name: "Zstandard Compressed Data",
format: "zst",
extension: "zst",
mime: "application/zstd",
from: true,
to: false,
internal: "zstd"
};

const rawFormat: FileFormat = {
name: "Raw Binary Data",
format: "bin",
extension: "bin",
mime: "application/octet-stream",
from: false,
to: true,
internal: "raw"
};

test("zstd handler decodes .zst to raw bytes", async () => {
const input = new TextEncoder().encode("hello zstd");
const compressed = Bun.zstdCompressSync(input);

const handler = new zstdHandler();
await handler.init();

const output = await handler.doConvert(
[{ name: "sample.zst", bytes: new Uint8Array(compressed) }],
zstdFormat,
rawFormat
);

expect(output).toHaveLength(1);
expect(output[0].name).toBe("sample");
expect(new TextDecoder().decode(output[0].bytes)).toBe("hello zstd");
});