diff --git a/packages/hash-rust/.gitignore b/packages/hash-rust/.gitignore new file mode 100644 index 0000000000..4e301317e5 --- /dev/null +++ b/packages/hash-rust/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +bin/ +pkg/ +wasm-pack.log diff --git a/packages/hash-rust/Cargo.toml b/packages/hash-rust/Cargo.toml new file mode 100644 index 0000000000..517b3be791 --- /dev/null +++ b/packages/hash-rust/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "murmur" +version = "0.1.0" +authors = ["Rom Grk "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cityhasher = "0.1.0" +wasm-bindgen = "0.2.84" + +[dependencies.xxhash-rust] +version = "0.8.12" +features = ["xxh32", "xxh3"] + +[dev-dependencies] +wasm-bindgen-test = "0.3.34" + +[package.metadata.wasm-pack.profile.dev] +wasm-opt = ['--enable-simd'] +[package.metadata.wasm-pack.profile.profiling] +wasm-opt = ['--enable-simd'] +[package.metadata.wasm-pack.profile.release] +wasm-opt = ['--enable-simd'] + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/packages/hash-rust/README.md b/packages/hash-rust/README.md new file mode 100644 index 0000000000..9dece3ac82 --- /dev/null +++ b/packages/hash-rust/README.md @@ -0,0 +1,5 @@ +# hash-rust + +Bootstraped from a standard `wasm-pack` template. + +Build with `wasm-pack build --target nodejs` diff --git a/packages/hash-rust/index.js b/packages/hash-rust/index.js new file mode 100644 index 0000000000..599453a62f --- /dev/null +++ b/packages/hash-rust/index.js @@ -0,0 +1,27 @@ +let imports = {} +let wasm + +const path = require('path').join(__dirname, './pkg/murmur_bg.wasm') +const bytes = require('fs').readFileSync(path) +const wasmModule = new WebAssembly.Module(bytes) +const wasmInstance = new WebAssembly.Instance(wasmModule, imports) +wasm = wasmInstance.exports + +// Manually written to encode faster than wasm_bindgen does. + +let MEM_START = 32 // Alignement for the bytes slice + +let memory = new Uint8Array(wasm.memory.buffer).subarray(MEM_START) +let cachedTextEncoder = new TextEncoder('utf-8') + +module.exports.xxh = wrapStringToU32('xxh') +module.exports.murmur2 = wrapStringToU32('murmur2') + +function wrapStringToU32(name) { + return input => { + const ptr0 = MEM_START + const len0 = cachedTextEncoder.encodeInto(input, memory).written + const ret = wasm[name](ptr0, len0) + return ret + } +} diff --git a/packages/hash-rust/src/lib.rs b/packages/hash-rust/src/lib.rs new file mode 100644 index 0000000000..5d33004675 --- /dev/null +++ b/packages/hash-rust/src/lib.rs @@ -0,0 +1,63 @@ +use wasm_bindgen::prelude::*; +use xxhash_rust::xxh32::xxh32; +use xxhash_rust::xxh3::xxh3_64; + +// xxh via xxhash_rust + +#[wasm_bindgen] +pub fn xxh(bytes: *const u8, length: usize) -> u32 { + let data = unsafe { std::slice::from_raw_parts( + std::mem::transmute::<_, *const u8>(bytes), + length, + ) }; + + return xxh32(data, 0); +} + + +// murmur2 implementation based on emotion's implementation (differs from original) + +const M: u32 = 0x5bd1e995; +const R: u32 = 24; + +#[wasm_bindgen] +pub fn murmur2(bytes: *const u8, mut length: usize) -> u32 { + let data = unsafe { std::slice::from_raw_parts( + std::mem::transmute::<*const u8, *const u32>(bytes), + length / 4, + ) }; + + let mut h = 0; // Not initialized to match emotion's implementation + let mut i = 0; + + while length >= 4 { + let mut k = unsafe { *data.get_unchecked(i) }; + + k *= M; + k ^= k >> R; + k *= M; + + h *= M; + h ^= k; + + i += 1; + length -= 4; + } + + if length >= 3 { + h ^= (unsafe { *bytes.add(i + 2) as u32 } & 0xff) << 16; + } + if length >= 2 { + h ^= (unsafe { *bytes.add(i + 1) as u32 } & 0xff) << 8; + } + if length >= 1 { + h ^= (unsafe { *bytes.add(i + 0) as u32 } & 0xff); + h *= M; + } + + h ^= h >> 13; + h *= M; + h = h ^ (h >> 15); + + return h; +}