use std::sync::atomic::{AtomicI32, AtomicU8, AtomicUsize, Ordering};
pub const DEFAULT_TILE_BUDGET_BYTES: usize = 64 * 1024;
pub const DEFAULT_DICT_CHUNK_BYTES: usize = 64 * 1024;
pub const DEFAULT_ZSTD_LEVEL: i32 = 9;
pub const MIN_BUDGET_KIB: usize = 4;
pub const MAX_BUDGET_KIB: usize = 4096;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameShape {
Stream,
Sized,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WriteOptions {
pub tile_budget: usize,
pub dict_chunk_budget: usize,
pub zstd_level: i32,
pub frame: FrameShape,
pub frequent_objects: usize,
}
impl Default for WriteOptions {
fn default() -> Self {
WriteOptions {
tile_budget: DEFAULT_TILE_BUDGET_BYTES,
dict_chunk_budget: DEFAULT_DICT_CHUNK_BYTES,
zstd_level: DEFAULT_ZSTD_LEVEL,
frame: FrameShape::Sized,
frequent_objects: 0,
}
}
}
static TILE_BUDGET: AtomicUsize = AtomicUsize::new(0);
static DICT_CHUNK_BUDGET: AtomicUsize = AtomicUsize::new(0);
static ZSTD_LEVEL: AtomicI32 = AtomicI32::new(0);
static FRAME: AtomicU8 = AtomicU8::new(u8::MAX);
static FREQUENT_OBJECTS: AtomicUsize = AtomicUsize::new(usize::MAX);
fn env_parse<T: std::str::FromStr>(name: &str) -> Option<T> {
std::env::var(name).ok().and_then(|v| v.trim().parse().ok())
}
fn kib_env(name: &str, default: usize) -> usize {
env_parse::<usize>(name)
.filter(|k| (MIN_BUDGET_KIB..=MAX_BUDGET_KIB).contains(k))
.map(|k| k * 1024)
.unwrap_or(default)
}
pub fn tile_budget() -> usize {
match TILE_BUDGET.load(Ordering::Relaxed) {
0 => {
let v = kib_env("RETE_TILE_KIB", DEFAULT_TILE_BUDGET_BYTES);
TILE_BUDGET.store(v, Ordering::Relaxed);
v
}
v => v,
}
}
pub fn dict_chunk_budget() -> usize {
match DICT_CHUNK_BUDGET.load(Ordering::Relaxed) {
0 => {
let v = kib_env("RETE_DICT_CHUNK_KIB", DEFAULT_DICT_CHUNK_BYTES);
DICT_CHUNK_BUDGET.store(v, Ordering::Relaxed);
v
}
v => v,
}
}
pub fn zstd_level() -> i32 {
match ZSTD_LEVEL.load(Ordering::Relaxed) {
0 => {
let v = env_parse::<i32>("RETE_ZSTD_LEVEL")
.filter(|l| (1..=22).contains(l))
.unwrap_or(DEFAULT_ZSTD_LEVEL);
ZSTD_LEVEL.store(v, Ordering::Relaxed);
v
}
v => v,
}
}
pub fn frame_shape() -> FrameShape {
let code = match FRAME.load(Ordering::Relaxed) {
u8::MAX => {
let v = match std::env::var("RETE_ZSTD_FRAME").ok().as_deref() {
Some("stream") => 0,
_ => 1,
};
FRAME.store(v, Ordering::Relaxed);
v
}
v => v,
};
if code == 1 {
FrameShape::Sized
} else {
FrameShape::Stream
}
}
pub fn frequent_objects() -> usize {
match FREQUENT_OBJECTS.load(Ordering::Relaxed) {
usize::MAX => {
let v = env_parse::<usize>("RETE_FREQUENT_OBJECTS").unwrap_or(0);
FREQUENT_OBJECTS.store(v, Ordering::Relaxed);
v
}
v => v,
}
}
pub fn write_options() -> WriteOptions {
WriteOptions {
tile_budget: tile_budget(),
dict_chunk_budget: dict_chunk_budget(),
zstd_level: zstd_level(),
frame: frame_shape(),
frequent_objects: frequent_objects(),
}
}
pub fn set_write_options(o: WriteOptions) {
let clamp_budget = |b: usize| b.clamp(MIN_BUDGET_KIB * 1024, MAX_BUDGET_KIB * 1024);
TILE_BUDGET.store(clamp_budget(o.tile_budget), Ordering::Relaxed);
DICT_CHUNK_BUDGET.store(clamp_budget(o.dict_chunk_budget), Ordering::Relaxed);
ZSTD_LEVEL.store(o.zstd_level.clamp(1, 22), Ordering::Relaxed);
FRAME.store(
match o.frame {
FrameShape::Stream => 0,
FrameShape::Sized => 1,
},
Ordering::Relaxed,
);
FREQUENT_OBJECTS.store(o.frequent_objects, Ordering::Relaxed);
}