Skip to content

Reduce direct dependencies#134

Draft
fschutt wants to merge 14 commits into
yeslogic:masterfrom
fschutt:reduce-deps
Draft

Reduce direct dependencies#134
fschutt wants to merge 14 commits into
yeslogic:masterfrom
fschutt:reduce-deps

Conversation

@fschutt

@fschutt fschutt commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

This PR removes 8 direct dependencies and replaces them with stdlib equivalents or small in-tree implementations. The goal here is to reduce the attack surface for supply-chain attacks by removing unnecessary dependencies if we only use 1 - 2 functions. With default features the transitive crate count drops from 54 → 42; with --no-default-features it drops from 47 → 34.

It is best to review this commit-by-commit, not the entire PR at once - many changes are only mechanical.

Dependencies removed

  • libc: unused dependency — deleted
  • bitreader: inline bit reader in src/binary/read.rs
  • byteorder: replace 3 usages with stdlib from_be_bytes / from_le_bytes
  • crc32fast: only one usage in generate_postscript_name for an edge case: still pulled in via flate2, but can be removed with flate2 feature flag adjustments
  • lazy_static: 3 usages, replaced by std::sync::OnceLock (stable since rustc 1.70)
  • itertools: stdlib Iterator::zip, .collect::<Vec<_>>(), windowed iterators, dedup() via filter()
  • num-traits: only 2 call-sites, inlined wrt. usage

The largest removal / inlining is:

  • pathfinder_geometry: only a few structs with basic geometry ops used, inlined in src/geom.rs

This was also the largest saving in transitive dependencies, but allsorts only uses some definitions and very basic geometry math, so very easy to review.

Overall, I'm obviously not trying to overwhelm people with automated AI PRs, I'm just looking if there's anything we can shed easily and inline to not depend on tons of dependencies. Some deps like ouroboros and enumflags2 still pull in lots of transitive deps.

cargo tree — before (master)

  allsorts v0.16.1
  ├── bitreader v0.3.11
  │   └── cfg-if v1.0.4
  ├── brotli-decompressor v5.0.0
  │   ├── alloc-no-stdlib v2.0.4
  │   └── alloc-stdlib v0.2.2
  │       └── alloc-no-stdlib v2.0.4                                                                                                                                                                          
  ├── byteorder v1.5.0                                                                                                                                                                                          
  ├── crc32fast v1.5.0                                                                                                                                                                                          
  │   └── cfg-if v1.0.4                                                                                                                                                                                         
  ├── encoding_rs v0.8.35                                                                                                                                                                                       
  │   └── cfg-if v1.0.4                                                                                                                                                                                         
  ├── enumflags2 v0.7.12                                                                                                                                                                                        
  │   └── enumflags2_derive v0.7.12 (proc-macro)
  ├── flate2 v1.1.9                                  (optional)                                                                                                                                                  
  │   ├── crc32fast v1.5.0 (*)                                                                                                                                                                                  
  │   └── libz-sys v1.1.28                                                                                                                                                                                      
  ├── glyph-names v0.2.0                                                                                                                                                                                        
  ├── itertools v0.14.0                                                                                                                                                                                         
  │   └── either v1.15.0                                                                                                                                                                                        
  ├── lazy_static v1.5.0                                                                                                                                                                                        
  ├── libc v0.2.186                                                                                                                                                                                             
  ├── log v0.4.29                                                                                                                                                                                               
  ├── num-traits v0.2.19                                                                                                                                                                                        
  │   [build-dependencies]                                                                                                                                                                                      
  │   └── autocfg v1.5.0          
  ├── ouroboros v0.18.5                                                                                                                                                                                         
  ├── pathfinder_geometry v0.5.1                                                                                                                                                                                
  │   ├── log v0.4.29                                                                                                                                                                                         
  │   └── pathfinder_simd v0.5.6                                                                                                                                                                                
  │       [build-dependencies]                                                                                                                                                                                
  │       └── rustc_version v0.4.1                                                                                                                                                                              
  │           └── semver v1.0.28                                  
  ├── rustc-hash v2.1.2                                                                                                                                                                                         
  ├── tinyvec v1.11.0                                                                                                                                                                                           
  ├── ucd-trie v0.1.7                                                                                                                                                                                         
  ├── unicode-canonical-combining-class v1.0.0                                                                                                                                                                  
  ├── unicode-general-category v1.1.0                                                                                                                                                                           
  └── unicode-joining-type v1.0.0                                                                                                                                                                               

