From f88e9feb8277e3ec7aa3fbeadc1941d2e47b71a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Tue, 14 Apr 2026 18:54:48 -0300 Subject: [PATCH 1/3] perf: avoid unnecessary clone and pre-allocate Vecs in hot paths - send.rs: borrow own_sending_jid.user instead of cloning CompactString into the retain closure - usync.rs: collect device nodes to get count, then pre-allocate Vec instead of growing from empty - message.rs: pre-allocate enc_nodes Vec with capacity 4 in the message decrypt path (typical messages have 1-2 enc nodes) --- src/message.rs | 2 +- wacore/src/send.rs | 4 ++-- wacore/src/usync.rs | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/message.rs b/src/message.rs index 22c26465f..a54ab010e 100644 --- a/src/message.rs +++ b/src/message.rs @@ -286,7 +286,7 @@ impl Client { let unavailable_node = nr.get_optional_child("unavailable"); - let mut all_enc_nodes: Vec<&NodeRef<'_>> = Vec::new(); + let mut all_enc_nodes: Vec<&NodeRef<'_>> = Vec::with_capacity(4); let direct_enc_nodes = nr.get_children_by_tag("enc"); all_enc_nodes.extend(direct_enc_nodes); diff --git a/wacore/src/send.rs b/wacore/src/send.rs index de62d83ad..59af35d50 100644 --- a/wacore/src/send.rs +++ b/wacore/src/send.rs @@ -1080,11 +1080,11 @@ pub async fn prepare_group_stanza< // because they need the SKDM to decrypt messages we send from this device // - Exclude hosted/Cloud API devices (device ID 99 or @hosted server) - they don't // participate in group E2EE, only in 1:1 chats - let own_user = own_sending_jid.user.clone(); + let own_user = &own_sending_jid.user; let own_device = own_sending_jid.device; let before_filter = resolved_list.len(); resolved_list.retain(|device_jid| { - let is_exact_sender = device_jid.user == own_user && device_jid.device == own_device; + let is_exact_sender = device_jid.user == *own_user && device_jid.device == own_device; let is_hosted = device_jid.is_hosted(); // Exclude the exact sending device and hosted devices !is_exact_sender && !is_hosted diff --git a/wacore/src/usync.rs b/wacore/src/usync.rs index aee5f7e91..c989f1c95 100644 --- a/wacore/src/usync.rs +++ b/wacore/src/usync.rs @@ -85,8 +85,9 @@ pub fn parse_get_user_devices_response_with_phash(resp_node: &Node) -> Result None, }); - let mut devices = Vec::new(); - for device_node in device_list_node.get_children_by_tag("device") { + let device_nodes: Vec<_> = device_list_node.get_children_by_tag("device").collect(); + let mut devices = Vec::with_capacity(device_nodes.len()); + for device_node in device_nodes { let device_id_str = match device_node.attrs().optional_string("id") { Some(id) => id, None => { From b40a47852227d5407101ee89b1802ea0d2b9c084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Tue, 14 Apr 2026 19:09:54 -0300 Subject: [PATCH 2/3] fix: use size_hint instead of collect for device Vec capacity Avoids materializing the iterator into a temporary Vec just to get a count. Filter iterators propagate the parent's upper bound through size_hint(), which here is the total children count. --- wacore/src/usync.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wacore/src/usync.rs b/wacore/src/usync.rs index c989f1c95..92d49a86d 100644 --- a/wacore/src/usync.rs +++ b/wacore/src/usync.rs @@ -85,9 +85,9 @@ pub fn parse_get_user_devices_response_with_phash(resp_node: &Node) -> Result None, }); - let device_nodes: Vec<_> = device_list_node.get_children_by_tag("device").collect(); - let mut devices = Vec::with_capacity(device_nodes.len()); - for device_node in device_nodes { + let device_iter = device_list_node.get_children_by_tag("device"); + let mut devices = Vec::with_capacity(device_iter.size_hint().1.unwrap_or(0)); + for device_node in device_iter { let device_id_str = match device_node.attrs().optional_string("id") { Some(id) => id, None => { From abcd0def6e3b113bb707e7c17b72324cf8f031d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Tue, 14 Apr 2026 19:47:11 -0300 Subject: [PATCH 3/3] fix: use children().len() for device Vec capacity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit size_hint() on Flatten+Filter returns (0, None) — the previous approach was a no-op equivalent to Vec::new(). Use the total children count as an upper-bound estimate instead. --- wacore/src/usync.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wacore/src/usync.rs b/wacore/src/usync.rs index 92d49a86d..cc56a3e40 100644 --- a/wacore/src/usync.rs +++ b/wacore/src/usync.rs @@ -85,9 +85,9 @@ pub fn parse_get_user_devices_response_with_phash(resp_node: &Node) -> Result None, }); - let device_iter = device_list_node.get_children_by_tag("device"); - let mut devices = Vec::with_capacity(device_iter.size_hint().1.unwrap_or(0)); - for device_node in device_iter { + let capacity = device_list_node.children().map_or(0, |c| c.len()); + let mut devices = Vec::with_capacity(capacity); + for device_node in device_list_node.get_children_by_tag("device") { let device_id_str = match device_node.attrs().optional_string("id") { Some(id) => id, None => {