From 5678e0ba37ce2c5c8161f0d48505e7993967e30c Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 20 Aug 2026 03:38:10 +0800 Subject: feat: add dispatch strategy features with auto-selection and benchmark support Add `dispatch_linear`, `dispatch_tree`, and `dispatch_phf` as mutually exclusive features for selecting the command dispatch strategy. When no strategy feature is enabled, an auto-selection heuristic picks the optimal strategy based on command table characteristics (size, name length, nesting depth). Add the `bench_support` feature to compile all three generators for the benchmark harness, and introduce `bench_cell!` to generate benchmark cells comparing strategies. The PHF generator uses a CHD minimal perfect hash for O(1) lookup independent of table size, while the trie generator was refactored to use a shared fallback method, keeping code size linear in table size. --- mingling_macros/src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'mingling_macros/src/lib.rs') diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 1aaedb1..1f4c44d 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -10,6 +10,19 @@ #![deny(clippy::nursery)] #![allow(clippy::redundant_pub_crate)] +// Dispatch strategies are mutually exclusive: `dispatch_linear`, `dispatch_tree`, +// and `dispatch_phf` each select the matching strategy generated by +// `gen_program!`. Enabling more than one would make the selection ambiguous; +// enabling none selects the automatic strategy (currently the linear list). +#[cfg(any( + all(feature = "dispatch_linear", feature = "dispatch_tree"), + all(feature = "dispatch_linear", feature = "dispatch_phf"), + all(feature = "dispatch_tree", feature = "dispatch_phf"), +))] +compile_error!( + "the `dispatch_linear`, `dispatch_tree`, and `dispatch_phf` features are mutually exclusive: enable at most one (none = auto)" +); + use proc_macro::TokenStream; use std::collections::BTreeSet; use std::sync::Mutex; @@ -20,6 +33,9 @@ mod derive; mod func; mod systems; +#[cfg(feature = "bench_support")] +mod bench_cell; + #[cfg(any(feature = "comp", feature = "pathf"))] mod build; @@ -1018,9 +1034,12 @@ pub fn register_metadata(input: TokenStream) -> TokenStream { /// This macro is called internally by `dispatcher!` and `dispatcher_clap!`. /// Each call stores the node name into the global `COMPILE_TIME_DISPATCHERS` /// registry and generates a static variable for the dispatcher instance. This -/// data is later consumed by `gen_program!` to generate command matching: a -/// character-level **trie** when the `dispatch_tree` feature is enabled, or a -/// linear longest-prefix list otherwise. +/// data is later consumed by `gen_program!` to generate command matching: +/// a **minimal perfect hash** (`dispatch_phf` feature), a character-level +/// **trie** (`dispatch_tree` feature), or a linear longest-prefix list +/// (`dispatch_linear` feature; with no dispatch feature enabled, "auto" mode +/// picks the best strategy from the command table). +/// The three dispatch features are mutually exclusive. /// /// The trie dispatch works by grouping commands by their character prefix, /// enabling O(n) lookup (where n is input length) instead of linear iteration @@ -1037,12 +1056,27 @@ pub fn register_metadata(input: TokenStream) -> TokenStream { /// # See also /// /// - `dispatcher!` — The primary way to declare dispatchers (calls this internally). -/// - `dispatch_tree_gen` / `dispatch_list_gen` modules — The matching-strategy generators. +/// - `dispatch_tree_gen` / `dispatch_list_gen` / `dispatch_phf_gen` modules — The matching-strategy generators. #[proc_macro] pub fn register_dispatcher(input: TokenStream) -> TokenStream { func::register_dispatcher::register_dispatcher(input) } +/// Generates a benchmark cell for the workspace-internal `mingling_bench` +/// harness (requires the `bench_support` feature). +/// +/// Input: `bench_cell!(strategy, module_name, "command" => DispType, ...)`. +/// `strategy` is one of `dispatch_linear`, `dispatch_tree`, `dispatch_phf`. +/// The expansion defines a +/// module containing per-entry dispatcher structs, the `crate::BenchDispatch` +/// impl (with the strategy-specific generated `dispatch_args`), the +/// command-name table, and a `measure` function that times hit/miss corpora. +#[cfg(feature = "bench_support")] +#[proc_macro] +pub fn bench_cell(input: TokenStream) -> TokenStream { + bench_cell::bench_cell_impl(input) +} + /// Declares a help rendering function for an entry type. /// /// The `#[help]` attribute converts a function into a help provider. Help -- cgit