cargo tree — after (reduce-deps)

  allsorts v0.16.1                                                                                                                                                                                              
  ├── brotli-decompressor v5.0.0                                  
  │   ├── alloc-no-stdlib v2.0.4                                                                                                                                                                                
  │   └── alloc-stdlib v0.2.2                                     
  │       └── alloc-no-stdlib v2.0.4                                                                                                                                                                          
  ├── encoding_rs v0.8.35                                                                                                                                                                                       
  │   └── cfg-if v1.0.4           
  ├── enumflags2 v0.7.12                                                                                                                                                                                        
  │   └── enumflags2_derive v0.7.12 (proc-macro)                                                                                                                                                                
  ├── flate2 v1.1.9                                   (optional)  
  │   ├── crc32fast v1.5.0                                                                                                                                                                                      
  │   └── libz-sys v1.1.28                                                                                                                                                                                      
  ├── glyph-names v0.2.0          
  ├── log v0.4.29                                                                                                                                                                                               
  ├── ouroboros v0.18.5                                                                                                                                                                                         
  ├── rustc-hash v2.1.2           
  ├── tinyvec v1.11.0                                                                                                                                                                                           
  ├── ucd-trie v0.1.7                                                                                                                                                                                         
  ├── unicode-canonical-combining-class v1.0.0                                                                                                                                                                  
  ├── unicode-general-category v1.1.0
  └── unicode-joining-type v1.0.0                                                                                                                                                                               

Each commit on the branch is a single, self-contained removal; tests pass at every step.

fschutt and others added 14 commits April 25, 2026 17:56
libc was declared in Cargo.toml but never used in src/ or tests/.
`grep -rn 'libc::' src/ tests/` returns no matches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
byteorder was used in exactly three places — a single match arm in
`lookup_offset_index` (`src/cff.rs`) reading u16/u24/u32 big-endian
values from a byte slice. Replace with stdlib `u16::from_be_bytes` /
`u32::from_be_bytes` (using a leading zero byte for the u24 case).
The function already panics on unexpected sizes, so the buffer length
preconditions are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
crc32fast was used in exactly one place — `generate_postscript_name`
in `src/variations.rs` — to compute a hash for the OpenType
"last resort" PostScript-name suffix when the generated name
exceeds 63 characters.

Replace with a small bitwise CRC-32 (IEEE 802.3 / gzip polynomial)
in a new `crc32` module. Output is bit-identical to crc32fast for
this code path, verified against the standard 0xCBF43926 check value.

This is not a hot path — it only runs when name generation overflows —
so a 20-line bitwise loop is preferred over keeping a dedicated CRC
crate as a direct dep. Note that crc32fast remains a transitive dep
of flate2 when zlib/rust backends are enabled; users who disable
flate2 features now get one fewer transitive crate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The lazy_static crate was used in two places:

* `src/cff.rs` — three statics holding CFF default operands whose values
  contain `Real(TinyVec<...>)`, which can't be `const` because
  `TinyVec`'s constructors aren't const.
* `tests/common.rs` — a compiled `Regex` used by glyph-test parsing.

Allsorts' MSRV is 1.83, so `std::sync::OnceLock` (stable since 1.70) is
available and replaces `lazy_static` directly with no extra dependency.
Each former `lazy_static` block becomes a private function that returns
`&'static T` via a function-local `OnceLock`.

`src/cff/cff2.rs` previously imported the static names — switch it to
import the new accessor functions and call them at the use sites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `bitreader` crate was used in a single file, `src/bitmap/cbdt.rs`,
to unpack bit-aligned glyph data for CBDT/EBDT bitmap fonts. Every call
site reads at most 8 bits at a time.

Replace with a small private `BitReader` struct in the same file: a
data slice plus a bit cursor, with a single `read_u8(bits)` method that
pulls into a 16-bit window so it works regardless of byte alignment.

Side benefits:

* The error type is now `ParseError` directly, so the
  `parse_error_from_bitreader_error` adapter and the related
  `.map_err(...)` calls at the four call sites go away.
* The `unreachable!()` arm for "too many bits for type" is no longer
  needed — the new reader takes `u8` and asserts `bits <= 8` in debug.

