Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,49 @@ jobs:
cargo check --locked --all-features
cargo check --locked --no-default-features

# `wasm32-unknown-unknown` has no threads, no clock and no system randomness,
# so a dependency reaching for one of them either fails to compile
# (`getrandom`) or panics at runtime; `wasm32-wasip1` has all three but no
# thread spawning.
wasm:
name: WebAssembly (${{ matrix.target }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [wasm32-unknown-unknown, wasm32-wasip1]

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4
- uses: dtolnay/rust-toolchain@d1031067263f94b142dd6c0ce24c5eb9d02d52a0 # master
with:
toolchain: stable
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Build with default features
run: cargo build --locked --lib --target ${{ matrix.target }}
- name: Build with no default features
run: cargo build --locked --lib --no-default-features --target ${{ matrix.target }}

# `wasm32-wasip1` is the only one of the two that can run the test
# harness: `wasm32-unknown-unknown` has no way to start a process or
# print to stdout, which libtest needs. The tests that panic on purpose
# are `ignore`d for `wasm`, which is `panic = abort`.
- uses: bytecodealliance/actions/wasmtime/setup@9152e710e9f7182e4c29ad218e4f335a7b203613 # v1
if: matrix.target == 'wasm32-wasip1'
- name: Test with default features
if: matrix.target == 'wasm32-wasip1'
env:
# `--dir` grants the guest the `tests/data` the integration tests read.
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime --dir=.
run: cargo test --locked --tests --target ${{ matrix.target }}
- name: Test with no default features
if: matrix.target == 'wasm32-wasip1'
env:
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime --dir=.
run: cargo test --locked --tests --no-default-features --target ${{ matrix.target }}

package:
name: Package
runs-on: ubuntu-latest
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this crate are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- On `wasm32-unknown-unknown`, `ahash` uses its `compile-time-rng` feature instead of the default `runtime-rng`, so `getrandom`, which otherwise requires a backend opt-in from the final binary on that target, is no longer compiled into the library and the crate builds there out of the box. Hash keys are drawn at build time rather than at process start; other targets are unchanged.
- `rayon` is no longer a dependency on `wasm` targets, whatever the features: the target has no threads, so `rayon` only ever ran its single-thread fallback there. The `parallel` feature stays enabled and compiles to the same sequential code as `--no-default-features`, so `wasm` consumers keep it out of their build even when another crate in the graph turns it on. `FastAutomaton::union_all_par` and `FastAutomaton::intersection_all_par` are therefore absent on `wasm`; every other item is unchanged, as is every non-`wasm` target.

### Added
- `ExecutionProfileBuilder::clock` and `ExecutionProfile::with_clock`, plus the `execution_profile::Clock` type (`fn() -> Duration`): the monotonic clock the execution timeout is measured against. It defaults to `std::time::Instant`, so existing profiles behave as before. On `wasm32-unknown-unknown`, where the standard library cannot read the time and a timeout previously panicked inside `Instant::now`, the host supplies one (a binding to `performance.now()`, say) and the timeout works; setting a timeout there without a clock makes `run` panic with a message saying so. A custom clock also makes timeouts deterministic in tests, as the `ExecutionProfile` documentation shows.

### Fixed
- On 32-bit targets (`wasm32`, `i686`, `armv7`, ...), `CharacterOrder::Shuffled` and `PathOrder::Shuffled` drew from the first 2^32 combinations of each path only, because the window indexing them was a `usize`: for `[a-z]{20}` every string shared its first 13 characters. The window is now 64-bit on every target; 64-bit targets are unchanged.

## [1.0.1] - 2026-09-07

A maintenance release covering dependencies and packaging. The public API is unchanged, and no operation returns a different result.
Expand Down
129 changes: 38 additions & 91 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 21 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,39 @@ include = [

[dependencies]
tracing = "0.1"
ahash = "0.8.11"
regex-syntax = "0.8.11"
# `ucd-16` has to match the Unicode database `regex-syntax` carries: a pattern is
# parsed into ranges by `regex-syntax` and named back into a class by
# `regex-charclass`, so a mismatch turns `\p{Greek}` into a raw range list on the
# way out. Checked by `tests/public_api.rs`.
# The feature flag (ucd-*) has to match the Unicode version `regex-syntax` uses.
regex-charclass = { version = "1.2.0", features = ["ucd-16"] }
rayon = { version = "1.10.0", optional = true }
bit-set = "0.11.1"
indexmap = "2.13.0"

# `wasm` has no threads: `rayon` would build, but only as its single-thread
# fallback. The `parallel` feature stays enabled there and compiles to the same
# sequential code as `--no-default-features`, minus the dependency.
[target.'cfg(not(target_family = "wasm"))'.dependencies]
rayon = { version = "1.10.0", optional = true }

# `ahash` draws its hash keys from `getrandom`, which `wasm32-unknown-unknown` has
# no backend for; there it falls back to `compile-time-rng`, drawing them at build
# time instead. The keys being fixed per build makes it in theory vulnerable to
# HashDoS, acceptable as long as it does not serve untrusted data at scale.
[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies]
ahash = "0.8.11"

[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
ahash = { version = "0.8.11", default-features = false, features = ["std", "compile-time-rng"] }

[features]
default = ["parallel"]
parallel = ["dep:rayon"]

[dev-dependencies]
criterion = { version = "0.8", features = ["html_reports"] }
proptest = "1"
proptest = { version = "1", default-features = false, features = ["std", "bit-set"] }
regex = "1.13.1"

[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.8", features = ["html_reports"] }

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Under the hood, every pattern compiles to a finite automaton:
cargo add regexsolver
```

The default `parallel` feature runs unions and intersections of more than 3 operands, and parts of the automaton-to-regex conversion, on [rayon](https://crates.io/crates/rayon). Turn it off for a leaner dependency tree on single-threaded workloads:
The default `parallel` feature runs unions and intersections of more than 3 operands, and parts of the automaton-to-regex conversion, on [rayon](https://crates.io/crates/rayon). It is a no-op on `wasm`, which has no threads and does not depend on rayon at all. Turn it off for a leaner dependency tree on single-threaded workloads:

```toml
regexsolver = { version = "1", default-features = false }
Expand Down
Loading
Loading