feat: add wasm bindings
Some checks are pending
CI / Build and test : macOS-latest, msrv (push) Waiting to run
CI / Build and test : macOS-latest, nightly (push) Waiting to run
CI / Build and test : macOS-latest, stable (push) Waiting to run
CI / Build and test : ubuntu-latest, msrv (push) Waiting to run
CI / Build and test : ubuntu-latest, nightly (push) Waiting to run
CI / Build and test : ubuntu-latest, stable (push) Waiting to run
CI / Build and test : windows-latest, msrv (push) Waiting to run
CI / Build and test : windows-latest, nightly (push) Waiting to run
CI / Build and test : windows-latest, stable (push) Waiting to run
CI / Build and test --all-features: macOS-latest, msrv (push) Waiting to run
CI / Build and test --all-features: macOS-latest, nightly (push) Waiting to run
CI / Build and test --all-features: macOS-latest, stable (push) Waiting to run
CI / Build and test --all-features: ubuntu-latest, msrv (push) Waiting to run
CI / Build and test --all-features: ubuntu-latest, nightly (push) Waiting to run
CI / Build and test --all-features: ubuntu-latest, stable (push) Waiting to run
CI / Build and test --all-features: windows-latest, msrv (push) Waiting to run
CI / Build and test --all-features: windows-latest, nightly (push) Waiting to run
CI / Build and test --all-features: windows-latest, stable (push) Waiting to run
CI / Build and test --no-default-features: macOS-latest, msrv (push) Waiting to run
CI / Build and test --no-default-features: macOS-latest, nightly (push) Waiting to run
CI / Build and test --no-default-features: macOS-latest, stable (push) Waiting to run
CI / Build and test --no-default-features: ubuntu-latest, msrv (push) Waiting to run
CI / Build and test --no-default-features: ubuntu-latest, nightly (push) Waiting to run
CI / Build and test --no-default-features: ubuntu-latest, stable (push) Waiting to run
CI / Build and test --no-default-features: windows-latest, msrv (push) Waiting to run
CI / Build and test --no-default-features: windows-latest, nightly (push) Waiting to run
CI / Build and test --no-default-features: windows-latest, stable (push) Waiting to run
CI / cargo_fmt (push) Waiting to run
CI / check_minimal_versions (push) Waiting to run
CI / style_and_docs () (push) Waiting to run
CI / style_and_docs (--all-features) (push) Waiting to run
CI / style_and_docs (--no-default-features) (push) Waiting to run
CI / fuzz_read (push) Blocked by required conditions
CI / fuzz_read_with_no_features (push) Blocked by required conditions
CI / fuzz_write (push) Blocked by required conditions
CI / fuzz_write_with_no_features (push) Blocked by required conditions
DevSkim / DevSkim (push) Waiting to run
Release / Release-plz (push) Waiting to run

This commit is contained in:
Erica Marigold 2024-12-26 15:22:35 +00:00
parent e074e09b83
commit 473b156e1f
7 changed files with 115 additions and 2 deletions

5
.gitignore vendored
View file

@ -4,3 +4,8 @@ target
\.idea/
/fuzz_read/out/
/fuzz_write/out/
## wasm start ##
pkg/
extracted/
## wasm end ##

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "wasynth"]
path = wasynth
url = https://github.com/Rerumu/Wasynth.git

View file

@ -18,6 +18,9 @@ edition = "2021"
exclude = ["tests/**", "examples/**", ".github/**", "fuzz_read/**", "fuzz_write/**"]
build = "src/build.rs"
[lib]
crate-type = ["cdylib"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
@ -26,8 +29,16 @@ rustdoc-args = ["--cfg", "docsrs"]
time = { version = "0.3.1", default-features = false }
[dependencies]
## wasm dependencies start ##
wasm-bindgen = { version = "0.2" }
js-sys = { version = "0.3" }
getrandom = { version = "0.2", features = ["js"] }
bzip2 = { path = "./bzip2-rs", optional = true, default-features = false, features = ["libbz2-rs-sys"] }
zstd = { version = "0.13", optional = true, default-features = false, features = ["wasm"] }
## wasm dependencies end ##
aes = { version = "0.8", optional = true }
bzip2 = { version = "0.4.3", optional = true }
# bzip2 = { version = "0.4.3", optional = true }
chrono = { version = "0.4", optional = true }
constant_time_eq = { version = "0.3", optional = true }
crc32fast = "1.4"
@ -44,7 +55,7 @@ time = { workspace = true, optional = true, features = [
"std",
] }
zeroize = { version = "1.8", optional = true, features = ["zeroize_derive"] }
zstd = { version = "0.13", optional = true, default-features = false }
# zstd = { version = "0.13", optional = true, default-features = false }
zopfli = { version = "0.8", optional = true }
deflate64 = { version = "0.1.9", optional = true }
lzma-rs = { version = "0.3", default-features = false, optional = true }
@ -101,3 +112,4 @@ harness = false
[[bench]]
name = "merge_archive"
harness = false

1
bzip2-rs Submodule

@ -0,0 +1 @@
Subproject commit 15258feb0cdc1114d6fc6b936254c4f4e1d5730c

View file

@ -31,6 +31,7 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs)]
#![allow(unexpected_cfgs)] // Needed for cfg(fuzzing) on nightly as of 2024-05-06
pub use crate::compression::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
pub use crate::read::HasZipMetadata;
pub use crate::read::ZipArchive;
@ -67,3 +68,74 @@ zip = \"="]
#[doc = "\"\n\
```"]
pub mod unstable;
// ====== wasm start ====== //
use std::io::{Cursor, Read};
use result::{ZipResult, ZipError};
use wasm_bindgen::{prelude::*, JsValue};
use js_sys::{Array, Object, Error, Reflect::set};
//
// Utilities
//
impl From<ZipError> for JsValue {
fn from(err: ZipError) -> JsValue {
match err {
ZipError::Io(e) => JsValue::from(Error::new(&format!("IO Error: {}", e))),
ZipError::InvalidArchive(msg) => JsValue::from(Error::new(&format!("Invalid Archive: {}", msg))),
ZipError::UnsupportedArchive(msg) => JsValue::from(Error::new(&format!("Unsupported Archive: {}", msg))),
ZipError::FileNotFound => JsValue::from(Error::new("File Not Found")),
ZipError::InvalidPassword => JsValue::from(Error::new("Invalid Password")),
}
}
}
/// Converts a Rust `Vec` to a JS Array.
fn vec_to_js_array<T: Into<JsValue>>(vec: Vec<T>) -> Array {
let js_array = Array::new();
for item in vec {
js_array.push(&item.into());
}
js_array
}
//
// Exports
//
/// Unzips a file provided as a JS array, returning a mapping of filenames to decompressed
/// contents.
#[wasm_bindgen]
pub fn unzip(bytes: &[u8]) -> ZipResult<JsValue> {
// TODO: Accept Uint8Array instead of array
let cursor = Cursor::new(bytes);
let mut archive = ZipArchive::new(cursor)?;
let extracted_files = Object::new();
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let file_name = file.name().to_string();
// Read the file contents into a Vec<u8>
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
// Convert to JS values and insert into object
let js_file_name = JsValue::from(file_name);
let js_contents = vec_to_js_array(contents);
set(&extracted_files, &js_file_name, &js_contents).unwrap();
}
Ok(JsValue::from(&extracted_files))
}
// ====== wasm end ====== //

19
test.ts Normal file
View file

@ -0,0 +1,19 @@
import * as path from "jsr:@std/path";
import { ensureDir } from "jsr:@std/fs";
import { unzip } from "./pkg/zip.js";
const EXTRACTED_DIR = "./extracted";
await ensureDir(EXTRACTED_DIR);
const TARGET_PATH = Deno.args[0];
if (!TARGET_PATH) {
console.error("usage: test.js <path>");
Deno.exit(1);
}
const zipFile = Deno.readFileSync(TARGET_PATH);
for (const [file, contents] of Object.entries(unzip([...zipFile]))) {
// TODO: handle directories & more in the future
const contentBytes = new TextEncoder().encode(contents);
Deno.writeFileSync(path.join(EXTRACTED_DIR, file), contentBytes);
}

1
wasynth Submodule

@ -0,0 +1 @@
Subproject commit 27f34987341ec1837d3ff911f3e583647920af16