Skip to content

perf: zero-copy ciphertext serialization and DRY send pipeline - #502

Merged
jlucaso1 merged 1 commit into
mainfrom
perf/into-serialized-and-encode-pad
Apr 7, 2026
Merged

perf: zero-copy ciphertext serialization and DRY send pipeline#502
jlucaso1 merged 1 commit into
mainfrom
perf/into-serialized-and-encode-pad

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Apr 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Zero-copy ciphertext

  • Add into_serialized(self) -> Box<[u8]> on SignalMessage, PreKeySignalMessage, SenderKeyMessage, SenderKeyDistributionMessage
  • Moves the internal Box<[u8]> buffer directly into NodeContent::Bytes instead of copying via .to_vec()
  • Box<[u8]> implements Into<Vec<u8>> (free conversion), so NodeBuilder::bytes() accepts it without allocation

DRY: extract_ciphertext() helper

  • Replaces 4 duplicated CiphertextMessage match blocks across prepare_peer_stanza, prepare_group_retry_stanza, encrypt_for_devices, and src/features/signal.rs
  • Returns (&'static str enc_type, bool is_prekey, Box<[u8]> bytes)

Encode + pad fusion

  • encode_and_pad() encodes protobuf and pads in a single pre-sized Vec::with_capacity(encoded_len + pad_len)
  • random_pad_len() extracted as shared helper (DRY between pad_message_v2 and encode_and_pad)
  • pad_message_v2 uses resize() instead of vec! + extend_from_slice

Incremental phash

  • participant_list_hash hashes sorted ad_strings incrementally instead of join() into one large concatenated string
  • Eliminates ~10KB allocation for 256-member groups
  • Uses sort_unstable instead of sort

Test plan

  • cargo fmt --all
  • cargo clippy --all --tests — zero warnings
  • cargo test --workspace --exclude e2e-tests — all 1119 tests pass
  • Benchmarked with iai-callgrind (SKDM 50: -0.28% instructions, -2.1% RAM hits)

Summary by CodeRabbit

  • Refactor
    • Improved internal message handling and encryption serialization efficiency through code restructuring and optimization.

- 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
@coderabbitai

coderabbitai Bot commented Apr 7, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request refactors ciphertext extraction and message serialization handling across the signal encryption layer. It introduces ownership-based serialization methods (into_serialized), centralizes ciphertext variant extraction logic into a new helper function, and optimizes padding and encoding operations by reducing intermediate allocations and consolidating repetitive patterns.

Changes

Cohort / File(s) Summary
Message Serialization APIs
wacore/libsignal/src/protocol/protocol.rs
Added into_serialized(self) -> Box<[u8]> methods to SignalMessage, PreKeySignalMessage, SenderKeyMessage, and SenderKeyDistributionMessage for ownership-consuming serialization alongside existing borrowed serialized() methods.
Ciphertext Extraction & Encryption Flow
wacore/src/send.rs
Added extract_ciphertext helper to centralize mapping of CiphertextMessage variants to (enc_type, is_prekey, serialized). Updated device encryption, peer stanza, and group retry stanza flows to use this helper instead of inline pattern matching.
Message Padding & Encoding
wacore/src/messages.rs
Refactored pad_message_v2 with a new random_pad_len() helper and resize-based padding to reduce allocations. Added encode_and_pad() helper to combine message encoding and padding in a single operation.
Signal Feature Integration
src/features/signal.rs
Replaced explicit CiphertextMessage pattern matching with a call to extract_ciphertext() and changed ciphertext serialization from serialized().to_vec() to into_serialized().into_vec() for ownership-based handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Hops through ciphertext with glee,
Extracts the secrets efficiently!
Into serialized, no clone in sight,
Padding paddles, buffers tight—
A rabbit's refactor, pure delight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing zero-copy ciphertext serialization (via into_serialized methods) and refactoring duplicated code in the send pipeline (via extract_ciphertext helper and encode_and_pad fusion).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/into-serialized-and-encode-pad

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between a3a578e and 6440497.

📒 Files selected for processing (4)
  • src/features/signal.rs
  • wacore/libsignal/src/protocol/protocol.rs
  • wacore/src/messages.rs
  • wacore/src/send.rs

Comment thread wacore/src/send.rs
Comment on lines +619 to 623
let Some((enc_type, is_prekey, serialized_bytes)) =
extract_ciphertext(encrypted_payload)
else {
continue;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchperf/into-serialized-and-encode-pad
Testbedubuntu-latest

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🚨 1 Alert

🐰 View full continuous benchmarking report in Bencher

@jlucaso1
jlucaso1 merged commit 74f485c into main Apr 7, 2026
10 checks passed
@jlucaso1
jlucaso1 deleted the perf/into-serialized-and-encode-pad branch April 7, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant