pct
A small, allocation-conscious Rust crate for percent-encoded strings used in URLs, URIs, IRIs, etc. — parse, validate, encode, decode, compare.
use ;
let s = new?;
assert_eq!;
assert_eq!;
let encoded = encode;
assert_eq!;
Pick an Encoder impl (UriReserved, IriReserved) or write your own:
use ;
;
let s = encode;
assert_eq!;
Why this fork
Fork of pct-str by Timothée Haudebourg. Public API and RFC behavior unchanged; this fork adds:
- SWAR plain-run scanner and portable-SIMD path (nightly, gated on
simd) for validate / decode / encode. - Hex-decode lookup tables replacing per-nibble branches.
- Byte-level fast paths for
new,decode,encode_bytes,eq,ord,hash,len. memchr-accelerated%scan (default-on).- Criterion bench suite under
benches/. - Rust 2024 edition, MSRV 1.85.
- Renamed crate to
pct.
Credit and history preserved — see Attribution.
Install
Feature flags
| Flag | Default | Enables |
|---|---|---|
std |
yes | std::error::Error impls, owned PctString, String APIs |
memchr |
yes | memchr-accelerated % scan in validate / decode / encode |
simd |
Portable-SIMD plain-run scanner. Requires nightly rustc. |
For no_std, disable default features. You get PctStr (borrowed, zero-alloc) and the Encoder trait. Re-enable std for PctString and String-returning APIs.
Streaming decode
PctStr::chars() and PctStr::bytes() are lazy iterators over the decoded form. No intermediate String, works under no_std:
let s = new?;
for ch in s.chars
For encoding from a known &str, prefer PctString::encode_bytes — it skips UTF-8 re-iteration and hits the SWAR/SIMD scanner directly.
Equivalence
Equality, ordering, and hashing compare the decoded bytes — PctStr::new("%41") == "A", and hex case (%2f vs %2F) is irrelevant. Keep this in mind when using PctStr / PctString as map keys: two values with different as_str() can hash equal.
Examples
See examples/ for runnable end-to-end usage:
Benchmarks
Criterion output: target/criterion/.
MSRV
Rust 1.85 (edition 2024). The simd feature additionally requires nightly.
Attribution
Original crate: pct-str by Timothée Haudebourg. Upstream commits are preserved in this repo's history under their original authorship. This fork is a thin layer of performance and ergonomics work on top of their design.
License
Dual-licensed, same as upstream. Pick whichever fits: