# Library Comparison — Parse Only
```bash
cargo bench -p synta-bench --bench comparison --features bench-compare
```
(`library_comparison` Criterion group)
## Implementations
Each library takes a fundamentally different approach to the same problem:
| **synta** | Typed RFC 5280 parse; issuer/subject/extensions stored as `RawDer<'a>` (borrowed byte span — no DN traversal, no allocation at parse time) |
| **cryptography-x509** | PyCA Rust core; deferred-everything — raw DER byte offsets, decode only on first field access |
| **x509-parser** | nom-based; fully eager typed parse — every DN, extension, and value decoded during parse |
| **x509-cert** | RustCrypto; fully eager typed parse — same approach as x509-parser but using the `der` crate |
| **NSS** | `CERT_NewTempCertificate`; formats issuer/subject Distinguished Names into C strings *at parse time*; arena allocation |
| **rust-openssl** | OpenSSL `d2i_X509` via the `openssl` crate's safe Rust bindings |
| **ossl** | OpenSSL `d2i_X509` via partial Rust FFI (Kryoptic project) |
The dominant cost in X.509 parsing is Distinguished Name traversal: a certificate's issuer
and subject each contain a SEQUENCE OF SET OF SEQUENCE with per-attribute OID lookup. synta
defers this entirely by storing the Name as a `RawDer<'a>` — a pointer+length into the
original input with no decoding. cryptography-x509 takes a similar deferred approach. The
nom-based and RustCrypto libraries decode Names eagerly. NSS goes further and formats them
into C strings, which is the dominant fraction of its 16× parse overhead.
## Traditional X.509 Certificates
Five PKITS end-entity certificates (AllCertificatesNoPoliciesTest2,
AllCertificatesSamePoliciesTest×2, AllCertificatesanyPolicyTest11, AnyPolicyTest14EE),
914–968 bytes each:
| cert_00 (NoPolicies) | 483.55 ns | 1426.4 ns | 1826.3 ns | 3032.1 ns | 7883.6 ns |
| cert_01 (SamePolicies-1) | 485.60 ns | 1466.8 ns | 2094.8 ns | 3238.4 ns | 7941.7 ns |
| cert_02 (SamePolicies-2) | 484.66 ns | 1448.2 ns | 2135.0 ns | 3183.5 ns | 8017.1 ns |
| cert_03 (anyPolicy) | 480.88 ns | 1441.0 ns | 1981.2 ns | 3180.3 ns | 7908.4 ns |
| cert_04 (AnyPolicyEE) | 476.91 ns | 1441.1 ns | 1990.7 ns | 3147.8 ns | 7755.7 ns |
| **Average** | **482 ns** | **1445 ns** | **2006 ns** | **3156 ns** | **7901 ns**|
rust-openssl and ossl averaged **15.4 µs** and **16.1 µs** respectively across the five
certs (not shown per-cert to keep the table readable).
synta is **3.0× faster** than cryptography-x509, **4.2× faster** than x509-parser,
**6.6× faster** than x509-cert, and **16× faster** than NSS.
The variation across certs (476–486 ns for synta) reflects differences in extension lists:
certs with more policy extensions contain more bytes in the `extensions` `RawDer` field's
tag+length header, which is the only part synta reads at parse time.
## Field-Level String Formatting
Parse + format issuer DN, subject DN, signature OID, not-before, not-after as strings.
Average over 5 PyCA PKITS test certificates (914–968 bytes):
| **synta** | **~1.55 µs** | baseline |
| x509-parser | ~3.95 µs | ~2.5× slower |
| NSS | ~8.45 µs | ~5.5× slower |
| x509-cert | ~11.35 µs | ~7.3× slower |
| rust-openssl | ~18.20 µs | ~11.7× slower |
synta uses `format_dn()` (single-pass DER walk), `ObjectIdentifier::Display` (zero-copy
dotted-decimal), and `UtcTime`/`GeneralizedTime::Display` for timestamps — no intermediate
allocations for the DN walk, no OID-to-string conversion.