Skip to main content

infotheory/backends/
mod.rs

1//! Backend discovery helpers and canonical backend naming.
2//!
3//! This module provides:
4//! - canonical backend name resolution for CLI/Python inputs,
5//! - feature-aware availability reporting,
6//! - exported lists of enabled backend families.
7
8/// Online probability calibration wrapper for rate predictors.
9pub mod calibration;
10pub mod ctw;
11/// Shared policy parser/compiler for online LLM backends.
12pub mod llm_policy;
13/// Mamba-1 based rate/compression backend.
14#[cfg(feature = "backend-mamba")]
15pub mod mambazip;
16/// Contiguous/sparse local match predictor primitives.
17pub mod match_model;
18/// Particle-latent rate backend.
19pub mod particle;
20/// Bounded-memory PPMD-style byte model.
21pub mod ppmd;
22pub mod rosaplus;
23/// RWKV7-based rate/compression backend.
24#[cfg(feature = "backend-rwkv")]
25pub mod rwkvzip;
26/// Exact online Sequitur grammar backend with byte-level predictive readout.
27pub mod sequitur;
28/// Sparse/gapped match predictor that wraps [`match_model`].
29pub mod sparse_match;
30/// Text/repeat context feature extraction for adaptive backends.
31pub mod text_context;
32pub mod zpaq_rate;
33use crate::coders::CoderType;
34
35/// Outcome of resolving a backend alias to a canonical backend name.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum BackendAvailability {
38    /// Backend is compiled and available.
39    Enabled(&'static str),
40    /// Backend alias is recognized, but the required Cargo feature is disabled.
41    Disabled {
42        /// Canonical backend name.
43        canonical: &'static str,
44        /// Cargo feature needed to enable this backend.
45        feature: &'static str,
46    },
47}
48
49/// Canonical names for rate backends recognized by CLI/API alias resolution.
50pub const AVAILABLE_RATE_BACKENDS: &[&str] = &[
51    "rosaplus",
52    "ctw",
53    "fac-ctw",
54    "match",
55    "sparse-match",
56    "ppmd",
57    "sequitur",
58    "calibrated",
59    #[cfg(feature = "backend-mamba")]
60    "mamba",
61    #[cfg(feature = "backend-rwkv")]
62    "rwkv7",
63    #[cfg(feature = "backend-zpaq")]
64    "zpaq",
65    "mixture",
66    "particle",
67];
68
69/// Canonical names for available compression backends in this build.
70pub const AVAILABLE_COMPRESSION_BACKENDS: &[&str] = &[
71    #[cfg(feature = "backend-zpaq")]
72    "zpaq",
73    "rate-ac",
74    "rate-rans",
75    #[cfg(feature = "backend-rwkv")]
76    "rwkv7",
77];
78
79/// Resolve a user-provided rate backend alias to a canonical backend name.
80///
81/// Returns `None` when the alias is unknown, and `BackendAvailability::Disabled`
82/// when known but not enabled in the current feature set.
83pub fn resolve_rate_backend_name(input: &str) -> Option<BackendAvailability> {
84    let key = input.trim().to_ascii_lowercase();
85    match key.as_str() {
86        "rosaplus" | "rosa" => Some(BackendAvailability::Enabled("rosaplus")),
87        "ctw" => Some(BackendAvailability::Enabled("ctw")),
88        "fac-ctw" | "facctw" => Some(BackendAvailability::Enabled("fac-ctw")),
89        "match" => Some(BackendAvailability::Enabled("match")),
90        "sparse-match" | "sparse_match" | "sparsematch" => {
91            Some(BackendAvailability::Enabled("sparse-match"))
92        }
93        "ppmd" | "ppm" => Some(BackendAvailability::Enabled("ppmd")),
94        "sequitur" => Some(BackendAvailability::Enabled("sequitur")),
95        "calibrated" | "cal" => Some(BackendAvailability::Enabled("calibrated")),
96        "zpaq" => {
97            if cfg!(feature = "backend-zpaq") {
98                Some(BackendAvailability::Enabled("zpaq"))
99            } else {
100                Some(BackendAvailability::Disabled {
101                    canonical: "zpaq",
102                    feature: "backend-zpaq",
103                })
104            }
105        }
106        "mixture" | "mix" => Some(BackendAvailability::Enabled("mixture")),
107        "particle" | "particles" => Some(BackendAvailability::Enabled("particle")),
108        "mamba" | "mamba1" => {
109            if cfg!(feature = "backend-mamba") {
110                Some(BackendAvailability::Enabled("mamba"))
111            } else {
112                Some(BackendAvailability::Disabled {
113                    canonical: "mamba",
114                    feature: "backend-mamba",
115                })
116            }
117        }
118        "rwkv7" | "rwkv" => {
119            if cfg!(feature = "backend-rwkv") {
120                Some(BackendAvailability::Enabled("rwkv7"))
121            } else {
122                Some(BackendAvailability::Disabled {
123                    canonical: "rwkv7",
124                    feature: "backend-rwkv",
125                })
126            }
127        }
128        _ => None,
129    }
130}
131
132/// Resolve a user-provided compression backend alias to a canonical backend name.
133///
134/// Returns `None` when the alias is unknown, and `BackendAvailability::Disabled`
135/// when known but not enabled in the current feature set.
136pub fn resolve_compression_backend_name(input: &str) -> Option<BackendAvailability> {
137    let key = input.trim().to_ascii_lowercase();
138    match key.as_str() {
139        "zpaq" => {
140            if cfg!(feature = "backend-zpaq") {
141                Some(BackendAvailability::Enabled("zpaq"))
142            } else {
143                Some(BackendAvailability::Disabled {
144                    canonical: "zpaq",
145                    feature: "backend-zpaq",
146                })
147            }
148        }
149        "rwkv7" | "rwkv" => {
150            if cfg!(feature = "backend-rwkv") {
151                Some(BackendAvailability::Enabled("rwkv7"))
152            } else {
153                Some(BackendAvailability::Disabled {
154                    canonical: "rwkv7",
155                    feature: "backend-rwkv",
156                })
157            }
158        }
159        "rate-ac" | "rate_ac" | "rateac" => Some(BackendAvailability::Enabled("rate-ac")),
160        "rate-rans" | "rate_rans" | "raterans" => Some(BackendAvailability::Enabled("rate-rans")),
161        _ => None,
162    }
163}
164
165/// Parse a generic entropy coder alias (`"ac"`/`"rans"`).
166pub fn parse_rate_coder(v: &str) -> Option<CoderType> {
167    match v {
168        "ac" | "AC" => Some(CoderType::AC),
169        "rans" | "RANS" | "rANS" => Some(CoderType::RANS),
170        _ => None,
171    }
172}
173
174/// Parse an RWKV entropy coder alias (`"ac"`/`"rans"`).
175#[cfg(feature = "backend-rwkv")]
176pub fn parse_rwkv7_coder(v: &str) -> Option<CoderType> {
177    parse_rate_coder(v)
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn resolve_rate_backend_name_canonicalizes_aliases() {
186        assert_eq!(
187            resolve_rate_backend_name("  Rosa  "),
188            Some(BackendAvailability::Enabled("rosaplus"))
189        );
190        assert_eq!(
191            resolve_rate_backend_name("facctw"),
192            Some(BackendAvailability::Enabled("fac-ctw"))
193        );
194        assert_eq!(
195            resolve_rate_backend_name("mix"),
196            Some(BackendAvailability::Enabled("mixture"))
197        );
198        assert_eq!(
199            resolve_rate_backend_name("sequitur"),
200            Some(BackendAvailability::Enabled("sequitur"))
201        );
202        assert_eq!(resolve_rate_backend_name("unknown"), None);
203    }
204
205    #[test]
206    fn resolve_rate_backend_name_reports_feature_disabled() {
207        if cfg!(feature = "backend-zpaq") {
208            assert_eq!(
209                resolve_rate_backend_name("zpaq"),
210                Some(BackendAvailability::Enabled("zpaq"))
211            );
212        } else {
213            assert_eq!(
214                resolve_rate_backend_name("zpaq"),
215                Some(BackendAvailability::Disabled {
216                    canonical: "zpaq",
217                    feature: "backend-zpaq",
218                })
219            );
220        }
221
222        if cfg!(feature = "backend-rwkv") {
223            assert_eq!(
224                resolve_rate_backend_name("rwkv7"),
225                Some(BackendAvailability::Enabled("rwkv7"))
226            );
227        } else {
228            assert_eq!(
229                resolve_rate_backend_name("rwkv"),
230                Some(BackendAvailability::Disabled {
231                    canonical: "rwkv7",
232                    feature: "backend-rwkv",
233                })
234            );
235        }
236
237        if cfg!(feature = "backend-mamba") {
238            assert_eq!(
239                resolve_rate_backend_name("mamba1"),
240                Some(BackendAvailability::Enabled("mamba"))
241            );
242        } else {
243            assert_eq!(
244                resolve_rate_backend_name("mamba"),
245                Some(BackendAvailability::Disabled {
246                    canonical: "mamba",
247                    feature: "backend-mamba",
248                })
249            );
250        }
251    }
252
253    #[test]
254    fn resolve_compression_backend_name_canonicalizes_aliases() {
255        assert_eq!(resolve_compression_backend_name("unknown"), None);
256
257        if cfg!(feature = "backend-zpaq") {
258            assert_eq!(
259                resolve_compression_backend_name("zpaq"),
260                Some(BackendAvailability::Enabled("zpaq"))
261            );
262        } else {
263            assert_eq!(
264                resolve_compression_backend_name("zpaq"),
265                Some(BackendAvailability::Disabled {
266                    canonical: "zpaq",
267                    feature: "backend-zpaq",
268                })
269            );
270        }
271
272        assert_eq!(
273            resolve_compression_backend_name("rate_ac"),
274            Some(BackendAvailability::Enabled("rate-ac"))
275        );
276        assert_eq!(
277            resolve_compression_backend_name("raterans"),
278            Some(BackendAvailability::Enabled("rate-rans"))
279        );
280
281        if cfg!(feature = "backend-rwkv") {
282            assert_eq!(
283                resolve_compression_backend_name("rwkv"),
284                Some(BackendAvailability::Enabled("rwkv7"))
285            );
286        }
287    }
288
289    #[cfg(feature = "backend-rwkv")]
290    #[test]
291    fn parse_rwkv7_coder_accepts_common_aliases() {
292        assert_eq!(parse_rwkv7_coder("ac"), Some(CoderType::AC));
293        assert_eq!(parse_rwkv7_coder("AC"), Some(CoderType::AC));
294        assert_eq!(parse_rwkv7_coder("rans"), Some(CoderType::RANS));
295        assert_eq!(parse_rwkv7_coder("RANS"), Some(CoderType::RANS));
296        assert_eq!(parse_rwkv7_coder("nope"), None);
297    }
298}