Reduce direct dependencies#134
Draft
fschutt wants to merge 14 commits into
Draft
Conversation
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>
Contributor
Author
|
Note: This will break semver only because it's now |
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-featuresit 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 — deletedbitreader: inline bit reader insrc/binary/read.rsbyteorder: replace 3 usages with stdlibfrom_be_bytes/from_le_bytescrc32fast: only one usage ingenerate_postscript_namefor an edge case: still pulled in via flate2, but can be removed with flate2 feature flag adjustmentslazy_static: 3 usages, replaced bystd::sync::OnceLock(stable since rustc 1.70)itertools: stdlibIterator::zip,.collect::<Vec<_>>(), windowed iterators,dedup()viafilter()num-traits: only 2 call-sites, inlined wrt. usageThe largest removal / inlining is:
pathfinder_geometry: only a few structs with basic geometry ops used, inlined insrc/geom.rsThis 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
ouroborosandenumflags2still pull in lots of transitive deps.cargo tree — before (master)
cargo tree — after (reduce-deps)
Each commit on the branch is a single, self-contained removal; tests pass at every step.