The full bitmap test suite (10 tests, including
`test_unpack_bit_aligned_data`) passes with the new implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mechanical change across 12 call sites in src/cff.rs, src/bitmap/cbdt.rs,
tests/aots.rs, and tests/cff.rs. `Itertools::collect_vec` is purely a
turbofish-saving convenience wrapper around `Iterator::collect` —
no behavioral difference. First step toward dropping the itertools
direct dep.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two `izip!(a, b, c, d)` invocations in `src/tables/cmap.rs`
(`map_glyph` and `mappings_fn`) are replaced with three chained
`Iterator::zip` calls plus a `map` to flatten the nested tuples into
a 4-tuple. No behavioral difference — `zip` short-circuits on the
shortest input just like `izip!`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two call sites used `Iterator::tuple_windows` to iterate over
consecutive pairs:

* `src/tables/glyf.rs`: walking pairs of `loca` offsets to compute
  per-glyph byte ranges. Replace with an index-based iteration over
  `0..loca.offsets.len() - 1`, calling the existing `get(i)` /
  `get(i + 1)` methods. The `if loca.offsets.len() < 2` guard above
  this loop already rules out underflow.
* `src/cff.rs` (`FDSelect::Format3::fd_index`): walking adjacent
  range entries plus the trailing sentinel. Replace with a `Peekable`
  iterator and a `while let Some` loop so we can look at the next
  entry without consuming it.

Both rewrites preserve identical semantics. Drop the now-unused
`use itertools::Itertools` lines from both files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`gpos_apply` had `lookup_indices.iter().copied().dedup()` to skip
runs of equal consecutive values after `sort_unstable()` on the slice
above. Replace with `Iterator::filter` carrying a `last: Option<u16>`
across calls — a one-liner that collapses runs identically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the last itertools usage — `itertools::Either` in
`src/woff2.rs::Woff2Tables::table_directory` — with a small private
two-variant `Either<L, R>` enum that impls `Iterator` for the
case where both variants share an item type. This is the same shape
itertools provides; the local definition is ~20 lines and avoids a
direct dep just for one type.

Also drop now-unused `use itertools::Itertools` lines from
`src/bitmap/cbdt.rs` (test module), `tests/aots.rs`, and
`tests/cff.rs`, then remove the `itertools` entry from Cargo.toml.

