Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASM support #100

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ categories = ["compression", "api-bindings"]
[workspace]

[dependencies]
libc = "0.2"
bzip2-sys = { version = "0.1.11", path = "bzip2-sys" }
tokio-io = { version = "0.1", optional = true }
futures = { version = "0.1", optional = true }
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
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
3 changes: 0 additions & 3 deletions bzip2-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ categories = ["external-ffi-bindings"]
name = "bzip2_sys"
path = "lib.rs"

[dependencies]
libc = "0.2"

[build-dependencies]
pkg-config = "0.3.9"
cc = "1.0"
Expand Down
39 changes: 36 additions & 3 deletions bzip2-sys/build.rs
Original file line number Diff line number Diff line change
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,27 @@ 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");

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.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 +63,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);
}
}
31 changes: 31 additions & 0 deletions bzip2-sys/examples/it_work.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::os::raw::{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() {}
9 changes: 7 additions & 2 deletions bzip2-sys/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
#![doc(html_root_url = "https://docs.rs/bzip2-sys/0.1")]

extern crate libc;
#[cfg(all(
target_arch = "wasm32",
target_vendor = "unknown",
target_os = "unknown"
))]
mod wasm_shim;

use libc::{c_char, c_int, c_uint, c_void};
use std::os::raw::{c_char, c_int, c_uint, c_void};

pub const BZ_RUN: c_int = 0;
pub const BZ_FLUSH: c_int = 1;
Expand Down
13 changes: 13 additions & 0 deletions bzip2-sys/wasm_shim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# WASM shims

This directory contains some WASM shims for C-functions used by bzip2 that are not available otherwise for the `wasm32-unknown-unknow` target.
Specifically, these are:

- `malloc`
- `calloc`
- `free`
- `memset`
- `memcpy`
- `memmove`

The shims are implemented in Rust and exposed as C functions that the bzip2-sys crate can then use / link against.
85 changes: 85 additions & 0 deletions bzip2-sys/wasm_shim/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};
use std::mem;
use std::ptr;

// Define a struct to hold the size before the allocated memory
#[repr(C)]
struct AllocationHeader {
size: usize,
}

const HEADER_SIZE: usize = mem::size_of::<AllocationHeader>();
const ALIGNMENT: usize = 2 * mem::size_of::<usize>();

// Helper function to create a layout that includes the header
fn layout_with_header(size: usize) -> Layout {
let adjusted_size = HEADER_SIZE + size;
Layout::from_size_align(adjusted_size, ALIGNMENT).expect("Layout creation failed")
}

#[no_mangle]
pub extern "C" fn rust_bzip2_wasm_shim_malloc(size: usize) -> *mut u8 {
unsafe {
let layout = layout_with_header(size);
let ptr = alloc(layout) as *mut AllocationHeader;
if !ptr.is_null() {
// Store the original size in the header
(*ptr).size = size;
// Return a pointer to the memory after the header
ptr.add(1) as *mut u8
} else {
ptr::null_mut()
}
}
}

#[no_mangle]
pub extern "C" fn rust_bzip2_wasm_shim_calloc(nmemb: usize, size: usize) -> *mut u8 {
let total_size = nmemb * size;
unsafe {
let layout = layout_with_header(total_size);
let ptr = alloc_zeroed(layout) as *mut AllocationHeader;
if !ptr.is_null() {
(*ptr).size = total_size;
ptr.add(1) as *mut u8
} else {
ptr::null_mut()
}
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_bzip2_wasm_shim_free(ptr: *mut u8) {
if !ptr.is_null() {
let ptr = (ptr as *mut AllocationHeader).sub(1); // Move back to the header
let size = (*ptr).size;
let layout = layout_with_header(size);
dealloc(ptr as *mut u8, layout);
}
}

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

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

#[no_mangle]
pub unsafe extern "C" fn rust_bzip2_wasm_shim_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
std::ptr::write_bytes(dest, c as u8, n);
dest
}
22 changes: 22 additions & 0 deletions bzip2-sys/wasm_shim/stdlib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stddef.h>

#ifndef _STDLIB_H
#define _STDLIB_H 1

void *rust_bzip2_wasm_shim_malloc(size_t size);
void *rust_bzip2_wasm_shim_calloc(size_t nmemb, size_t size);
void rust_bzip2_wasm_shim_free(void *ptr);

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

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

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

#endif // _STDLIB_H
22 changes: 22 additions & 0 deletions bzip2-sys/wasm_shim/string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stdlib.h>

#ifndef _STRING_H
#define _STRING_H 1

void *rust_bzip2_wasm_shim_memcpy(void *restrict dest, const void *restrict src, size_t n);
void *rust_bzip2_wasm_shim_memmove(void *dest, const void *src, size_t n);
void *rust_bzip2_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_bzip2_wasm_shim_memcpy(dest, src, n);
}

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

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

#endif // _STRING_H
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
#![doc(html_root_url = "https://docs.rs/bzip2/")]

extern crate bzip2_sys as ffi;
extern crate libc;
#[cfg(test)]
extern crate partial_io;
#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::marker;
use std::mem;
use std::slice;

use libc::{c_int, c_uint};
use std::os::raw::{c_int, c_uint};

use {ffi, Compression};

Expand Down