use std::sync::atomic::{AtomicI32, Ordering};
use crate::timefn::{clock_span_ns, DurationNs, TimeT};
pub const KB: usize = 1 << 10;
pub const MB: usize = 1 << 20;
pub const GB: usize = 1 << 30;
pub const MAGICNUMBER_SIZE: usize = 4;
pub const LZ4IO_MAGICNUMBER: u32 = 0x184D2204;
pub const LZ4IO_SKIPPABLE0: u32 = 0x184D2A50;
pub const LZ4IO_SKIPPABLEMASK: u32 = 0xFFFF_FFF0;
pub const LEGACY_MAGICNUMBER: u32 = 0x184C2102;
pub const CACHELINE: usize = 64;
pub const LEGACY_BLOCKSIZE: usize = 8 * MB;
pub const MIN_STREAM_BUFSIZE: usize = 192 * KB;
pub const LZ4IO_BLOCKSIZEID_DEFAULT: u32 = 7;
pub const LZ4_MAX_DICT_SIZE: usize = 64 * KB;
pub static DISPLAY_LEVEL: AtomicI32 = AtomicI32::new(0);
pub const REFRESH_RATE_NS: DurationNs = 200_000_000;
#[inline]
pub fn display_level(level: i32, msg: &str) {
if DISPLAY_LEVEL.load(Ordering::Relaxed) >= level {
eprint!("{}", msg);
if DISPLAY_LEVEL.load(Ordering::Relaxed) >= 4 {
use std::io::Write;
let _ = std::io::stderr().flush();
}
}
}
pub fn cpu_load_sec(cpu_start: libc::clock_t) -> f64 {
#[cfg(not(target_os = "windows"))]
{
extern "C" {
fn clock() -> libc::clock_t;
}
const CLOCKS_PER_SEC: libc::clock_t = 1_000_000;
let elapsed = unsafe { clock() } - cpu_start;
elapsed as f64 / CLOCKS_PER_SEC as f64
}
#[cfg(target_os = "windows")]
{
use std::mem::MaybeUninit;
unsafe {
let process = winapi::um::processthreadsapi::GetCurrentProcess();
let mut creation = MaybeUninit::uninit();
let mut exit = MaybeUninit::uninit();
let mut kernel = MaybeUninit::uninit();
let mut user = MaybeUninit::uninit();
winapi::um::processthreadsapi::GetProcessTimes(
process,
creation.as_mut_ptr(),
exit.as_mut_ptr(),
kernel.as_mut_ptr(),
user.as_mut_ptr(),
);
let k = kernel.assume_init();
let u = user.assume_init();
debug_assert_eq!(
k.dwHighDateTime, 0,
"kernel time dwHighDateTime unexpected non-zero"
);
debug_assert_eq!(
u.dwHighDateTime, 0,
"user time dwHighDateTime unexpected non-zero"
);
((k.dwLowDateTime as f64) + (u.dwLowDateTime as f64)) * 100.0 / 1_000_000_000.0
}
}
}
pub fn final_time_display(time_start: TimeT, cpu_start: libc::clock_t, size: u64) {
#[cfg(feature = "multithread")]
{
if !crate::timefn::support_mt_measurements() {
display_level(5, "time measurements not compatible with multithreading \n");
return;
}
}
let duration_ns = clock_span_ns(time_start);
let seconds = (duration_ns.max(1)) as f64 / 1_000_000_000.0_f64;
let cpu_load_s = cpu_load_sec(cpu_start);
let msg = format!(
"Done in {:.2} s ==> {:.2} MiB/s (cpu load : {:.0}%)\n",
seconds,
(size as f64) / seconds / 1024.0 / 1024.0,
(cpu_load_s / seconds) * 100.0,
);
display_level(3, &msg);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockMode {
Linked = 0,
Independent = 1,
}
#[derive(Clone, Debug)]
pub struct Prefs {
pub pass_through: bool,
pub overwrite: bool,
pub test_mode: bool,
pub block_size_id: u32,
pub block_size: usize,
pub block_checksum: bool,
pub stream_checksum: bool,
pub block_independence: bool,
pub sparse_file_support: i32,
pub content_size_flag: bool,
pub use_dictionary: bool,
pub favor_dec_speed: bool,
pub dictionary_filename: Option<String>,
pub remove_src_file: bool,
pub nb_workers: i32,
}
pub fn default_nb_workers() -> i32 {
#[cfg(feature = "multithread")]
{
let nb_cores = num_cpus::get_physical() as i32;
let spared = 1 + ((nb_cores as u32) >> 3) as i32;
if nb_cores <= spared {
1
} else {
nb_cores - spared
}
}
#[cfg(not(feature = "multithread"))]
{
1
}
}
impl Default for Prefs {
fn default() -> Self {
Prefs {
pass_through: false,
overwrite: true,
test_mode: false,
block_size_id: LZ4IO_BLOCKSIZEID_DEFAULT,
block_size: 0,
block_checksum: false,
stream_checksum: true,
block_independence: true,
sparse_file_support: 1,
content_size_flag: false,
use_dictionary: false,
favor_dec_speed: false,
dictionary_filename: None,
remove_src_file: false,
nb_workers: default_nb_workers(),
}
}
}
impl Prefs {
pub fn new() -> Self {
Self::default()
}
pub fn set_nb_workers(&mut self, nb_workers: i32) -> i32 {
let clamped = nb_workers.max(1).min(crate::config::NB_WORKERS_MAX as i32);
self.nb_workers = clamped;
clamped
}
pub fn set_dictionary_filename(&mut self, filename: Option<&str>) -> bool {
self.dictionary_filename = filename.map(|s| s.to_owned());
self.use_dictionary = self.dictionary_filename.is_some();
self.use_dictionary
}
pub fn set_pass_through(&mut self, yes: bool) -> bool {
self.pass_through = yes;
yes
}
pub fn set_overwrite(&mut self, yes: bool) -> bool {
self.overwrite = yes;
yes
}
pub fn set_test_mode(&mut self, yes: bool) -> bool {
self.test_mode = yes;
yes
}
pub fn set_block_size_id(&mut self, bsid: u32) -> usize {
const BLOCK_SIZE_TABLE: [usize; 4] = [64 * KB, 256 * KB, MB, 4 * MB];
const MIN_BSID: u32 = 4;
const MAX_BSID: u32 = 7;
if !(MIN_BSID..=MAX_BSID).contains(&bsid) {
return 0;
}
self.block_size_id = bsid;
self.block_size = BLOCK_SIZE_TABLE[(bsid - MIN_BSID) as usize];
self.block_size
}
pub fn set_block_size(&mut self, block_size: usize) -> usize {
const MIN_BLOCK_SIZE: usize = 32;
const MAX_BLOCK_SIZE: usize = 4 * MB;
let block_size = block_size.max(MIN_BLOCK_SIZE).min(MAX_BLOCK_SIZE);
self.block_size = block_size;
let mut bsid: u32 = 0;
let mut bs = block_size - 1;
while {
bs >>= 2;
bs != 0
} {
bsid += 1;
}
if bsid < 7 {
bsid = 7;
}
self.block_size_id = bsid - 3;
block_size
}
pub fn set_block_mode(&mut self, mode: BlockMode) -> bool {
self.block_independence = mode == BlockMode::Independent;
self.block_independence
}
pub fn set_block_checksum_mode(&mut self, enable: bool) -> bool {
self.block_checksum = enable;
enable
}
pub fn set_stream_checksum_mode(&mut self, enable: bool) -> bool {
self.stream_checksum = enable;
enable
}
pub fn set_sparse_file(&mut self, enable: bool) -> i32 {
self.sparse_file_support = if enable { 2 } else { 0 };
self.sparse_file_support
}
pub fn set_content_size(&mut self, enable: bool) -> bool {
self.content_size_flag = enable;
enable
}
pub fn favor_dec_speed(&mut self, favor: bool) {
self.favor_dec_speed = favor;
}
pub fn set_remove_src_file(&mut self, flag: bool) {
self.remove_src_file = flag;
}
}
pub fn set_notification_level(level: i32) -> i32 {
DISPLAY_LEVEL.store(level, Ordering::Relaxed);
level
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_prefs_fields() {
let p = Prefs::default();
assert!(!p.pass_through);
assert!(p.overwrite);
assert!(!p.test_mode);
assert_eq!(p.block_size_id, LZ4IO_BLOCKSIZEID_DEFAULT);
assert_eq!(p.block_size, 0);
assert!(!p.block_checksum);
assert!(p.stream_checksum);
assert!(p.block_independence);
assert_eq!(p.sparse_file_support, 1);
assert!(!p.content_size_flag);
assert!(!p.use_dictionary);
assert!(!p.favor_dec_speed);
assert!(p.dictionary_filename.is_none());
assert!(!p.remove_src_file);
assert!(p.nb_workers >= 1);
}
#[test]
fn set_nb_workers_clamps() {
let mut p = Prefs::default();
assert_eq!(p.set_nb_workers(0), 1);
assert_eq!(p.set_nb_workers(1000), crate::config::NB_WORKERS_MAX as i32);
assert_eq!(p.set_nb_workers(4), 4);
}
#[test]
fn set_block_size_id_valid() {
let mut p = Prefs::default();
assert_eq!(p.set_block_size_id(4), 64 * KB);
assert_eq!(p.set_block_size_id(5), 256 * KB);
assert_eq!(p.set_block_size_id(6), MB);
assert_eq!(p.set_block_size_id(7), 4 * MB);
}
#[test]
fn set_block_size_id_invalid() {
let mut p = Prefs::default();
assert_eq!(p.set_block_size_id(3), 0);
assert_eq!(p.set_block_size_id(8), 0);
}
#[test]
fn set_block_size_clamps() {
let mut p = Prefs::default();
let s = p.set_block_size(10); assert_eq!(s, 32);
let s = p.set_block_size(100 * MB); assert_eq!(s, 4 * MB);
}
#[test]
fn set_sparse_file_returns_two_when_enabled() {
let mut p = Prefs::default();
assert_eq!(p.set_sparse_file(true), 2);
assert_eq!(p.set_sparse_file(false), 0);
}
#[test]
fn set_dictionary_filename() {
let mut p = Prefs::default();
assert!(p.set_dictionary_filename(Some("dict.lz4")));
assert!(p.use_dictionary);
assert_eq!(p.dictionary_filename.as_deref(), Some("dict.lz4"));
p.set_dictionary_filename(None);
assert!(!p.use_dictionary);
}
#[test]
fn set_notification_level_updates_global() {
set_notification_level(3);
assert_eq!(DISPLAY_LEVEL.load(Ordering::Relaxed), 3);
set_notification_level(0);
}
#[test]
fn set_block_mode_independent() {
let mut p = Prefs::default();
assert!(p.set_block_mode(BlockMode::Independent));
assert!(!p.set_block_mode(BlockMode::Linked));
}
}