Skip to content

Commit 81e43f7

Browse files
committed
Don't use Uint8Array when not necessary
1 parent 8d0bf4d commit 81e43f7

File tree

2 files changed

+92
-34
lines changed

2 files changed

+92
-34
lines changed

.github/workflows/tests.yml

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,27 @@ jobs:
229229
# run: cargo test
230230

231231
web:
232-
name: Web
232+
name: Web ${{ matrix.rust.description }}
233233
runs-on: ubuntu-24.04
234+
strategy:
235+
fail-fast: false
236+
matrix:
237+
rust:
238+
- { version: stable, atomics: false }
239+
- {
240+
description: with Atomics,
241+
atomics: true,
242+
version: nightly,
243+
components: rust-src,
244+
flags: '-Ctarget-feature=+atomics,+bulk-memory',
245+
args: '-Zbuild-std=panic_abort,std',
246+
}
234247
steps:
235248
- uses: actions/checkout@v4
236-
- uses: dtolnay/rust-toolchain@stable
249+
- uses: dtolnay/rust-toolchain@master
250+
with:
251+
toolchain: ${{ matrix.rust.version }}
252+
components: ${{ matrix.rust.components }}
237253
- name: Install precompiled wasm-pack
238254
shell: bash
239255
run: |
@@ -243,35 +259,36 @@ jobs:
243259
wasm-pack --version
244260
- uses: Swatinem/rust-cache@v2
245261
- name: Test (Node)
262+
if: matrix.rust.atomics == false
246263
env:
247-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
248-
run: wasm-pack test --node -- --features std
264+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
265+
run: wasm-pack test --node -- --features std ${{ matrix.rust.args }}
249266
- name: Test (Firefox)
250267
env:
251268
WASM_BINDGEN_USE_BROWSER: 1
252-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
253-
run: wasm-pack test --headless --firefox -- --features std
269+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
270+
run: wasm-pack test --headless --firefox -- --features std ${{ matrix.rust.args }}
254271
- name: Test (Chrome)
255272
env:
256273
WASM_BINDGEN_USE_BROWSER: 1
257-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
258-
run: wasm-pack test --headless --chrome -- --features std
274+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
275+
run: wasm-pack test --headless --chrome -- --features std ${{ matrix.rust.args }}
259276
- name: Test (dedicated worker)
260277
env:
261278
WASM_BINDGEN_USE_DEDICATED_WORKER: 1
262-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
263-
run: wasm-pack test --headless --firefox -- --features std
279+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
280+
run: wasm-pack test --headless --firefox -- --features std ${{ matrix.rust.args }}
264281
- name: Test (shared worker)
265282
env:
266283
WASM_BINDGEN_USE_SHARED_WORKER: 1
267-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
268-
run: wasm-pack test --headless --firefox -- --features std
284+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
285+
run: wasm-pack test --headless --firefox -- --features std ${{ matrix.rust.args }}
269286
- name: Test (service worker)
270287
env:
271288
WASM_BINDGEN_USE_SERVICE_WORKER: 1
272-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
289+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
273290
# Firefox doesn't support module service workers and therefor can't import scripts
274-
run: wasm-pack test --headless --chrome -- --features std
291+
run: wasm-pack test --headless --chrome -- --features std ${{ matrix.rust.args }}
275292

276293
wasi:
277294
name: WASI

src/backends/wasm_js.rs

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
extern crate std;
77

88
use crate::Error;
9-
use core::mem::MaybeUninit;
9+
use core::{
10+
mem::MaybeUninit,
11+
sync::atomic::{AtomicU8, Ordering},
12+
};
1013
#[cfg(feature = "std")]
1114
use std::thread_local;
1215

@@ -15,35 +18,71 @@ pub use crate::util::{inner_u32, inner_u64};
1518
#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
1619
compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!");
1720

