use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::sync::mpsc;
use std::thread::{self, ThreadId};
static HELD: Mutex<Vec<(PathBuf, Hold)>> = Mutex::new(Vec::new());
static RAN_ON: Mutex<Vec<(PathBuf, ThreadId)>> = Mutex::new(Vec::new());
struct Hold {
entered: mpsc::Sender<()>,
release: mpsc::Receiver<()>,
}
pub(crate) fn hold(path: &Path) -> (mpsc::Receiver<()>, mpsc::Sender<()>) {
let (entered, has_entered) = mpsc::channel();
let (release, released) = mpsc::channel();
HELD.lock().unwrap().push((
path.to_path_buf(),
Hold {
entered,
release: released,
},
));
(has_entered, release)
}
pub(crate) fn checkpoint(path: &Path) {
RAN_ON
.lock()
.unwrap()
.push((path.to_path_buf(), thread::current().id()));
let hold = {
let mut held = HELD.lock().unwrap();
held.iter()
.position(|(held, _)| held == path)
.map(|at| held.remove(at).1)
};
if let Some(hold) = hold {
let _ = hold.entered.send(());
let _ = hold.release.recv();
}
}
pub(crate) fn threads_for(path: &Path) -> Vec<ThreadId> {
RAN_ON
.lock()
.unwrap()
.iter()
.filter(|(ran, _)| ran == path)
.map(|&(_, thread)| thread)
.collect()
}
const MMDB_MARKER: &[u8] = b"\xab\xcd\xefMaxMind.com";
const ARCHIVE_DIRECTORY: &str = "GeoLite2-City_20241231";
pub(crate) fn mmdb_body(payload: &[u8]) -> Vec<u8> {
let mut body = payload.to_vec();
body.extend_from_slice(MMDB_MARKER);
body.extend_from_slice(b"binary metadata section");
body
}
pub(crate) fn sha256_hex(body: &[u8]) -> String {
use sha2::Digest;
use std::fmt::Write as _;
sha2::Sha256::digest(body)
.iter()
.fold(String::new(), |mut hex, byte| {
let _ = write!(hex, "{byte:02x}");
hex
})
}
pub(crate) fn gzip(payload: &[u8]) -> Vec<u8> {
let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::fast());
encoder.write_all(payload).unwrap();
encoder.finish().unwrap()
}
pub(crate) fn tar_gz(member: &str, payload: &[u8]) -> Vec<u8> {
let path = format!("{ARCHIVE_DIRECTORY}/{member}");
tar_gz_of(&[(path.as_str(), tar::EntryType::Regular, payload)])
}
pub(crate) fn tar_gz_of(entries: &[(&str, tar::EntryType, &[u8])]) -> Vec<u8> {
let mut builder = tar::Builder::new(flate2::write::GzEncoder::new(
Vec::new(),
flate2::Compression::fast(),
));
for (path, kind, payload) in entries {
let mut header = tar::Header::new_gnu();
header.set_size(payload.len() as u64);
header.set_mode(0o644);
header.set_entry_type(*kind);
header.set_cksum();
builder.append_data(&mut header, path, *payload).unwrap();
}
let mut encoder = builder.into_inner().unwrap();
encoder.flush().unwrap();
encoder.finish().unwrap()
}