diff --git a/advanced/binary-protocol.mdx b/advanced/binary-protocol.mdx
index ffca7abc..f389a9fe 100644
--- a/advanced/binary-protocol.mdx
+++ b/advanced/binary-protocol.mdx
@@ -454,6 +454,8 @@ Location: `wacore/binary/src/encoder.rs:227-237`
### Packed Encoding
+Nibble and hex packing share one code path, `write_packed_bytes`: an ASCII→nibble lookup table (`NIBBLE_ENC` or `HEX_ENC`, picked by `data_type`) maps each input byte to its packed nibble, pairs are packed two at a time into a stack buffer, and validity is checked once via an OR accumulator (`seen`) instead of per pair — that is what lets the pair loop unroll.
+
#### Nibble packing (numeric strings)
Strings containing only digits, dash, and dot are packed into 4 bits per character:
@@ -466,58 +468,55 @@ Strings containing only digits, dash, and dot are packed into 4 bits per charact
pub const PACKED_MAX: u8 = 127; // Max length for packed/token strings
-fn pack_nibble(value: u8) -> u8 {
- match value {
- b'-' => 10,
- b'.' => 11,
- 0 => 15, // Padding
- c if c.is_ascii_digit() => c - b'0',
- _ => panic!("Invalid nibble"),
+/// ASCII to nibble for `NIBBLE_8`: digits plus the two punctuation characters
+/// a phone number can carry.
+static NIBBLE_ENC: [u8; 256] = {
+ let mut table = [PACK_INVALID; 256];
+ let mut c = b'0';
+ while c <= b'9' {
+ table[c as usize] = c - b'0';
+ c += 1;
}
-}
+ table[b'-' as usize] = 10;
+ table[b'.' as usize] = 11;
+ table[0] = 15; // padding for an odd-length string
+ table
+};
```
-Location: `wacore/binary/src/encoder.rs:769-777`
+Location: `wacore/binary/src/encoder.rs:32-43`
-#### Hex Packing
+#### Hex packing
-Uppercase hex strings (0-9, A-F) are packed into 4 bits per character:
+Uppercase hex strings (0-9, A-F) are packed into 4 bits per character, against `HEX_ENC` instead of `NIBBLE_ENC`:
```rust
// Input: "DEADBEEF"
// Packed: 0xDE, 0xAD, 0xBE, 0xEF
-fn pack_hex(value: u8) -> u8 {
- match value {
- c if c.is_ascii_digit() => c - b'0',
- c if (b'A'..=b'F').contains(&c) => 10 + (c - b'A'),
- 0 => 15, // Padding
- _ => panic!("Invalid hex"),
+/// ASCII to nibble, the inverse of the decoder's `HEX_PAIRS`.
+static HEX_ENC: [u8; 256] = {
+ let mut table = [PACK_INVALID; 256];
+ let mut c = b'0';
+ while c <= b'9' {
+ table[c as usize] = c - b'0';
+ c += 1;
}
-}
+ let mut c = b'A';
+ while c <= b'F' {
+ table[c as usize] = 10 + (c - b'A');
+ c += 1;
+ }
+ table[0] = 15; // padding for an odd-length string
+ table
+};
```
-Location: `wacore/binary/src/encoder.rs:780-787`
-
-#### SIMD Optimization
-
-The encoder uses SIMD instructions for fast packing of long strings:
+Location: `wacore/binary/src/encoder.rs:14-28`
-```rust
-while input_bytes.len() >= 16 {
- let input = u8x16::from_slice(chunk);
- let indices = input.saturating_sub(nibble_base);
- let nibbles = lookup.swizzle_dyn(indices);
-
- let (evens, odds) = nibbles.deinterleave(
- nibbles.rotate_elements_left::<1>()
- );
- let packed = (evens << Simd::splat(4)) | odds;
- self.write_raw_bytes(&packed.to_array()[..8])?;
-}
-```
-
-Location: `wacore/binary/src/encoder.rs:809-824`
+
+ Packing used to run through per-character `match` ladders (`pack_nibble`/`pack_hex`) reached via a `fn` pointer, and for a while carried a `portable_simd` fast path for long strings on top of that. Both are gone: measurement showed the vector path only ever beat the `match` ladders, not a lookup table, and lost to the table at every string length tested — `HEX_PAIRS[byte]` (the decoder's mirror-image table) is one 2-byte load, and a shuffle/interleave/store sequence doesn't beat that. The two lookup tables above replaced all three call paths; an exhaustive test (`encode_tables_match_the_ladders_they_replaced`) checks every one of the 256 byte values against the original `match` ladders so the tables can't silently drift from the encoding they replaced.
+
### JID Encoding
@@ -850,7 +849,7 @@ fn unpack_nibble(packed: u8, position: u8) -> u8 {
}
```
-Unpacking a run of packed values dispatches through a 256-entry byte-pair table — one lookup and a 2-byte store per input byte, rather than the two-shift/two-lookup scalar walk this snippet shows. This is an implementation detail of `read_packed`; the value mapping above still describes what each nibble decodes to for `NIBBLE_8`. `HEX_8` unpacking is the mirror of [`pack_hex`](#hex-packing) shown earlier — 0–9 to digits, 10–15 to `A`–`F` — not the nibble table above.
+Unpacking a run of packed values dispatches through a 256-entry byte-pair table — one lookup and a 2-byte store per input byte, rather than the two-shift/two-lookup scalar walk this snippet shows. This is an implementation detail of `read_packed`; the value mapping above still describes what each nibble decodes to for `NIBBLE_8`. `HEX_8` unpacking is the mirror of [`HEX_ENC`](#hex-packing) shown earlier — 0–9 to digits, 10–15 to `A`–`F` — not the nibble table above.
Location: `wacore/binary/src/decoder.rs:400-450`
diff --git a/installation.mdx b/installation.mdx
index 9ae5df34..8def907d 100644
--- a/installation.mdx
+++ b/installation.mdx
@@ -7,8 +7,7 @@ description: Add whatsapp-rust to your Rust project
Before installing whatsapp-rust, ensure you have:
-- **Rust nightly** (default) — required only by the default `simd` feature, which uses the unstable `portable_simd` API. The project pins `nightly-2026-06-16` via `rust-toolchain.toml`.
-- **Rust 1.94 or newer** — the workspace MSRV. Edition 2024 itself needs no nightly, so stable works once `simd` is off. See [Using stable Rust](#using-stable-rust).
+- **Rust 1.94 or newer** — the workspace MSRV, and all default features build on **stable Rust**. See [Using stable Rust](#using-stable-rust).
- **Cargo** package manager
@@ -55,9 +54,7 @@ tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
If you need to declare a sibling crate explicitly (for example to enable a crate-specific feature flag), you can still add it individually. The full multi-crate form:
-
-
-```toml Nightly (default)
+```toml Cargo.toml
[dependencies]
whatsapp-rust = "0.7"
# Only needed for crate-specific features not exposed through whatsapp-rust:
@@ -69,22 +66,6 @@ whatsapp-rust = "0.7"
tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
```
-```toml Stable Rust
-[dependencies]
-whatsapp-rust = { version = "0.7", default-features = false, features = [
- "sqlite-storage",
- "tokio-transport",
- "tokio-runtime",
- "ureq-client",
- "tokio-native",
- "signal",
-] }
-wacore = { version = "0.7", default-features = false }
-tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
-```
-
-
-
## Feature flags
whatsapp-rust supports several optional features:
@@ -96,7 +77,6 @@ whatsapp-rust supports several optional features:
| `tokio-transport` | Tokio WebSocket transport | ✅ Yes |
| `ureq-client` | Ureq HTTP client | ✅ Yes |
| `sqlite-storage` | SQLite storage backend | ✅ Yes |
-| `simd` | SIMD-optimized binary protocol encoding/decoding (**requires nightly Rust**) | ✅ Yes |
| `signal` | Unix signal handling (graceful shutdown on SIGTERM/Ctrl+C) | ✅ Yes |
| `tracing` | Emit `tracing` spans/events across connect, send, receive, IQ, app state, pairing, media, and session flows. See [Observability](/advanced/observability) | ❌ No |
| `tracing-pii` | Render raw phone numbers in `tracing` fields instead of redacted `pn#`. Local debugging only — never enable in production | ❌ No |
@@ -192,38 +172,11 @@ See [Chat Store](/api/chat-store) for the full API.
## Using stable Rust
-whatsapp-rust uses Rust **edition 2024** and declares an MSRV of **1.94**, both of which stable Rust supports. The one nightly-only piece is the default `simd` feature, which uses Rust's unstable `portable_simd` API for optimized binary protocol encoding/decoding. The project itself pins `nightly-2026-06-16` via `rust-toolchain.toml`.
-
-To compile on **stable Rust**, disable the `simd` feature by setting `default-features = false`. You must do this on **both** `whatsapp-rust` and `wacore` — otherwise Cargo's [feature unification](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification) will re-enable SIMD through the `wacore` dependency:
+whatsapp-rust uses Rust **edition 2024** and declares an MSRV of **1.94**, both of which stable Rust supports, and the default feature set has no nightly-only dependency — `cargo build`/`cargo add whatsapp-rust` works with **stable Rust** out of the box, no feature flags to disable.
-```toml Cargo.toml
-[dependencies]
-# Disable defaults (removes `simd`), then re-enable everything else
-whatsapp-rust = { version = "0.7", default-features = false, features = [
- "sqlite-storage",
- "tokio-transport",
- "tokio-runtime",
- "ureq-client",
- "tokio-native",
- "signal",
-] }
-# wacore also needs default-features = false to prevent feature unification
-# from re-enabling simd
-wacore = { version = "0.7", default-features = false }
-
-# These crates have no SIMD dependency — no changes needed
-whatsapp-rust-sqlite-storage = "0.7"
-whatsapp-rust-tokio-transport = "0.7"
-whatsapp-rust-ureq-http-client = "0.7"
-waproto = "0.7"
-tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
-```
-
-
- Setting `default-features = false` only on `whatsapp-rust` is **not enough** if you also depend on `wacore` directly. The direct `wacore` dependency enables `simd` by default, and Cargo merges features across all dependents. Both must opt out.
-
-
-The encoder/decoder automatically falls back to scalar code paths when SIMD is disabled. There is no functional difference — only a minor performance difference in binary protocol operations.
+
+ The project's own `rust-toolchain.toml` still pins a nightly compiler (`nightly-2026-06-16`), but only for internal, binary-size-focused build flags — `-Zshare-generics` and lld/ICF linking, set workspace-wide in `.cargo/config.toml`, plus `-Zbuild-std` for the Docker image build only (see [Docker deployment](#docker-deployment) below) — not for any language feature the published crates need. That pin governs building the whatsapp-rust workspace itself; it has no effect on your project when you depend on whatsapp-rust from crates.io or git.
+
## 32-bit target support
diff --git a/pt/installation.mdx b/pt/installation.mdx
index 68aa4135..0f3b2d72 100644
--- a/pt/installation.mdx
+++ b/pt/installation.mdx
@@ -7,8 +7,7 @@ description: Adicione whatsapp-rust ao seu projeto Rust
Antes de instalar whatsapp-rust, certifique-se de ter:
-- **Rust nightly** (padrão) — necessário apenas pela feature padrão `simd`, que usa a API instável `portable_simd`. O projeto fixa `nightly-2026-06-16` via `rust-toolchain.toml`.
-- **Rust 1.94 ou mais novo** — o MSRV do workspace. A edição 2024 em si não exige nightly, então o stable funciona assim que o `simd` é desabilitado. Veja [Usando Rust stable](#usando-rust-stable).
+- **Rust 1.94 ou mais novo** — o MSRV do workspace, e todas as features padrão compilam com **Rust stable**. Veja [Usando Rust stable](#usando-rust-stable).
- Gerenciador de pacotes **Cargo**
@@ -19,9 +18,7 @@ Antes de instalar whatsapp-rust, certifique-se de ter:
`whatsapp-rust` reexporta a pilha inteira (`wacore`, `wacore_binary`, `waproto` e todas as implementações incluídas), então **uma linha de dependência basta** para a maioria dos projetos:
-
-
-```toml Nightly (padrão)
+```toml Cargo.toml
[dependencies]
whatsapp-rust = "0.7"
# Só é preciso declarar um crate irmão para features específicas dele:
@@ -33,24 +30,6 @@ whatsapp-rust = "0.7"
tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
```
-```toml Rust Stable
-[dependencies]
-whatsapp-rust = { version = "0.7", default-features = false, features = [
- "sqlite-storage",
- "tokio-transport",
- "tokio-runtime",
- "ureq-client",
- "tokio-native",
- "signal",
-] }
-# wacore também precisa de default-features = false para a unificação de
-# features não reativar o simd
-wacore = { version = "0.7", default-features = false }
-tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
-```
-
-
-
Todo caminho de sub-crate é alcançável pelo crate principal — `whatsapp_rust::waproto::whatsapp` (apelidado como `wa` no `prelude`), `whatsapp_rust::wacore`, `whatsapp_rust::wacore_binary`, `whatsapp_rust::store::SqliteStore`, `whatsapp_rust::http::UreqHttpClient` e `whatsapp_rust::transport::TokioWebSocketTransportFactory`. O mesmo vale para os crates de terceiros cujos tipos aparecem na API pública (`anyhow`, `async_trait`, `bytes`, `chrono`, `futures`, `serde`, `serde_json`, `async_channel`, `buffa`): você nunca precisa adicioná-los nem fixar suas versões.
## Feature flags
@@ -64,7 +43,6 @@ whatsapp-rust suporta diversas features opcionais:
| `tokio-transport` | Transporte WebSocket Tokio | ✅ Sim |
| `ureq-client` | Cliente HTTP Ureq | ✅ Sim |
| `sqlite-storage` | Backend de armazenamento SQLite | ✅ Sim |
-| `simd` | Codificação/decodificação do protocolo binário otimizada com SIMD (**requer Rust nightly**) | ✅ Sim |
| `signal` | Manipulação de sinais Unix (desligamento gracioso em SIGTERM/Ctrl+C) | ✅ Sim |
| `tracing` | Emite spans/eventos `tracing` em connect, send, receive, IQ, app state, pareamento, mídia e fluxos de sessão. Veja [Observabilidade](/advanced/observability) | ❌ Não |
| `tracing-pii` | Mostra números de telefone crus nos campos do `tracing` em vez do `pn#` redigido. Apenas para depuração local — nunca habilite em produção | ❌ Não |
@@ -151,38 +129,11 @@ Veja [Chat Store](/api/chat-store) para a API completa.
## Usando Rust stable
-whatsapp-rust usa a **edição Rust 2024** e declara um MSRV de **1.94** — ambos suportados pelo Rust stable. A única parte exclusiva do nightly é a feature padrão `simd`, que usa a API instável `portable_simd` do Rust para codificação/decodificação otimizada do protocolo binário. O projeto em si fixa `nightly-2026-06-16` via `rust-toolchain.toml`.
-
-Para compilar com **Rust stable**, desabilite a feature `simd` definindo `default-features = false`. Você deve fazer isso em **ambos** `whatsapp-rust` e `wacore` — caso contrário, a [unificação de features](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification) do Cargo irá reabilitar SIMD através da dependência `wacore`:
+whatsapp-rust usa a **edição Rust 2024** e declara um MSRV de **1.94**, ambos suportados pelo Rust stable, e o conjunto de features padrão não tem nenhuma dependência exclusiva do nightly — `cargo build`/`cargo add whatsapp-rust` funciona com **Rust stable** de fábrica, sem precisar desabilitar nenhuma feature.
-```toml Cargo.toml
-[dependencies]
-# Desabilita os padrões (remove `simd`), depois reabilita o resto
-whatsapp-rust = { version = "0.7", default-features = false, features = [
- "sqlite-storage",
- "tokio-transport",
- "tokio-runtime",
- "ureq-client",
- "tokio-native",
- "signal",
-] }
-# wacore também precisa de default-features = false para evitar que
-# a unificação de features reabilite simd
-wacore = { version = "0.7", default-features = false }
-
-# Estes crates não dependem de SIMD — nenhuma mudança necessária
-whatsapp-rust-sqlite-storage = "0.7"
-whatsapp-rust-tokio-transport = "0.7"
-whatsapp-rust-ureq-http-client = "0.7"
-waproto = "0.7"
-tokio = { version = "1.53", features = ["macros", "rt-multi-thread"] }
-```
-
-
- Definir `default-features = false` somente em `whatsapp-rust` **não é suficiente** se você também depende de `wacore` diretamente. A dependência direta de `wacore` habilita `simd` por padrão, e o Cargo mescla features entre todos os dependentes. Ambos precisam optar por sair.
-
-
-O codificador/decodificador faz fallback automaticamente para caminhos escalares quando o SIMD está desabilitado. Não há diferença funcional — apenas uma pequena diferença de desempenho nas operações do protocolo binário.
+
+ O `rust-toolchain.toml` do próprio projeto ainda fixa um compilador nightly (`nightly-2026-06-16`), mas apenas para flags de build internas focadas em tamanho de binário — `-Zshare-generics` e linking lld/ICF, definidas para todo o workspace em `.cargo/config.toml`, mais `-Zbuild-std` somente para o build da imagem Docker (veja a seção de implantação com Docker abaixo) — não para nenhuma feature de linguagem que os crates publicados precisem. Essa fixação rege a compilação do workspace whatsapp-rust em si; ela não afeta seu projeto quando você depende de whatsapp-rust via crates.io ou git.
+
## Suporte a alvos de 32 bits