OxiArc
ZIP, TAR — and LZH, the archive format of 1990s Japan. List what is inside, build new archives, and race six codecs against each other, all in your browser. Nothing leaves the page.
Choosing files, building archives, and exporting make no network requests.
Inspect an archive
Drop an archive and the page detects its format by magic bytes and lists what is inside. The listing below is the bundled sample — an LZH archive written by oxiarc itself, with entry dates fixed to 1995. Its filenames are recorded in Shift_JIS, the LZH convention: a tool that only knows UTF-8 shows them as mojibake, and this page decodes them correctly.
Create an archive
Choose files and assemble a ZIP or TAR.GZ on the spot, then download the result. Timestamps are stamped by this page's clock: there is no clock inside wasm (SystemTime traps on wasm32-unknown-unknown), so JavaScript hands the time to oxiarc's explicit-mtime API.
No files yet. Use "Add files" above.
Codec lab
One input, six codecs: compress, decompress, then verify the bytes came back identical — with both directions timed. The default input is the bundled Japanese text on archive history (—); you can also race your own file (up to 4 MB).
| Codec | Compressed | Ratio | Compress | Decompress | Verified |
|---|---|---|---|---|---|
| gzip (6) | — | — | |||
| zstd (3) | — | — | |||
| brotli (5) | — | — | |||
| lz4 | — | — | |||
| bzip2 (9) | — | — | |||
| lzma | — | — | |||
Times are measured in the worker with performance.now() around the wasm calls alone. The numbers are re-measured on the spot, every run.
What this demo refuses
- 7z / CAB / ISO 9660 — detected and named, not listed or extracted.
- Password-protected ZIP — lists fine; extraction is refused.
- XZ — decode only; the write half is not linked.
- Snappy — detected only; not in the codec race.
- Writing LZH — the create station builds ZIP / TAR.GZ only. The bundled sample was written with oxiarc's LZH writer on the host, where a clock exists.
| oxiarc | v— (Pure Rust, Apache-2.0) |
|---|---|
| target | wasm32-unknown-unknown |
| wasm size | 779 KB (gzip 411 KB) |
| sample entries | — |
| external requests | — |
| server round-trips | 0 |
| C / C++ / Fortran | 0 bytes |
External requests since the page finished loading: —
Counting starts once the bundled sample archive has loaded. Choosing files, building archives, and exporting do not move this number. You can confirm it in your dev tools' Network tab.
Implementation code
// crates/oxiarc-wasm/src/create.rs:106-148 — verbatim, the code running above
pub fn finish(&mut self, level: u8) -> Result<Vec<u8>, OxiArcWasmError> {
let level = level.clamp(1, 9);
let mut out = Vec::new();
let mut writer = oxiarc_archive::zip::write_zip(&mut out);
for file in &self.files {
// Same store-if-smaller policy as ZipWriter::add_file_with_options,
// reimplemented because only add_file_raw takes an explicit mtime.
let compressed = oxiarc_deflate::deflate(&file.data, level).map_err(|e| {
OxiArcWasmError::CodecFailed {
codec: "deflate".to_owned(),
detail: e.to_string(),
}
})?;
let (payload, method) = if compressed.len() < file.data.len() {
(compressed, ZipMethod::Deflate)
} else {
(file.data.clone(), ZipMethod::Stored)
};
let crc = Crc32::compute(&file.data);
writer
.add_file_raw(
&file.name,
method,
crc,
file.data.len() as u64,
Some(file.mtime),
&payload,
)
.map_err(|e| OxiArcWasmError::ArchiveInvalid {
format: "zip".to_owned(),
detail: e.to_string(),
})?;
}
writer
.finish()
.map_err(|e| OxiArcWasmError::ArchiveInvalid {
format: "zip".to_owned(),
detail: e.to_string(),
})?;
drop(writer);
self.files.clear();
Ok(out)
} This is the code running above, right now.