perf!: yoke zero-copy node decoding and Jid Server enum - #513
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three major performance optimizations that eliminate allocation overhead in the node decode path:
Jid Server enum: Replace
Jid { server: Cow<'static, str> }with aServeru8 enum. Shrinks Jid from 56→32 bytes. Server comparisons becomeu8==u8instead of string comparison.Yoke zero-copy decoding: Replace
Arc<Node>withArc<OwnedNodeRef>throughout the entire handler chain.OwnedNodeRefwrapsYoke<NodeRef<'static>, Vec<u8>>, keeping the decodedNodeRefborrowing directly from the decompressed buffer — zero allocation for attribute keys, values, and byte content.Full NodeRef migration + API ergonomics: Flipped the
ProtocolNodetrait sotry_from_node_ref(&NodeRef)is the required method. Unified API naming —NodeRef::attrs()matchesNode::attrs(),ValueRef::as_str()returnsCow<str>likeNodeValue::as_str(). Added delegation methods toOwnedNodeRefso handlers can callnode.attrs()directly without.get().Breaking Changes
1. Jid.server:
Cow<'static, str>→ServerenumType change:
Jid { server: Cow<'static, str> }→Jid { server: Server }Same for:
JidRef { server: Cow<'a, str> }→JidRef { server: Server }cow_server_from_str()deleted — useServer::try_from(s)insteadJidError)Serverserializes as string ("s.whatsapp.net") for backward-compatible JSONPn,Lid,Group,Broadcast,Newsletter,Hosted,HostedLid,Messenger,Interop,Bot,Legacy2. Handler trait:
Arc<Node>→Arc<OwnedNodeRef>OwnedNodeRefdelegates:tag(),attrs(),get_attr(),children(),get_optional_child(),get_optional_child_by_tag(),get_children_by_tag(),content_bytes(),content_str(),content_nodes().Use
.get()only when you need the full&NodeRef(e.g., passing to functions).3. Unified API — NodeRef matches Node
node.attrs()node.attrs()node.attrs()node.attrs.get("k")node.get_attr("k")node.get_attr("k")node.children()node.children()node.children()node.get_optional_child("t")node.content → NodeContent::Bytesnode.content_bytes()node.content_bytes()node.content_as_string()node.content_str()node.content_str()4. ValueRef::as_str() now matches NodeValue::as_str()
to_string_cow()deleted —as_str()does the same thing now.5. Event types
6. ProtocolNode trait flipped
7. IqSpec::parse_response
8. Helper functions renamed (dropped
_refsuffix)All
iq::nodehelpers take&NodeRefas canonical signature:required_child,optional_child,required_attr,optional_attr,collect_childrenStanza parsers:
DeviceNotification::try_parse,BusinessNotification::try_parse,parse_lid_mappings_from_response,parse_prekeys_response9. New public re-exports
whatsapp_rust::{Server, OwnedNodeRef, CompactString, Jid}wacore_binary::{Server, Jid, JidRef, JidExt, Node, NodeRef, NodeValue, OwnedNodeRef, NodeContent, NodeContentRef, Attrs, CompactString, AttrParser, AttrParserRef, DeviceKey, ...}What's zero-copy and what's not
Fully zero-copy (no allocation on decode path):
decrypt_frame→OwnedNodeRef(yoke wraps decompressed buffer)Arc<OwnedNodeRef>(cheap refcount)parse_responseimplementationsparse_message_info, encryption, routing)Structurally required
to_owned()(7 calls):GroupNotificationAction::Create/Link/Unlink { raw: Node }— struct fieldStreamError.raw/ConnectFailure.raw— event fieldsIqError::Disconnected(Node)—'staticerror typeenc_nodeclone crossing async task boundaryTest plan
cargo fmt --allcargo clippy --all --tests— zero errors, zero warningscargo test -p wacore-binary -p wacore -p whatsapp-rust— all passingcargo check -p wacore-binary --all-features— serde verifiedSummary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests