Skip to content

Commit 488ed72

Browse files
committed
feat: update dependencies, optimize WASM performance, and improve memory allocation
1 parent 254525e commit 488ed72

10 files changed

Lines changed: 123 additions & 62 deletions

File tree

.cargo/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[target.wasm32-unknown-unknown]
2-
rustflags = ["-C", "target-feature=+simd128"]
2+
rustflags = ["-C", "target-feature=+simd128", "--cfg", "getrandom_backend=\"wasm_js\""]
33

44
[build]
55
target = "wasm32-unknown-unknown"

Cargo.lock

Lines changed: 29 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ image = { version = "0.25.5", default-features = false, features = [
5656
], optional = true }
5757

5858
img-parts = { version = "0.4", default-features = false, optional = true }
59-
js-sys = "0.3"
59+
js-sys = "0.3.85"
6060
log = "0.4"
6161
md-5 = "0.10"
6262
prost = { version = "0.14.1", default-features = false }
@@ -75,14 +75,15 @@ sha2 = "0.10"
7575
simd-adler32 = { version = "0.3.7", default-features = false, features = [
7676
"std",
7777
] }
78-
symphonia = { version = "0.5.4", default-features = false, features = [
78+
symphonia = { version = "0.5.5", default-features = false, features = [
7979
"mp3",
8080
"aac",
8181
"isomp4",
8282
"ogg",
8383
], optional = true }
84+
talc = "4.4.3"
8485
tsify-next = { version = "0.5", default-features = false, features = ["js"] }
85-
uuid = { version = "1.18.1", default-features = false, features = ["v4", "js"] }
86+
uuid = { version = "1.20.0", default-features = false, features = ["v4", "js"] }
8687

8788
wacore-appstate = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch = "main", package = "wacore-appstate" }
8889
wacore-binary = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch = "main", features = [
@@ -91,8 +92,8 @@ wacore-binary = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch
9192
wacore-libsignal = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch = "main", package = "wacore-libsignal" }
9293
wacore-noise = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch = "main", package = "wacore-noise" }
9394
waproto = { git = "https://github.com/jlucaso1/whatsapp-rust.git", branch = "main", package = "waproto" }
94-
wasm-bindgen = "0.2.100"
95-
wasm-bindgen-futures = "0.4.55"
95+
wasm-bindgen = { version = "0.2.108", features = ["enable-interning"] }
96+
wasm-bindgen-futures = "0.4"
9697
web-sys = { version = "0.3", features = [
9798
"ReadableStream",
9899
"ReadableStreamDefaultReader",

src/binary.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,23 @@ impl InternalBinaryNode {
123123

124124
#[inline]
125125
fn convert_attrs(attrs: &[(Cow<'_, str>, ValueRef<'_>)]) -> Attrs {
126-
let obj = Object::new();
127-
for (k, v) in attrs.iter() {
128-
let js_value = match v.as_str() {
129-
Some(s) => JsValue::from_str(s),
130-
None => JsValue::from_str(&v.to_string()),
131-
};
132-
let _ = js_sys::Reflect::set(&obj, &JsValue::from_str(k), &js_value);
126+
// Use Object.from_entries() which is faster than multiple Reflect.set() calls
127+
let entries = Array::new_with_length(attrs.len() as u32);
128+
for (i, (k, v)) in attrs.iter().enumerate() {
129+
let pair = Array::new_with_length(2);
130+
pair.set(0, JsValue::from_str(k));
131+
pair.set(
132+
1,
133+
match v.as_str() {
134+
Some(s) => JsValue::from_str(s),
135+
None => JsValue::from_str(&v.to_string()),
136+
},
137+
);
138+
entries.set(i as u32, pair.into());
133139
}
134-
obj.unchecked_into()
140+
Object::from_entries(&entries)
141+
.unwrap_or_else(|_| Object::new())
142+
.unchecked_into()
135143
}
136144
}
137145

@@ -146,12 +154,17 @@ impl InternalBinaryNode {
146154
pub fn to_json(&self) -> JsValue {
147155
let obj = Object::new();
148156

157+
// Use interned strings for frequently used keys
149158
let _ = js_sys::Reflect::set(
150159
&obj,
151-
&JsValue::from_str("tag"),
160+
&wasm_bindgen::intern("tag").into(),
152161
&JsValue::from_str(&self.node_ref().tag),
153162
);
154-
let _ = js_sys::Reflect::set(&obj, &JsValue::from_str("attrs"), &self.attrs().into());
163+
let _ = js_sys::Reflect::set(
164+
&obj,
165+
&wasm_bindgen::intern("attrs").into(),
166+
&self.attrs().into(),
167+
);
155168

156169
if let Some(content) = self.content() {
157170
let content_js: JsValue = content.into();
@@ -160,7 +173,11 @@ impl InternalBinaryNode {
160173
} else {
161174
content_js
162175
};
163-
let _ = js_sys::Reflect::set(&obj, &JsValue::from_str("content"), &content_value);
176+
let _ = js_sys::Reflect::set(
177+
&obj,
178+
&wasm_bindgen::intern("content").into(),
179+
&content_value,
180+
);
164181
}
165182

166183
obj.into()
@@ -169,7 +186,8 @@ impl InternalBinaryNode {
169186
fn serialize_child_nodes(&self, content_js: &JsValue) -> JsValue {
170187
let arr = Array::from(content_js);
171188
let json_arr = Array::new_with_length(arr.length());
172-
let to_json_key = JsValue::from_str("toJSON");
189+
// Use interned string for toJSON key - called repeatedly
190+
let to_json_key: JsValue = wasm_bindgen::intern("toJSON").into();
173191

174192
for i in 0..arr.length() {
175193
let item = arr.get(i);

src/curve.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn calculate_agreement(
8383
let pub_key = parse_public_key(public_key_bytes)?;
8484
let secret = priv_key.calculate_agreement(&pub_key).map_err(map_err)?;
8585

86-
let result = Uint8Array::new_with_length(secret.len() as u32);
86+
let result = Uint8Array::new_with_length(32);
8787
result.copy_from(secret.as_ref());
8888
Ok(result)
8989
}
@@ -106,7 +106,7 @@ pub fn calculate_signature(
106106
.calculate_signature(message, &mut OsRng.unwrap_err())
107107
.map_err(map_err)?;
108108

109-
let result = Uint8Array::new_with_length(signature.len() as u32);
109+
let result = Uint8Array::new_with_length(64);
110110
result.copy_from(signature.as_ref());
111111
Ok(result)
112112
}

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
// Use Talc allocator for better WASM performance
2+
// WasmHandler integrates with WebAssembly's memory.grow for dynamic allocation
3+
#[cfg(target_arch = "wasm32")]
4+
#[global_allocator]
5+
static ALLOCATOR: talc::Talck<talc::locking::AssumeUnlockable, talc::WasmHandler> =
6+
unsafe { talc::Talc::new(talc::WasmHandler::new()).lock() };
7+
18
pub mod appstate;
29
#[cfg(feature = "audio")]
310
pub mod audio;

src/noise_session.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl NoiseSession {
4646
is_finished: false,
4747
intro_header: Some(intro_header),
4848
frame_decoder: FrameDecoder::new(),
49-
encode_scratch: Vec::with_capacity(4096),
49+
encode_scratch: Vec::new(), // Lazy allocation - grows on demand
5050
})
5151
}
5252

@@ -221,7 +221,7 @@ impl NoiseSession {
221221
pub fn get_hash(&self) -> Uint8Array {
222222
if let Some(ref handshake) = self.handshake {
223223
let hash = handshake.hash();
224-
let result = Uint8Array::new_with_length(hash.len() as u32);
224+
let result = Uint8Array::new_with_length(32);
225225
result.copy_from(hash);
226226
result
227227
} else {

src/session_cipher.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use js_sys::{Object, Reflect, Uint8Array};
22
use rand::{TryRngCore, rngs::OsRng};
3-
use std::cell::RefCell;
43
use wasm_bindgen::prelude::*;
54

65
use crate::{
@@ -11,9 +10,20 @@ use wacore_libsignal::protocol::{self as libsignal, SessionStore, UsePQRatchet};
1110

1211
#[inline]
1312
fn bytes_to_uint8array(bytes: &[u8]) -> Uint8Array {
14-
let result = Uint8Array::new_with_length(bytes.len() as u32);
15-
result.copy_from(bytes);
16-
result
13+
Uint8Array::from(bytes)
14+
}
15+
16+
// Interned keys for encrypt result - avoids repeated string allocation
17+
#[inline]
18+
fn create_encrypt_result(type_id: u8, body: Uint8Array) -> Result<EncryptResult, JsValue> {
19+
let obj = Object::new();
20+
let _ = Reflect::set(
21+
&obj,
22+
&wasm_bindgen::intern("type").into(),
23+
&(type_id as u32).into(),
24+
);
25+
let _ = Reflect::set(&obj, &wasm_bindgen::intern("body").into(), &body.into());
26+
Ok(obj.unchecked_into())
1727
}
1828

1929
#[wasm_bindgen]
@@ -22,11 +32,6 @@ extern "C" {
2232
pub type EncryptResult;
2333
}
2434

25-
thread_local! {
26-
static TYPE_KEY: RefCell<JsValue> = RefCell::new(JsValue::from_str("type"));
27-
static BODY_KEY: RefCell<JsValue> = RefCell::new(JsValue::from_str("body"));
28-
}
29-
3035
#[wasm_bindgen(js_name = SessionCipher)]
3136
pub struct SessionCipher {
3237
storage_adapter: JsStorageAdapter,
@@ -62,11 +67,7 @@ impl SessionCipher {
6267
let body_array = bytes_to_uint8array(ciphertext_message.serialize());
6368
let type_id = ciphertext_message.message_type() as u8;
6469

65-
let result = Object::new();
66-
TYPE_KEY.with(|k| Reflect::set(&result, &k.borrow(), &(type_id as u32).into()))?;
67-
BODY_KEY.with(|k| Reflect::set(&result, &k.borrow(), &body_array.into()))?;
68-
69-
Ok(result.unchecked_into())
70+
create_encrypt_result(type_id, body_array)
7071
}
7172

7273
#[wasm_bindgen(js_name = decryptPreKeyWhisperMessage)]

src/session_record.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ impl SessionRecord {
3333

3434
// 3. Legacy libsignal-node JSON format (has "_sessions" key)
3535
// Returns empty record to trigger safe re-negotiation
36-
if Reflect::has(&val, &JsValue::from_str(SESSIONS_KEY)).unwrap_or(false) {
36+
if Reflect::has(&val, &wasm_bindgen::intern(SESSIONS_KEY).into()).unwrap_or(false) {
3737
return create_empty_session_record();
3838
}
3939

4040
// 4. Buffer-like objects { type: 'Buffer', data: [...] }
41-
if let Ok(data) = Reflect::get(&val, &JsValue::from_str(DATA_KEY))
41+
if let Ok(data) = Reflect::get(&val, &wasm_bindgen::intern(DATA_KEY).into())
4242
&& Array::is_array(&data)
4343
{
4444
return Ok(SessionRecord::new(js_array_to_vec(&Array::from(&data))));

0 commit comments

Comments
 (0)