#![allow(warnings)]
use serde::{Deserialize, Serialize};
use crate::config::{
ChunkingConfig, CodecTunables, Defaults, DictionaryConfig, EncryptionConfig, TournamentConfig,
WriteConfig,
};
pub const MAX_RATIO: &str = "max-ratio";
pub const MAX_SPEED: &str = "max-speed";
pub const BALANCED: &str = "balanced";
pub const COMPETITIVE: &str = "competitive";
pub const MAX_READ: &str = "max-read";
pub const MAX_WRITE: &str = "max-write";
pub const MAX_WRITE_RW: &str = "max-write-rw";
pub const MAX_READ_RW: &str = "max-read-rw";
pub const BALANCED_RW: &str = "balanced-rw";
#[must_use]
pub fn select(name: &str) -> Option<WriteConfig> {
match name {
MAX_RATIO => Some(max_ratio()),
MAX_SPEED => Some(max_speed()),
BALANCED => Some(balanced()),
COMPETITIVE => Some(competitive()),
MAX_READ => Some(max_read()),
MAX_WRITE => Some(max_write()),
MAX_WRITE_RW => Some(max_write_rw()),
MAX_READ_RW => Some(max_read_rw()),
BALANCED_RW => Some(balanced_rw()),
_ => None,
}
}
#[must_use]
pub fn max_ratio() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "brotli".into(),
binary_codec: "zstd".into(),
metadata_codec: "brotli".into(),
metadata_quality: 11,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 8192,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 65_536,
min_chunk_size: 8192,
max_chunk_size: 262_144,
},
tournament: TournamentConfig {
codecs: vec![
"store".into(),
"lz4".into(),
"lz4-hc".into(),
"zstd".into(),
"brotli".into(),
"ppmd".into(),
"bzip2".into(),
],
min_size_threshold: 256,
skip_for_binary: false,
short_circuit_threshold: 0,
},
codec_tunables: CodecTunables {
ppmd7: crate::config::Ppmd7Tunables {
order: 6,
memory_budget_mb: 256,
},
ppmd8: crate::config::Ppmd8Tunables {
order: 8,
memory_budget_mb: 128,
},
brotli: crate::config::BrotliTunables {
quality: 11,
window: 24,
},
lzma: crate::config::LzmaTunables {
lc: 3,
lp: 0,
pb: 2,
dict_size_mb: 64,
use_optimal_parser: true,
},
bzip2: crate::config::Bzip2Tunables { block_size_kb: 900 },
},
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: true,
min_class_size: 50,
max_dict_size: 131_072,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: false,
}
}
#[must_use]
pub fn max_speed() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "lz4".into(),
binary_codec: "lz4".into(),
metadata_codec: "lz4".into(),
metadata_quality: 1,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: vec![],
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 4096,
min_chunk_size: 512,
max_chunk_size: 16_384,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into()],
min_size_threshold: 0,
skip_for_binary: true,
short_circuit_threshold: 500,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 0,
window: 10,
},
..CodecTunables::default()
},
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: false,
min_class_size: 0,
max_dict_size: 0,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: false,
}
}
#[must_use]
pub fn balanced() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "brotli".into(),
binary_codec: "lz4".into(),
metadata_codec: "zstd".into(),
metadata_quality: 3,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 16_384,
min_chunk_size: 2048,
max_chunk_size: 65_536,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into(), "brotli".into()],
min_size_threshold: 256,
skip_for_binary: true,
short_circuit_threshold: 250,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 5,
window: 22,
},
..CodecTunables::default()
},
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: true,
min_class_size: 100,
max_dict_size: 65_536,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: false,
}
}
#[must_use]
pub fn competitive() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "zstd".into(),
binary_codec: "lz4".into(),
metadata_codec: "zstd".into(),
metadata_quality: 3,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 8192,
min_chunk_size: 1024,
max_chunk_size: 65_536,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into(), "brotli".into()],
min_size_threshold: 0,
skip_for_binary: true,
short_circuit_threshold: 250,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 5,
window: 22,
},
..CodecTunables::default()
},
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: false,
min_class_size: 0,
max_dict_size: 0,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: false,
}
}
#[must_use]
pub fn max_read() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "zstd".into(),
binary_codec: "zstd".into(),
metadata_codec: "zstd".into(),
metadata_quality: 11,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 8192,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 65_536,
min_chunk_size: 8192,
max_chunk_size: 262_144,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into(), "zstd".into(), "brotli".into()],
min_size_threshold: 256,
skip_for_binary: false,
short_circuit_threshold: 200,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 11,
window: 22,
},
lzma: crate::config::LzmaTunables {
dict_size_mb: 64,
use_optimal_parser: true,
..crate::config::LzmaTunables::default()
},
..CodecTunables::default()
},
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: true,
min_class_size: 50,
max_dict_size: 131_072,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: false,
}
}
#[must_use]
pub fn max_write() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "lz4".into(),
binary_codec: "lz4".into(),
metadata_codec: "lz4".into(),
metadata_quality: 1,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: vec![],
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 131_072,
min_chunk_size: 16_384,
max_chunk_size: 524_288,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into()],
min_size_threshold: 0,
skip_for_binary: true,
short_circuit_threshold: 500,
},
codec_tunables: CodecTunables::default(),
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: false,
min_class_size: 0,
max_dict_size: 0,
trainer: "frequency".into(),
},
mode: crate::config::ImageMode::ReadOnly,
write_codec: "lz4".into(),
turnover_threshold: 0,
skip_chunking: true,
}
}
#[must_use]
pub fn max_write_rw() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "lz4".into(),
binary_codec: "lz4".into(),
metadata_codec: "lz4".into(),
metadata_quality: 1,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: vec![],
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 131_072,
min_chunk_size: 16_384,
max_chunk_size: 524_288,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into()],
min_size_threshold: 0,
skip_for_binary: true,
short_circuit_threshold: 500,
},
codec_tunables: CodecTunables::default(),
mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::CopyOnWrite),
write_codec: "lz4".into(),
turnover_threshold: 500,
skip_chunking: true,
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: false,
min_class_size: 0,
max_dict_size: 0,
trainer: "frequency".into(),
},
}
}
#[must_use]
pub fn max_read_rw() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "zstd".into(),
binary_codec: "zstd".into(),
metadata_codec: "zstd".into(),
metadata_quality: 6,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 8192,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 65_536,
min_chunk_size: 8192,
max_chunk_size: 262_144,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into(), "zstd".into(), "brotli".into()],
min_size_threshold: 256,
skip_for_binary: false,
short_circuit_threshold: 200,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 11,
window: 22,
},
..CodecTunables::default()
},
mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::UpdateInPlace),
write_codec: "zstd".into(),
turnover_threshold: 1000,
skip_chunking: false,
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: true,
min_class_size: 50,
max_dict_size: 131_072,
trainer: "frequency".into(),
},
}
}
#[must_use]
pub fn balanced_rw() -> WriteConfig {
WriteConfig {
defaults: Defaults {
text_codec: "zstd".into(),
binary_codec: "lz4".into(),
metadata_codec: "zstd".into(),
metadata_quality: 3,
metadata_externalize_threshold: crate::METADATA_EXTERNALIZE_THRESHOLD,
shared_inline: true,
inline_threshold: 4096,
},
categorizers: crate::config::defaults::all_v0_1(),
chunking: ChunkingConfig {
name: "fastcdc".into(),
avg_chunk_size: 16_384,
min_chunk_size: 2048,
max_chunk_size: 65_536,
},
tournament: TournamentConfig {
codecs: vec!["store".into(), "lz4".into(), "zstd".into()],
min_size_threshold: 256,
skip_for_binary: true,
short_circuit_threshold: 250,
},
codec_tunables: CodecTunables {
brotli: crate::config::BrotliTunables {
quality: 5,
window: 22,
},
..CodecTunables::default()
},
mode: crate::config::ImageMode::ReadWrite(crate::config::RWMode::UpdateInPlace),
write_codec: "zstd".into(),
turnover_threshold: 1000,
skip_chunking: false,
encryption: EncryptionConfig {
aead: "chacha20-poly1305".into(),
key_wrap: "x25519-hkdf".into(),
},
dictionaries: DictionaryConfig {
enabled: true,
min_class_size: 100,
max_dict_size: 65_536,
trainer: "frequency".into(),
},
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ProfileSpec {
Preset(String),
Custom(CustomProfile),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
pub struct CustomProfile {
pub name: String,
#[serde(default = "default_text")]
pub text_codec: String,
#[serde(default = "default_binary")]
pub binary_codec: String,
#[serde(default = "default_quality")]
pub brotli_quality: u8,
#[serde(default)]
pub chunk_avg_size: u32,
#[serde(default)]
pub skip_tournament_for_binary: bool,
#[serde(default = "default_true")]
pub enable_flac: bool,
#[serde(default = "default_true")]
pub enable_ricepp: bool,
}
fn default_text() -> String {
"brotli".into()
}
fn default_binary() -> String {
"lz4".into()
}
fn default_quality() -> u8 {
5
}
fn default_true() -> bool {
true
}
pub fn resolve(spec: &ProfileSpec) -> Option<WriteConfig> {
match spec {
ProfileSpec::Preset(name) => select(name),
ProfileSpec::Custom(custom) => {
let mut config = balanced();
if !custom.text_codec.is_empty() {
config.defaults.text_codec = custom.text_codec.clone();
}
if !custom.binary_codec.is_empty() {
config.defaults.binary_codec = custom.binary_codec.clone();
}
if custom.brotli_quality > 0 {
config.codec_tunables.brotli.quality = custom.brotli_quality;
}
if custom.chunk_avg_size > 0 {
config.chunking.avg_chunk_size = custom.chunk_avg_size;
}
config.tournament.skip_for_binary = custom.skip_tournament_for_binary;
if !custom.enable_flac {
config.categorizers.retain(|c| c.name != "pcm_audio");
}
if !custom.enable_ricepp {
config.categorizers.retain(|c| c.name != "fits");
}
Some(config)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_builtins_resolve() {
for name in [
MAX_RATIO,
MAX_SPEED,
BALANCED,
COMPETITIVE,
MAX_READ,
MAX_WRITE,
MAX_WRITE_RW,
MAX_READ_RW,
BALANCED_RW,
] {
let config = select(name).expect("profile exists");
config.validate().expect("validates");
}
}
#[test]
fn competitive_uses_lz4_for_binary() {
let config = competitive();
assert_eq!(config.binary_codec_id().unwrap(), 0x01); }
#[test]
fn competitive_uses_zstd_for_text() {
let config = competitive();
assert_eq!(config.text_codec_id().unwrap(), 0x02); }
#[test]
fn max_speed_disables_categorizers() {
let config = max_speed();
assert!(config.categorizers.is_empty());
}
#[test]
fn max_ratio_enables_ppmd() {
let config = max_ratio();
assert_eq!(config.codec_tunables.ppmd7.memory_budget_mb, 256);
}
#[test]
fn custom_profile_inherits_balanced() {
let spec = ProfileSpec::Custom(CustomProfile {
name: "test".into(),
brotli_quality: 9,
..CustomProfile::default()
});
let config = resolve(&spec).expect("resolves");
assert_eq!(config.codec_tunables.brotli.quality, 9);
assert_eq!(config.defaults.text_codec, "brotli");
}
}