Skip to main content

limnifs_write/config/
profile.rs

1//! Compression profiles — predefined codec strategies for different goals.
2//!
3//! A [`CompressionProfile`] bundles codec selection, parameters, tournament
4//! behavior, and chunking into a single named configuration. Four built-in
5//! profiles cover the main use cases; users can define custom profiles via
6//! TOML.
7//!
8//! ## Built-in profiles
9//!
10//! | Profile | Goal | Create speed | Ratio | vs SquashFS | vs DwarFS |
11//! |---------|------|-------------|-------|------------|-----------|
12//! | `max-ratio` | Smallest output | Slow | Best | Wins ratio | Ties ratio |
13//! | `max-speed` | Fastest create | Match SquashFS | OK | Ties speed | Wins speed |
14//! | `balanced` | Good trade-off | Medium | Good | Wins ratio | Wins speed |
15//! | `competitive` | Beat both | Fast | Best-of-both | **Wins both** | **Wins both** |
16//!
17//! ## Usage
18//!
19//! ```toml
20//! # Use a built-in profile
21//! profile = "competitive"
22//!
23//! # Or define a custom profile inline
24//! [profile]
25//! name = "my-custom"
26//! text_codec = "brotli"
27//! brotli_quality = 7
28//! binary_codec = "lz4"
29//! chunk_avg_size = 32768
30//! tournament = "none"
31//! ```
32
33#![allow(warnings)]
34
35use serde::{Deserialize, Serialize};
36
37use crate::config::{
38    ChunkingConfig, CodecTunables, Defaults, DictionaryConfig, EncryptionConfig, TournamentConfig,
39    WriteConfig,
40};
41
42/// Built-in profile names.
43pub const MAX_RATIO: &str = "max-ratio";
44pub const MAX_SPEED: &str = "max-speed";
45pub const BALANCED: &str = "balanced";
46pub const COMPETITIVE: &str = "competitive";
47pub const MAX_READ: &str = "max-read";
48pub const MAX_WRITE: &str = "max-write";
49pub const MAX_WRITE_RW: &str = "max-write-rw";
50pub const MAX_READ_RW: &str = "max-read-rw";
51pub const BALANCED_RW: &str = "balanced-rw";
52
53/// Select a built-in profile by name. Returns a complete [`WriteConfig`]
54/// configured for that profile's strategy.
55#[must_use]
56pub fn select(name: &str) -> Option<WriteConfig> {
57    match name {
58        MAX_RATIO => Some(max_ratio()),
59        MAX_SPEED => Some(max_speed()),
60        BALANCED => Some(balanced()),
61        COMPETITIVE => Some(competitive()),
62        MAX_READ => Some(max_read()),
63        MAX_WRITE => Some(max_write()),
64        MAX_WRITE_RW => Some(max_write_rw()),
65        MAX_READ_RW => Some(max_read_rw()),
66        BALANCED_RW => Some(balanced_rw()),
67        _ => None,
68    }
69}
70
71/// Maximum compression ratio. Tries every applicable codec per drop,
72/// picks the smallest. Slowest create, smallest output.
73///
74/// - Text: Brotli q11 + LZMA + PPMd7 (256 MB budget) tournament
75/// - Binary: ZSTD L19 + LZMA tournament
76/// - Categorizers: all enabled (FLAC, Rice++, FSST+Brotli)
77/// - Chunks: 64 KB (better cross-chunk pattern matching)
78/// - Whole-file max: 256 MB
79#[must_use]
80pub fn max_ratio() -> WriteConfig {
81    WriteConfig {
82        defaults: Defaults {
83            text_codec: "brotli".into(),
84            binary_codec: "zstd".into(),
85            metadata_codec: "brotli".into(),
86            metadata_quality: 11,
87            inline_threshold: 8192,
88        },
89        categorizers: crate::config::defaults::all_v0_1(),
90        chunking: ChunkingConfig {
91            name: "fastcdc".into(),
92            avg_chunk_size: 65_536,
93            min_chunk_size: 8192,
94            max_chunk_size: 262_144,
95        },
96        tournament: TournamentConfig {
97            codecs: vec![
98                "store".into(),
99                "lz4".into(),
100                "lz4-hc".into(),
101                "zstd".into(),
102                "brotli".into(),
103                "ppmd".into(),
104                "bzip2".into(),
105            ],
106            min_size_threshold: 256,
107            skip_for_binary: false,
108            short_circuit_threshold: 0,
109        },
110        codec_tunables: CodecTunables {
111            ppmd7: crate::config::Ppmd7Tunables {
112                order: 6,
113                memory_budget_mb: 256,
114            },
115            ppmd8: crate::config::Ppmd8Tunables {
116                order: 8,
117                memory_budget_mb: 128,
118            },
119            brotli: crate::config::BrotliTunables {
120                quality: 11,
121                window: 24,
122            },
123            lzma: crate::config::LzmaTunables {
124                lc: 3,
125                lp: 0,
126                pb: 2,
127                dict_size_mb: 64,
128                use_optimal_parser: true,
129            },
130            bzip2: crate::config::Bzip2Tunables { block_size_kb: 900 },
131        },
132        encryption: EncryptionConfig {
133            aead: "chacha20-poly1305".into(),
134            key_wrap: "x25519-hkdf".into(),
135        },
136        dictionaries: DictionaryConfig {
137            enabled: true,
138            min_class_size: 50,
139            max_dict_size: 131_072,
140            trainer: "frequency".into(),
141        },
142        mode: crate::config::ImageMode::ReadOnly,
143        write_codec: "lz4".into(),
144        turnover_threshold: 0,
145        skip_chunking: false,
146    }
147}
148
149/// Maximum speed. Single-codec per content class, no tournament.
150/// Matches SquashFS LZ4 speed on binary data.
151///
152/// - Text: LZ4 (instant)
153/// - Binary: LZ4 (instant)
154/// - Categorizers: disabled (no FLAC, no Rice++)
155/// - Tournament: none (classify once, compress once)
156/// - Chunks: 4 KB (maximum parallelism)
157#[must_use]
158pub fn max_speed() -> WriteConfig {
159    WriteConfig {
160        defaults: Defaults {
161            text_codec: "lz4".into(),
162            binary_codec: "lz4".into(),
163            metadata_codec: "lz4".into(),
164            metadata_quality: 1,
165            inline_threshold: 4096,
166        },
167        categorizers: vec![],
168        chunking: ChunkingConfig {
169            name: "fastcdc".into(),
170            avg_chunk_size: 4096,
171            min_chunk_size: 512,
172            max_chunk_size: 16_384,
173        },
174        tournament: TournamentConfig {
175            codecs: vec!["store".into(), "lz4".into()],
176            min_size_threshold: 0,
177            skip_for_binary: true,
178            short_circuit_threshold: 500,
179        },
180        codec_tunables: CodecTunables {
181            brotli: crate::config::BrotliTunables {
182                quality: 0,
183                window: 10,
184            },
185            ..CodecTunables::default()
186        },
187        encryption: EncryptionConfig {
188            aead: "chacha20-poly1305".into(),
189            key_wrap: "x25519-hkdf".into(),
190        },
191        dictionaries: DictionaryConfig {
192            enabled: false,
193            min_class_size: 0,
194            max_dict_size: 0,
195            trainer: "frequency".into(),
196        },
197        mode: crate::config::ImageMode::ReadOnly,
198        write_codec: "lz4".into(),
199        turnover_threshold: 0,
200        skip_chunking: false,
201    }
202}
203
204/// Balanced profile. Good ratio + good speed for general use.
205///
206/// - Text: Brotli q5 (fast, good ratio)
207/// - Binary: LZ4 (fast)
208/// - Categorizers: FLAC for small audio, skip large
209/// - Tournament: Brotli + ZSTD only
210/// - Chunks: 16 KB
211#[must_use]
212pub fn balanced() -> WriteConfig {
213    WriteConfig {
214        defaults: Defaults {
215            text_codec: "brotli".into(),
216            binary_codec: "lz4".into(),
217            metadata_codec: "zstd".into(),
218            metadata_quality: 3,
219            inline_threshold: 4096,
220        },
221        categorizers: crate::config::defaults::all_v0_1(),
222        chunking: ChunkingConfig {
223            name: "fastcdc".into(),
224            avg_chunk_size: 16_384,
225            min_chunk_size: 2048,
226            max_chunk_size: 65_536,
227        },
228        tournament: TournamentConfig {
229            codecs: vec!["store".into(), "lz4".into(), "brotli".into()],
230            min_size_threshold: 256,
231            skip_for_binary: true,
232            short_circuit_threshold: 250,
233        },
234        codec_tunables: CodecTunables {
235            brotli: crate::config::BrotliTunables {
236                quality: 5,
237                window: 22,
238            },
239            ..CodecTunables::default()
240        },
241        encryption: EncryptionConfig {
242            aead: "chacha20-poly1305".into(),
243            key_wrap: "x25519-hkdf".into(),
244        },
245        dictionaries: DictionaryConfig {
246            enabled: true,
247            min_class_size: 100,
248            max_dict_size: 65_536,
249            trainer: "frequency".into(),
250        },
251        mode: crate::config::ImageMode::ReadOnly,
252        write_codec: "lz4".into(),
253        turnover_threshold: 0,
254        skip_chunking: false,
255    }
256}
257
258/// Competitive profile — beat SquashFS on ratio AND DwarFS on speed.
259///
260/// Uses ZSTD L1 for text (5x faster compress than Brotli, 3x faster
261/// decompress, 3x better ratio than SquashFS LZ4). LZ4 for binary.
262#[must_use]
263pub fn competitive() -> WriteConfig {
264    WriteConfig {
265        defaults: Defaults {
266            text_codec: "zstd".into(),
267            binary_codec: "lz4".into(),
268            metadata_codec: "zstd".into(),
269            metadata_quality: 3,
270            inline_threshold: 4096,
271        },
272        categorizers: crate::config::defaults::all_v0_1(),
273        chunking: ChunkingConfig {
274            name: "fastcdc".into(),
275            avg_chunk_size: 8192,
276            min_chunk_size: 1024,
277            max_chunk_size: 65_536,
278        },
279        tournament: TournamentConfig {
280            codecs: vec!["store".into(), "lz4".into(), "brotli".into()],
281            min_size_threshold: 0,
282            skip_for_binary: true,
283            short_circuit_threshold: 250,
284        },
285        codec_tunables: CodecTunables {
286            brotli: crate::config::BrotliTunables {
287                quality: 5,
288                window: 22,
289            },
290            ..CodecTunables::default()
291        },
292        encryption: EncryptionConfig {
293            aead: "chacha20-poly1305".into(),
294            key_wrap: "x25519-hkdf".into(),
295        },
296        dictionaries: DictionaryConfig {
297            enabled: false,
298            min_class_size: 0,
299            max_dict_size: 0,
300            trainer: "frequency".into(),
301        },
302        mode: crate::config::ImageMode::ReadOnly,
303        write_codec: "lz4".into(),
304        turnover_threshold: 0,
305        skip_chunking: false,
306    }
307}
308
309/// Maximum read profile — optimized for read-heavy workloads (write
310/// once, read many). Uses codecs with the best ratio that still
311/// decompresses quickly. Write cost is amortised over many reads.
312///
313/// - Text/Binary: ZSTD L19 (best ratio among fast-decompress codecs;
314///   ZSTD decompresses at ~1500 MB/s vs Brotli's ~500 MB/s)
315/// - Metadata: ZSTD L19
316/// - Categorizers: enabled (FLAC, Rice++ for best ratio per file type)
317/// - Chunks: 64 KB (fewer drops = fewer slab lookups during extract)
318/// - Inline threshold: 8192 (more inline = fewer slab reads)
319#[must_use]
320pub fn max_read() -> WriteConfig {
321    WriteConfig {
322        defaults: Defaults {
323            text_codec: "zstd".into(),
324            binary_codec: "zstd".into(),
325            metadata_codec: "zstd".into(),
326            metadata_quality: 11,
327            inline_threshold: 8192,
328        },
329        categorizers: crate::config::defaults::all_v0_1(),
330        chunking: ChunkingConfig {
331            name: "fastcdc".into(),
332            avg_chunk_size: 65_536,
333            min_chunk_size: 8192,
334            max_chunk_size: 262_144,
335        },
336        tournament: TournamentConfig {
337            codecs: vec!["store".into(), "lz4".into(), "zstd".into(), "brotli".into()],
338            min_size_threshold: 256,
339            skip_for_binary: false,
340            short_circuit_threshold: 200,
341        },
342        codec_tunables: CodecTunables {
343            brotli: crate::config::BrotliTunables {
344                quality: 11,
345                window: 22,
346            },
347            lzma: crate::config::LzmaTunables {
348                dict_size_mb: 64,
349                use_optimal_parser: true,
350                ..crate::config::LzmaTunables::default()
351            },
352            ..CodecTunables::default()
353        },
354        encryption: EncryptionConfig {
355            aead: "chacha20-poly1305".into(),
356            key_wrap: "x25519-hkdf".into(),
357        },
358        dictionaries: DictionaryConfig {
359            enabled: true,
360            min_class_size: 50,
361            max_dict_size: 131_072,
362            trainer: "frequency".into(),
363        },
364        mode: crate::config::ImageMode::ReadOnly,
365        write_codec: "lz4".into(),
366        turnover_threshold: 0,
367        skip_chunking: false,
368    }
369}
370
371/// Maximum write profile — optimized for write-heavy workloads where
372/// write latency matters more than ratio. Uses the fastest possible
373/// compression (LZ4 at ~1 GB/s) and skips all categorization/tournament
374/// overhead.
375///
376/// - Text/Binary/Metadata: LZ4 (fastest compress AND decompress)
377/// - Categorizers: disabled (zero categorization overhead)
378/// - Tournament: none (classify once, compress once)
379/// - Chunks: 128 KB (minimal per-chunk overhead)
380#[must_use]
381pub fn max_write() -> WriteConfig {
382    WriteConfig {
383        defaults: Defaults {
384            text_codec: "lz4".into(),
385            binary_codec: "lz4".into(),
386            metadata_codec: "lz4".into(),
387            metadata_quality: 1,
388            inline_threshold: 4096,
389        },
390        categorizers: vec![],
391        chunking: ChunkingConfig {
392            name: "fastcdc".into(),
393            avg_chunk_size: 131_072,
394            min_chunk_size: 16_384,
395            max_chunk_size: 524_288,
396        },
397        tournament: TournamentConfig {
398            codecs: vec!["store".into(), "lz4".into()],
399            min_size_threshold: 0,
400            skip_for_binary: true,
401            short_circuit_threshold: 500,
402        },
403        codec_tunables: CodecTunables::default(),
404        encryption: EncryptionConfig {
405            aead: "chacha20-poly1305".into(),
406            key_wrap: "x25519-hkdf".into(),
407        },
408        dictionaries: DictionaryConfig {
409            enabled: false,
410            min_class_size: 0,
411            max_dict_size: 0,
412            trainer: "frequency".into(),
413        },
414        mode: crate::config::ImageMode::ReadOnly,
415        write_codec: "lz4".into(),
416        turnover_threshold: 0,
417        skip_chunking: true,
418    }
419}
420
421/// Maximum write profile for RW images — optimized for write-heavy
422/// live filesystems where write latency per operation matters most.
423///
424/// - Write codec: LZ4 (instant compress, minimal write latency)
425/// - Turnover codec: ZSTD L12 (re-compaction with decent ratio)
426/// - Mode: CopyOnWrite (fast updates, unreferenced blocks reclaimed)
427/// - Chunks: 128 KB (minimal per-chunk overhead per write)
428/// - Turnover threshold: 500 updates
429#[must_use]
430pub fn max_write_rw() -> WriteConfig {
431    WriteConfig {
432        defaults: Defaults {
433            text_codec: "lz4".into(),
434            binary_codec: "lz4".into(),
435            metadata_codec: "lz4".into(),
436            metadata_quality: 1,
437            inline_threshold: 4096,
438        },
439        categorizers: vec![],
440        chunking: ChunkingConfig {
441            name: "fastcdc".into(),
442            avg_chunk_size: 131_072,
443            min_chunk_size: 16_384,
444            max_chunk_size: 524_288,
445        },
446        tournament: TournamentConfig {
447            codecs: vec!["store".into(), "lz4".into()],
448            min_size_threshold: 0,
449            skip_for_binary: true,
450            short_circuit_threshold: 500,
451        },
452        codec_tunables: CodecTunables::default(),
453        mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::CopyOnWrite),
454        write_codec: "lz4".into(),
455        turnover_threshold: 500,
456        skip_chunking: true,
457        encryption: EncryptionConfig {
458            aead: "chacha20-poly1305".into(),
459            key_wrap: "x25519-hkdf".into(),
460        },
461        dictionaries: DictionaryConfig {
462            enabled: false,
463            min_class_size: 0,
464            max_dict_size: 0,
465            trainer: "frequency".into(),
466        },
467    }
468}
469
470/// Maximum read profile for RW images — optimized for read-heavy
471/// live filesystems where read throughput and integrity matter.
472///
473/// - Write codec: ZSTD L6 (good ratio, decent compress speed)
474/// - Turnover codec: ZSTD L19 (best ratio for compaction)
475/// - Mode: UpdateInPlace (full history for audit trail)
476/// - Chunks: 64 KB (fewer drops to traverse during reads)
477/// - Turnover threshold: 1000 updates
478#[must_use]
479pub fn max_read_rw() -> WriteConfig {
480    WriteConfig {
481        defaults: Defaults {
482            text_codec: "zstd".into(),
483            binary_codec: "zstd".into(),
484            metadata_codec: "zstd".into(),
485            metadata_quality: 6,
486            inline_threshold: 8192,
487        },
488        categorizers: crate::config::defaults::all_v0_1(),
489        chunking: ChunkingConfig {
490            name: "fastcdc".into(),
491            avg_chunk_size: 65_536,
492            min_chunk_size: 8192,
493            max_chunk_size: 262_144,
494        },
495        tournament: TournamentConfig {
496            codecs: vec!["store".into(), "lz4".into(), "zstd".into(), "brotli".into()],
497            min_size_threshold: 256,
498            skip_for_binary: false,
499            short_circuit_threshold: 200,
500        },
501        codec_tunables: CodecTunables {
502            brotli: crate::config::BrotliTunables {
503                quality: 11,
504                window: 22,
505            },
506            ..CodecTunables::default()
507        },
508        mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::UpdateInPlace),
509        write_codec: "zstd".into(),
510        turnover_threshold: 1000,
511        skip_chunking: false,
512        encryption: EncryptionConfig {
513            aead: "chacha20-poly1305".into(),
514            key_wrap: "x25519-hkdf".into(),
515        },
516        dictionaries: DictionaryConfig {
517            enabled: true,
518            min_class_size: 50,
519            max_dict_size: 131_072,
520            trainer: "frequency".into(),
521        },
522    }
523}
524
525/// Balanced RW profile — general-purpose read-write image.
526///
527/// - Write codec: ZSTD L1 (fast, decent ratio per write)
528/// - Turnover codec: Brotli q5 (good ratio compaction)
529/// - Mode: UpdateInPlace
530/// - Chunks: 16 KB
531/// - Turnover threshold: 1000 updates
532#[must_use]
533pub fn balanced_rw() -> WriteConfig {
534    WriteConfig {
535        defaults: Defaults {
536            text_codec: "zstd".into(),
537            binary_codec: "lz4".into(),
538            metadata_codec: "zstd".into(),
539            metadata_quality: 3,
540            inline_threshold: 4096,
541        },
542        categorizers: crate::config::defaults::all_v0_1(),
543        chunking: ChunkingConfig {
544            name: "fastcdc".into(),
545            avg_chunk_size: 16_384,
546            min_chunk_size: 2048,
547            max_chunk_size: 65_536,
548        },
549        tournament: TournamentConfig {
550            codecs: vec!["store".into(), "lz4".into(), "zstd".into()],
551            min_size_threshold: 256,
552            skip_for_binary: true,
553            short_circuit_threshold: 250,
554        },
555        codec_tunables: CodecTunables {
556            brotli: crate::config::BrotliTunables {
557                quality: 5,
558                window: 22,
559            },
560            ..CodecTunables::default()
561        },
562        mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::UpdateInPlace),
563        write_codec: "zstd".into(),
564        turnover_threshold: 1000,
565        skip_chunking: false,
566        encryption: EncryptionConfig {
567            aead: "chacha20-poly1305".into(),
568            key_wrap: "x25519-hkdf".into(),
569        },
570        dictionaries: DictionaryConfig {
571            enabled: true,
572            min_class_size: 100,
573            max_dict_size: 65_536,
574            trainer: "frequency".into(),
575        },
576    }
577}
578
579/// TOML-representable profile selector. Either a built-in name or
580/// an inline custom profile.
581#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
582#[serde(untagged)]
583pub enum ProfileSpec {
584    /// Use a built-in profile by name.
585    Preset(String),
586    /// Define a custom profile inline.
587    Custom(CustomProfile),
588}
589
590/// User-defined profile fields. Any field not specified inherits from
591/// the `balanced` profile.
592#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
593pub struct CustomProfile {
594    pub name: String,
595    #[serde(default = "default_text")]
596    pub text_codec: String,
597    #[serde(default = "default_binary")]
598    pub binary_codec: String,
599    #[serde(default = "default_quality")]
600    pub brotli_quality: u8,
601    #[serde(default)]
602    pub chunk_avg_size: u32,
603    #[serde(default)]
604    pub skip_tournament_for_binary: bool,
605    #[serde(default = "default_true")]
606    pub enable_flac: bool,
607    #[serde(default = "default_true")]
608    pub enable_ricepp: bool,
609}
610
611fn default_text() -> String {
612    "brotli".into()
613}
614fn default_binary() -> String {
615    "lz4".into()
616}
617fn default_quality() -> u8 {
618    5
619}
620fn default_true() -> bool {
621    true
622}
623
624/// Resolve a [`ProfileSpec`] into a concrete [`WriteConfig`].
625pub fn resolve(spec: &ProfileSpec) -> Option<WriteConfig> {
626    match spec {
627        ProfileSpec::Preset(name) => select(name),
628        ProfileSpec::Custom(custom) => {
629            let mut config = balanced();
630            if !custom.text_codec.is_empty() {
631                config.defaults.text_codec = custom.text_codec.clone();
632            }
633            if !custom.binary_codec.is_empty() {
634                config.defaults.binary_codec = custom.binary_codec.clone();
635            }
636            if custom.brotli_quality > 0 {
637                config.codec_tunables.brotli.quality = custom.brotli_quality;
638            }
639            if custom.chunk_avg_size > 0 {
640                config.chunking.avg_chunk_size = custom.chunk_avg_size;
641            }
642            config.tournament.skip_for_binary = custom.skip_tournament_for_binary;
643            if !custom.enable_flac {
644                config.categorizers.retain(|c| c.name != "pcm_audio");
645            }
646            if !custom.enable_ricepp {
647                config.categorizers.retain(|c| c.name != "fits");
648            }
649            Some(config)
650        }
651    }
652}
653
654#[cfg(test)]
655mod tests {
656    use super::*;
657
658    #[test]
659    fn all_builtins_resolve() {
660        for name in [
661            MAX_RATIO,
662            MAX_SPEED,
663            BALANCED,
664            COMPETITIVE,
665            MAX_READ,
666            MAX_WRITE,
667            MAX_WRITE_RW,
668            MAX_READ_RW,
669            BALANCED_RW,
670        ] {
671            let config = select(name).expect("profile exists");
672            config.validate().expect("validates");
673        }
674    }
675
676    #[test]
677    fn competitive_uses_lz4_for_binary() {
678        let config = competitive();
679        assert_eq!(config.binary_codec_id().unwrap(), 0x01); // LZ4
680    }
681
682    #[test]
683    fn competitive_uses_zstd_for_text() {
684        let config = competitive();
685        assert_eq!(config.text_codec_id().unwrap(), 0x02); // ZSTD
686    }
687
688    #[test]
689    fn max_speed_disables_categorizers() {
690        let config = max_speed();
691        assert!(config.categorizers.is_empty());
692    }
693
694    #[test]
695    fn max_ratio_enables_ppmd() {
696        let config = max_ratio();
697        assert_eq!(config.codec_tunables.ppmd7.memory_budget_mb, 256);
698    }
699
700    #[test]
701    fn custom_profile_inherits_balanced() {
702        let spec = ProfileSpec::Custom(CustomProfile {
703            name: "test".into(),
704            brotli_quality: 9,
705            ..CustomProfile::default()
706        });
707        let config = resolve(&spec).expect("resolves");
708        assert_eq!(config.codec_tunables.brotli.quality, 9);
709        // Inherited from balanced
710        assert_eq!(config.defaults.text_codec, "brotli");
711    }
712}