sorug
sorug is an ultra-high-performance, zero-copy, WHATWG URL Living Standard-compliant URL parser written in Rust. It targets production parsers that need correctness and nanosecond-scale throughput — and currently outperforms both servo/rust-url and ada-url on the hot paths that matter.
Why sorug?
| Pillar | What it means |
|---|---|
| Zero-Copy | Canonical ASCII inputs stay borrowed (Backing::Borrowed); heap allocation only on first required mutation (CoW). |
| SIMD / SWAR | 64-bit scheme-prefix jumps + SWAR delimiter scans for short inputs; memchr for longer buffers. |
| Custom Punycode | In-crate Punycode + UTS #46 mapping; membership tables from vendored Unicode UCD (data/ucd/) + idna_overlay.txt — no ICU/idna runtime dep. |
| 891 / 891 WPT | Full pass of the Web Platform Tests urltestdata suite shipped in-tree. |
| 278 / 278 setters | Full pass of WPT setters_tests.json (component mutators). |
forbid(unsafe_code) |
Zero unsafe in library code. Correctness first; speed without memory-safety shortcuts. |
no_std + alloc |
Embedded / WASM friendly (default-features = false). |
Benchmarks
Criterion, Linux, release profile (lto = true, codegen-units = 1). Lower is better (nanoseconds / parse). Measured 2026-08-07 (0.5/0.6 prep; Fast Path ASCII now leads ada).
| Workload | sorug | ada-url | servo/url |
|---|---|---|---|
Fast Path ASCII (https://example.com/api/v1/users) |
28.3 ns | 31.9 ns | 97 ns |
| Complex Query / Fragment | 56.0 ns | 140 ns | 216 ns |
| IDNA / Punycode | 196 ns | 277 ns | 248 ns |
| File Edge Case | 31.1 ns | 95.0 ns | 129 ns |
Reproduce locally:
Numbers are indicative. Absolute values vary by CPU; relative ordering is what we track. IDNA uses an in-tree UTS #46 path with a Latin-1/CJK/kana/Hangul identity fast path; membership tables are built from vendored Unicode UCD (no ICU/
idnacrate).
Quick start
[]
= "0.5"
use Url;
Relative resolution with join / make_relative:
use Url;
let base = parse?;
let joined = base.join?;
assert_eq!;
let target = parse?;
assert_eq!;
Mutate components (WHATWG / WPT setters) and edit the query as form-urlencoded pairs:
use ;
let mut url = parse?;
url.set_pathname;
url.set_search;
assert_eq!;
let mut params = parse;
params.append;
url.set_search_params;
assert_eq!;
Features
| Feature | Default | Purpose |
|---|---|---|
std |
yes | [std::error::Error] for ParseError; memchr std backend |
serde |
no | Serialize / deserialize Url as an href string |
http |
no | Convert between Url and http::Uri (implies std) |
# no_std + alloc
= { = "0.5", = false }
# with serde
= { = "0.5", = ["serde"] }
# with http::Uri bridge
= { = "0.5", = ["http"] }
http feature example:
use Uri;
use Url;
let url = parse.unwrap;
let uri: Uri = url.to_uri.unwrap;
let back = uri_to_url.unwrap;
assert_eq!;
Git dependency (tracking main):
[]
= { = "https://github.com/hocestnonsatis/sorug" }
Current Status & Roadmap
Today (0.5.0)
- Relative URL ops:
join/make_relative/path_segments/path_segments_mut/query_pairs(_mut). - Typed
Host(+Host::parse), rust-url-shaped getters,Hash/Ord, optionalserde/http,no_std+alloc. - File paths, unique opaque origins,
set_ip_host/socket_addrs,SearchParams(incl. value-awarehas/delete/size),parse_with_params. - IDNA: in-tree Punycode + UTS #46; membership tables from vendored Unicode UCD +
data/idna_overlay.txt(Node/WPT). - WPT parser: 891 / 891; WPT setters: 278 / 278.
- Docs: docs.rs/sorug; recipes in docs/cookbook.md.
Breaking (0.3 → 0.4)
Origin::Opaqueis nowOpaque(OpaqueOrigin)with unique nonces — distinct opaque origins no longer compare equal.
Next (toward 1.0; no freeze yet)
- Keep WPT fixtures current via
scripts/refresh-wpt.sh; differential fuzz vs rust-url with documented divergences; weekly long fuzz via.github/workflows/fuzz-long.yml. - Unicode UCD refresh when a new major is ready (
./scripts/refresh-ucd.sh); see data/ucd/README.md. - When API churn stays low: run the 1.0 freeze checklist below.
Semver and MSRV policy
- 0.x: Breaking changes allowed in minor bumps (
0.N → 0.N+1) when called out in CHANGELOG.md. Additive APIs may land in patch or minor. - MSRV: Declared in
Cargo.tomlasrust-version(currently 1.85). MSRV bumps are minor in 0.x (documented in the changelog); CI verifies the declared toolchain. - Features:
std(default),serde,http(impliesstd). Disablingstdis supported (no_std+alloc). - FFI:
sorug-ffiis not on crates.io; ABI may change with the workspace version. Prefer GitHub Release binaries pinned to a tag.
1.0 freeze checklist
Do not cut 1.0 until all boxes are true for a sustained low-churn period (no rush — product work continues on 0.x). Gate notes: docs/api-audit.md.
- Public surface audit signed off:
Url<'a>lifetime,Backingstays public/advanced,Statestaysdoc(hidden),ParseErrorstays two variants — docs/api-audit.md (2026-08-07). - rust-url migration notes complete (
hostvshost_parsed, port setters, origins, lifetimes) — see docs/cookbook.md. - WPT parser + setters green on current fixtures; fuzz smoke + weekly long fuzz green; no open
wpt-freshnessregressions — sustained period required (no rush). - CHANGELOG + docs.rs + Trusted Publishing ready for the freeze cut; FFI stays
publish = false— verify again at freeze time.
Not goals for 1.0: historical non-WHATWG quirk parity; trading forbid(unsafe_code) for micro-wins; adding ICU/idna crates; expanding ParseError; hiding Backing.
C FFI
Optional C bindings live in ffi/ (sorug-ffi, workspace member, not on crates.io). Prebuilt cdylib / staticlib + sorug.h ship on GitHub Releases. The main crate stays forbid(unsafe_code). See ffi/README.md.
Not goals (for now)
- Matching every historical quirk of non-WHATWG parsers.
- Trading
forbid(unsafe_code)for micro-wins.
Design sketch
- Index-based record — component boundaries are
u32offsets into the WHATWGhrefserialization. - Lazy / CoW serialization — borrow when input is already canonical; upgrade to owned on mutation.
- Strict state machine — transitions follow the URL Living Standard basic URL parser.
Testing
See CONTRIBUTING.md for policies, style, and review expectations.
Cookbook
Integration snippets and rust-url migration: docs/cookbook.md. 1.0 freeze gate (no rush): docs/1.0-gate.md.
License
Licensed under either of
at your option.
Code of Conduct
Participation is governed by our Code of Conduct.