18-
use js_sys::Uint8Array;
19-
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
21+
use js_sys::{SharedArrayBuffer, Uint8Array, WebAssembly::Memory};
22+
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
2023

2124
// Size of our temporary Uint8Array buffer used with WebCrypto methods
2225
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
2326
const CRYPTO_BUFFER_SIZE: u16 = 256;
2427

28+
const MEMORY_KIND_UNINIT: u8 = 0;
29+
const MEMORY_KIND_NOT_SHARED: u8 = 1;
30+
const MEMORY_KIND_SHARED: u8 = 2;
31+
32+
static MEMORY_KIND: AtomicU8 = AtomicU8::new(0);
33+
2534
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
2635
CRYPTO.with(|crypto| {
2736
let crypto = crypto.as_ref().ok_or(Error::WEB_CRYPTO)?;
2837

29-
// getRandomValues does not work with all types of WASM memory,
30-
// so we initially write to browser memory to avoid exceptions.
31-
let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into());
32-
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
33-
let chunk_len: u32 = chunk
34-
.len()
35-
.try_into()
36-
.expect("chunk length is bounded by CRYPTO_BUFFER_SIZE");
37-
// The chunk can be smaller than buf's length, so we call to
38-
// JS to create a smaller view of buf without allocation.
39-
let sub_buf = buf.subarray(0, chunk_len);
40-
41-
if crypto.get_random_values(&sub_buf).is_err() {
42-
return Err(Error::WEB_GET_RANDOM_VALUES);
43-
}
38+
let shared = loop {
39+
break match MEMORY_KIND.load(Ordering::Relaxed) {
40+
MEMORY_KIND_NOT_SHARED => false,
41+
MEMORY_KIND_SHARED => true,
42+
MEMORY_KIND_UNINIT => {
43+
let memory: Memory = wasm_bindgen::memory().unchecked_into();
44+
let val = if memory.buffer().is_instance_of::<SharedArrayBuffer>() {
45+
MEMORY_KIND_SHARED
46+
} else {
47+
MEMORY_KIND_NOT_SHARED
48+
};
49+
MEMORY_KIND.store(val, Ordering::Relaxed);
50+
continue;
51+
}
52+
_ => unreachable!(),
53+
};
54+
};
55+
56+
if shared {
57+
// getRandomValues does not work with all types of WASM memory,
58+
// so we initially write to browser memory to avoid exceptions.
59+
let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into());
60+
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
61+
let chunk_len: u32 = chunk
62+
.len()
63+
.try_into()
64+
.expect("chunk length is bounded by CRYPTO_BUFFER_SIZE");
65+
// The chunk can be smaller than buf's length, so we call to
66+
// JS to create a smaller view of buf without allocation.
67+
let sub_buf = buf.subarray(0, chunk_len);
4468

45-
// SAFETY: `sub_buf`'s length is the same length as `chunk`
46-
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
69+
if crypto.get_random_values(&sub_buf).is_err() {
70+
return Err(Error::WEB_GET_RANDOM_VALUES);
71+
}
72+
73+
// SAFETY: `sub_buf`'s length is the same length as `chunk`
74+
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
75+
}
76+
} else {
77+
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
78+
// SAFETY: this is only safe because on Wasm the issues with unitialized data don't exist
79+
let buf = unsafe {
80+
core::slice::from_raw_parts_mut(chunk.as_mut_ptr().cast::<u8>(), chunk.len())
81+
};
82+
if crypto.get_random_values_ref(buf).is_err() {
83+
return Err(Error::WEB_GET_RANDOM_VALUES);
84+
}
85+
}
4786
}
4887
Ok(())
4988
})
@@ -59,4 +98,6 @@ extern "C" {
5998
// Crypto.getRandomValues()
6099
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
61100
fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;
101+
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
102+
fn get_random_values_ref(this: &Crypto, buf: &mut [u8]) -> Result<(), JsValue>;
62103
}

0 commit comments

Comments
 (0)