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