perf: zero-copy ciphertext serialization and DRY send pipeline - #502
Conversation
- Add into_serialized(self) -> Box<[u8]> on SignalMessage, PreKeySignalMessage, SenderKeyMessage, SenderKeyDistributionMessage — moves the internal buffer instead of copying via .to_vec() - Extract extract_ciphertext() helper in wacore::send, replacing 4 duplicated CiphertextMessage match blocks across peer, group, and retry stanza builders - Add encode_and_pad() that encodes protobuf + pads in a single pre-sized allocation, replacing encode_to_vec() + pad_message_v2() two-step - DRY: extract random_pad_len() shared between pad_message_v2 and encode_and_pad - Use resize() instead of vec! + extend_from_slice for padding bytes - Hash sorted phash strings incrementally instead of join() to avoid the large concatenated string allocation
📝 WalkthroughWalkthroughThis pull request refactors ciphertext extraction and message serialization handling across the signal encryption layer. It introduces ownership-based serialization methods ( Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@wacore/src/send.rs`:
- Around line 619-623: The code silently skips unsupported ciphertexts when
extract_ciphertext(encrypted_payload) returns None; add a warning log
immediately before the continue to surface partial fanout failures. At the
extract_ciphertext call site, emit a warning (using the existing logging
facility, e.g. warn! or log::warn!) that includes identifying context such as a
short debug/hex of encrypted_payload and the device/recipient identifiers
available in the surrounding scope, then continue; this makes unsupported
ciphertext variants observable without changing control flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b92091c8-432b-4fb5-9644-3653e32138d9
📒 Files selected for processing (4)
src/features/signal.rswacore/libsignal/src/protocol/protocol.rswacore/src/messages.rswacore/src/send.rs
| let Some((enc_type, is_prekey, serialized_bytes)) = | ||
| extract_ciphertext(encrypted_payload) | ||
| else { | ||
| continue; | ||
| }; |
There was a problem hiding this comment.
Add diagnostics before skipping unsupported ciphertext variants.
This branch silently drops a device encryption attempt. A warning log here would make partial fanout failures observable during debugging/incident response.
Suggested patch
- let Some((enc_type, is_prekey, serialized_bytes)) =
- extract_ciphertext(encrypted_payload)
- else {
- continue;
- };
+ let Some((enc_type, is_prekey, serialized_bytes)) =
+ extract_ciphertext(encrypted_payload)
+ else {
+ log::warn!(
+ "Unsupported ciphertext variant for device {}; skipping encryption output",
+ signal_address
+ );
+ continue;
+ };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let Some((enc_type, is_prekey, serialized_bytes)) = | |
| extract_ciphertext(encrypted_payload) | |
| else { | |
| continue; | |
| }; | |
| let Some((enc_type, is_prekey, serialized_bytes)) = | |
| extract_ciphertext(encrypted_payload) | |
| else { | |
| log::warn!( | |
| "Unsupported ciphertext variant for device {}; skipping encryption output", | |
| signal_address | |
| ); | |
| continue; | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wacore/src/send.rs` around lines 619 - 623, The code silently skips
unsupported ciphertexts when extract_ciphertext(encrypted_payload) returns None;
add a warning log immediately before the continue to surface partial fanout
failures. At the extract_ciphertext call site, emit a warning (using the
existing logging facility, e.g. warn! or log::warn!) that includes identifying
context such as a short debug/hex of encrypted_payload and the device/recipient
identifiers available in the surrounding scope, then continue; this makes
unsupported ciphertext variants observable without changing control flow.
|
| Branch | perf/into-serialized-and-encode-pad |
| Testbed | ubuntu-latest |
⚠️ WARNING: Truncated view!The full continuous benchmarking report exceeds the maximum length allowed on this platform.
Summary
Zero-copy ciphertext
into_serialized(self) -> Box<[u8]>onSignalMessage,PreKeySignalMessage,SenderKeyMessage,SenderKeyDistributionMessageBox<[u8]>buffer directly intoNodeContent::Bytesinstead of copying via.to_vec()Box<[u8]>implementsInto<Vec<u8>>(free conversion), soNodeBuilder::bytes()accepts it without allocationDRY:
extract_ciphertext()helperCiphertextMessagematch blocks acrossprepare_peer_stanza,prepare_group_retry_stanza,encrypt_for_devices, andsrc/features/signal.rs(&'static str enc_type, bool is_prekey, Box<[u8]> bytes)Encode + pad fusion
encode_and_pad()encodes protobuf and pads in a single pre-sizedVec::with_capacity(encoded_len + pad_len)random_pad_len()extracted as shared helper (DRY betweenpad_message_v2andencode_and_pad)pad_message_v2usesresize()instead ofvec!+extend_from_sliceIncremental phash
participant_list_hashhashes sorted ad_strings incrementally instead ofjoin()into one large concatenated stringsort_unstableinstead ofsortTest plan
cargo fmt --allcargo clippy --all --tests— zero warningscargo test --workspace --exclude e2e-tests— all 1119 tests passSummary by CodeRabbit