Skip to content

Commit

Permalink
feat: Add WASM support (target wasm32-unknown-unknown)
Browse files Browse the repository at this point in the history
inspired by zstd-rs (gyscos/zstd-rs#139)
  • Loading branch information
REASY committed Mar 13, 2023
1 parent 3032f37 commit c7678bd
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 3 deletions.
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -10,6 +10,21 @@ A streaming compression/decompression library for rust with bindings to libbz2.
bzip2 = "0.4"
```

## WASM
bzip2-rs can be compiled to WASM. Make sure you added `wasm32-unknown-unknown` target
```bash
rustup target add wasm32-unknown-unknown
```
To build and run WASM example make sure that you working directory in terminal is `bzip2-sys`
### Build WASM target
```bash
cargo build --target wasm32-unknown-unknown --no-default-features --example it_work
```

### Run WASM target using wasmtime
```bash
wasmtime ..\target\wasm32-unknown-unknown\debug\examples\it_work.wasm --invoke test_decompress
```

# License

Expand Down
2 changes: 2 additions & 0 deletions bzip2-sys/Cargo.toml
Expand Up @@ -28,3 +28,5 @@ cc = "1.0"
[features]
# Enable this feature if you want to have a statically linked bzip2
static = []
std = [] # Use std types instead of libc in bindgen

41 changes: 38 additions & 3 deletions bzip2-sys/build.rs
Expand Up @@ -2,7 +2,7 @@ extern crate cc;
extern crate pkg_config;

use std::path::PathBuf;
use std::{env, fs};
use std::{env, fmt, fs};

fn main() {
let mut cfg = cc::Build::new();
Expand All @@ -23,6 +23,29 @@ fn main() {
}
}

// List out the WASM targets that need wasm-shim.
// Note that Emscripten already provides its own C standard library so
// wasm32-unknown-emscripten should not be included here.
// See: https://github.com/gyscos/zstd-rs/pull/209
let need_wasm_shim = env::var("TARGET").map_or(false, |target| {
target == "wasm32-unknown-unknown" || target == "wasm32-wasi"
});

if need_wasm_shim {
cargo_print(&"rerun-if-changed=wasm-shim/stdlib.h");
cargo_print(&"rerun-if-changed=wasm-shim/string.h");

cfg.include("wasm-shim/");
cfg.define("XXH_STATIC_ASSERT", Some("0"));
cfg.opt_level(3);
}
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();

if target_arch == "wasm32" || target_os == "hermit" {
cargo_print(&"rustc-cfg=feature=\"std\"");
}

let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());

cfg.include("bzip2-1.0.8")
Expand All @@ -42,6 +65,18 @@ fn main() {
let include = dst.join("include");
fs::create_dir_all(&include).unwrap();
fs::copy(src.join("bzlib.h"), dst.join("include/bzlib.h")).unwrap();
println!("cargo:root={}", dst.display());
println!("cargo:include={}", dst.join("include").display());
cargo_print(&format_args!("cargo:root={}", dst.display()));
cargo_print(&format_args!(
"cargo:include={}",
dst.join("include").display()
));
}

/// Print a line for cargo.
///
/// If non-cargo is set, do not print anything.
fn cargo_print(content: &dyn fmt::Display) {
if cfg!(not(feature = "non-cargo")) {
println!("cargo:{}", content);
}
}
15 changes: 15 additions & 0 deletions bzip2-sys/bzlib.h
@@ -0,0 +1,15 @@
#ifdef PKG_CONFIG

/* Just use installed headers */
#include <bzlib.h>

#else // #ifdef PKG_CONFIG

#include "bzip2-1.0.8/bzlib.h"

#endif // #ifdef PKG_CONFIG


/* This file is used to generate bindings for both headers.
* Check update_bindings.sh to see how to use it.
* Or use the `bindgen` feature, which will create the bindings automatically. */
35 changes: 35 additions & 0 deletions bzip2-sys/examples/it_work.rs
@@ -0,0 +1,35 @@
#[cfg(feature = "std")]
use std::os::raw::{c_char, c_int, c_uint};

#[cfg(not(feature = "std"))]
use libc::{c_char, c_int, c_uint};

#[no_mangle]
pub extern "C" fn test_decompress() -> bool {
let uncompressed_bytes = include_bytes!("../bzip2-1.0.8/sample1.ref");
let compressed_bytes = include_bytes!("../bzip2-1.0.8/sample1.bz2");
let mut raw: Box<bzip2_sys::bz_stream> = unsafe { Box::new(std::mem::zeroed()) };
let mut buf: [u8; 100352] = [0; 98 * 1024];
unsafe {
assert_eq!(bzip2_sys::BZ2_bzDecompressInit(&mut *raw, 0, 0 as c_int), 0);
raw.next_in = compressed_bytes.as_ptr() as *mut c_char;
raw.avail_in = compressed_bytes.len().min(c_uint::MAX as usize) as c_uint;

raw.next_out = buf.as_mut_ptr() as *mut c_char;
raw.avail_out = buf.len() as c_uint;
assert_eq!(
bzip2_sys::BZ2_bzDecompress(&mut *raw),
bzip2_sys::BZ_STREAM_END
);
bzip2_sys::BZ2_bzDecompressEnd(&mut *raw);
};
let total_out = ((raw.total_out_lo32 as u64) | ((raw.total_out_hi32 as u64) << 32)) as usize;
assert_eq!(total_out, uncompressed_bytes.len());

let slice: &[u8] = buf[0..total_out].as_ref();
assert_eq!(uncompressed_bytes, slice);

return true;
}

fn main() {}
7 changes: 7 additions & 0 deletions bzip2-sys/lib.rs
Expand Up @@ -2,6 +2,13 @@

extern crate libc;

#[cfg(target_arch = "wasm32")]
mod wasm_shim;

#[cfg(feature = "std")]
use std::os::raw::{c_char, c_int, c_uint, c_void};

#[cfg(not(feature = "std"))]
use libc::{c_char, c_int, c_uint, c_void};

pub const BZ_RUN: c_int = 0;
Expand Down
22 changes: 22 additions & 0 deletions bzip2-sys/wasm-shim/stdlib.h
@@ -0,0 +1,22 @@
#include <stddef.h>

#ifndef _STDLIB_H
#define _STDLIB_H 1

void *rust_zstd_wasm_shim_malloc(size_t size);
void *rust_zstd_wasm_shim_calloc(size_t nmemb, size_t size);
void rust_zstd_wasm_shim_free(void *ptr);

inline void *malloc(size_t size) {
return rust_zstd_wasm_shim_malloc(size);
}

inline void *calloc(size_t nmemb, size_t size) {
return rust_zstd_wasm_shim_calloc(nmemb, size);
}

inline void free(void *ptr) {
rust_zstd_wasm_shim_free(ptr);
}

#endif // _STDLIB_H
22 changes: 22 additions & 0 deletions bzip2-sys/wasm-shim/string.h
@@ -0,0 +1,22 @@
#include <stdlib.h>

#ifndef _STRING_H
#define _STRING_H 1

void *rust_zstd_wasm_shim_memcpy(void *restrict dest, const void *restrict src, size_t n);
void *rust_zstd_wasm_shim_memmove(void *dest, const void *src, size_t n);
void *rust_zstd_wasm_shim_memset(void *dest, int c, size_t n);

inline void *memcpy(void *restrict dest, const void *restrict src, size_t n) {
return rust_zstd_wasm_shim_memcpy(dest, src, n);
}

inline void *memmove(void *dest, const void *src, size_t n) {
return rust_zstd_wasm_shim_memmove(dest, src, n);
}

inline void *memset(void *dest, int c, size_t n) {
return rust_zstd_wasm_shim_memset(dest, c, n);
}

#endif // _STRING_H
55 changes: 55 additions & 0 deletions bzip2-sys/wasm_shim.rs
@@ -0,0 +1,55 @@
use std::alloc::{alloc, dealloc, Layout};
use std::os::raw::{c_int, c_void};

#[no_mangle]
pub extern "C" fn rust_zstd_wasm_shim_malloc(size: usize) -> *mut c_void {
unsafe {
let layout = Layout::from_size_align_unchecked(size, 1);
alloc(layout).cast()
}
}

#[no_mangle]
pub extern "C" fn rust_zstd_wasm_shim_calloc(nmemb: usize, size: usize) -> *mut c_void {
unsafe {
let layout = Layout::from_size_align_unchecked(size * nmemb, 1);
alloc(layout).cast()
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_zstd_wasm_shim_free(ptr: *mut c_void) {
// layout is not actually used
let layout = Layout::from_size_align_unchecked(1, 1);
dealloc(ptr.cast(), layout);
}

#[no_mangle]
pub unsafe extern "C" fn rust_zstd_wasm_shim_memcpy(
dest: *mut c_void,
src: *const c_void,
n: usize,
) -> *mut c_void {
std::ptr::copy_nonoverlapping(src as *const u8, dest as *mut u8, n);
dest
}

#[no_mangle]
pub unsafe extern "C" fn rust_zstd_wasm_shim_memmove(
dest: *mut c_void,
src: *const c_void,
n: usize,
) -> *mut c_void {
std::ptr::copy(src as *const u8, dest as *mut u8, n);
dest
}

#[no_mangle]
pub unsafe extern "C" fn rust_zstd_wasm_shim_memset(
dest: *mut c_void,
c: c_int,
n: usize,
) -> *mut c_void {
std::ptr::write_bytes(dest as *mut u8, c as u8, n);
dest
}

0 comments on commit c7678bd

Please sign in to comment.