Combined with earlier commits in this PR, this removes the entire
itertools subtree (`itertools` + `either`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The crate was used in two places:

* `src/cff.rs` — five generic bounds of the form
  `F: num::Unsigned + Copy` / `N: num::Unsigned + Copy` on
  `Range<F, N>` impls and helpers. The `num::Unsigned` half is purely
  documentary — the function bodies only use the `From<...>` projections
  to `u16`/`u32`/`usize` (which are listed alongside in the where
  clauses). Drop the `num::Unsigned` part of each bound; `Copy` plus the
  conversion bounds are sufficient for compilation.
* `src/bitmap.rs::bigger_or_closer_to_zero` — generic over
  `V: PartialOrd + num::Signed + num::Zero`. The two callers pass an
  `i16` and an `i32`. Specialise the function to take `i32` and have
  the `i16` caller widen with `i32::from(...)`. The whole-number
  widen-then-compare is bit-identical to the previous behaviour and
  removes the trait soup.

Drop `use num_traits as num` lines and the `num-traits` entry in
Cargo.toml. Also removes the `autocfg` build dep that num-traits
pulls in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a small `geom` module containing only the 2D geometry surface
that allsorts actually uses, as a precursor to dropping the
`pathfinder_geometry` dependency.

The module provides:

* `Vector2F`, `Vector2I` (with `vec2f` / `vec2i` constructors).
* `RectF`, `RectI` built from a `(origin, lower_right)` pair —
  matching pathfinder's non-normalising `from_points` contract so
  callers can keep their existing min/max conventions.
* `LineSegment2F` exposing `from()` / `to()`.
* `Matrix2x2F` and `Transform2F` with `row_major` constructors,
  scale matrix construction, point-vs-rect transforms, and `Mul`
  operator overloads for `Transform2F * Vector2F` (point) and
  `Transform2F * RectF` (axis-aligned bounding box of the four
  transformed corners — same semantics pathfinder uses).

Six unit tests pin the arithmetic, rect operations, matrix scale,
point/rect transforms, and `round_out` / `union` semantics.

This commit only adds the module — no call sites are migrated yet.
Subsequent commits replace `pathfinder_geometry` imports file-by-file
and finally drop the dependency from `Cargo.toml`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace every `use pathfinder_geometry::...` import in `src/` with the
equivalent symbol from the new `crate::geom` module. No external API
shape change beyond the eventual swap of the public types in the
`OutlineSink` trait, `colr_clip_box`, etc. — those types now come from
`crate::geom` and have identical method signatures and operator
behaviour to the pathfinder versions.

Files migrated:

* `src/font.rs` — `NullSink` impl and `colr_clip_box` return type.
* `src/outline.rs` — `OutlineSink` trait signatures, `BBox` /
  `BoundingBoxSink`, the `BezierCoefficients` curve helpers, and the
  doctest imports (`use allsorts::geom::{LineSegment2F, Vector2F};`).
* `src/cff/outline.rs` — Type 2 CharString → `OutlineSink` translation.
* `src/tables/glyf.rs` — `From<CompositeGlyphScale> for Matrix2x2F`.
* `src/tables/glyf/outline.rs` — glyf outline visitor + composite
  transform composition; the inner `contour` mod and the test module.
* `src/tables/glyf/variation.rs` — gvar deltas, composite glyph
  bounding boxes, `Transform2F * RectF` for child-glyph bbox
  composition, plus the test module's `vec2i`.
* `src/tables/colr.rs` — COLR paint visitor's `Transform2F::row_major`
  call and `OutlineSink` impls on the debug visitor.
* `src/variations.rs` — `RectI` bounding-box accumulation in the
  variable-font instancing path and its local `union_rect` helper.

`src/geom.rs` gains the small set of methods/operators needed by the
above sites that weren't in the initial commit:

* `Vector2F::round`, `Vector2F::to_i32`, `Vector2F::lerp`,
  `AddAssign<Vector2F>`.
* `Vector2I::min` / `Vector2I::max` (used by `union_rect`).
* `RectF::to_i32`, plus pathfinder-compatible `origin_x` / `origin_y`
  / `width` / `height` aliases.
* `RectI::origin` / `RectI::lower_right` accessors.
* `LineSegment2F::from_x` / `from_y` / `to_x` / `to_y` accessors used
  by tests and the glyf visitor's debug path.

Full test suite (633+ tests across 15 binaries plus doctests) passes.

The `pathfinder_geometry` direct dep and its public re-export are
removed in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
With every internal use site now pointing at `crate::geom` (previous
commit), the `pathfinder_geometry` entry can be removed from
`Cargo.toml`, the `pub use pathfinder_geometry` re-export from
`src/lib.rs`, and the corresponding `outline` row from the feature
table in the crate-level docs. (The `outline` Cargo feature itself was
already a documented no-op and is left alone — it remains a stable
feature flag that users may pass without effect.)

Tree impact (consumers see these crates leave allsorts' subgraph):

* `pathfinder_geometry`
* `pathfinder_simd`
* `rustc_version` (build-dep of pathfinder_simd)
* `semver`           (build-dep, transitively via rustc_version)
* the `pathfinder_simd` build script

For consumers of allsorts:

* **SemVer break** — anyone who imported `pathfinder_geometry::*` via
  `allsorts::pathfinder_geometry::...` or who relied on the
  `OutlineSink` trait / `colr_clip_box` / `Font::bounding_box`
  signatures using pathfinder's `Vector2F`/`RectF`/`LineSegment2F`
  must switch to `allsorts::geom::*`. The methods, fields, and
  operator semantics are identical; only the path changes.

* No runtime behavioural change — `crate::geom` is plain `f32` math
  with the same semantics as pathfinder_geometry's CPU paths. The
  pathfinder SIMD lanes were never enabled by allsorts.

Full test suite (633+ tests) still passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fschutt

fschutt commented Apr 25, 2026

Copy link
Copy Markdown
Contributor Author

Note: This will break semver only because it's now allsorts::geom::Vector2F instead of pathfinder_geometry::Vector2F and so on.

fschutt added a commit to fschutt/allsorts-azul that referenced this pull request May 23, 2026
Cherry-picks the dependency-removal commits from PR yeslogic#134 (reduce-deps)
WITHOUT the upstream churn / enumflags2 swap, so the public API is
unchanged. Drops 7 direct dependencies (23 -> 16): libc, byteorder,
crc32fast, lazy_static (-> std OnceLock), bitreader, itertools, num-traits.
pathfinder_geometry is intentionally retained (publicly re-exported via
'pub use pathfinder_geometry', used by azul-layout's outline path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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