use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::Arc;
use std::sync::atomic::AtomicU8;
use std::sync::atomic::Ordering;
use mago_prelude::Prelude;
use zstd::zstd_safe::WriteBuf;
include!(concat!(env!("OUT_DIR"), "/prelude_frames.rs"));
static PRELUDE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/prelude.zst"));
const PENDING: u8 = 0;
const CLAIMED: u8 = 1;
const DONE: u8 = 2;
pub fn load() -> Prelude {
let loading = Loading::new();
if rayon::current_num_threads() > 1 {
loading.help();
}
loading.finish()
}
#[derive(Clone)]
pub struct Loading(Arc<Decompression>);
impl Loading {
pub fn new() -> Self {
Self(Arc::new(Decompression::new()))
}
pub fn help(&self) {
for frame in (0..FRAMES.len()).rev() {
if self.0.states[frame].load(Ordering::Relaxed) == PENDING {
let decompression = Arc::clone(&self.0);
rayon::spawn(move || decompression.run(frame, &mut None));
}
}
}
pub fn finish(&self) -> Prelude {
Prelude::decode(self.0.finish()).expect("Failed to decode embedded prelude")
}
}
struct Decompression {
bytes: Box<[UnsafeCell<MaybeUninit<u8>>]>,
states: [AtomicU8; FRAMES.len()],
}
unsafe impl Sync for Decompression {}
impl Decompression {
fn new() -> Self {
let bytes = Box::<[MaybeUninit<u8>]>::new_uninit_slice(FRAMES.iter().map(|(_, size)| size).sum());
let bytes = unsafe { Box::from_raw(Box::into_raw(bytes) as *mut [UnsafeCell<MaybeUninit<u8>>]) };
Self { bytes, states: std::array::from_fn(|_| AtomicU8::new(PENDING)) }
}
fn run(&self, frame: usize, decompressor: &mut Option<zstd::bulk::Decompressor<'static>>) {
if self.states[frame].compare_exchange(PENDING, CLAIMED, Ordering::Acquire, Ordering::Relaxed).is_err() {
return;
}
let compressed_start: usize = FRAMES[..frame].iter().map(|(compressed, _)| compressed).sum();
let start: usize = FRAMES[..frame].iter().map(|(_, size)| size).sum();
let (compressed, size) = FRAMES[frame];
let bytes = UnsafeCell::raw_get(self.bytes.as_ptr()).cast::<u8>();
let mut output = Region { start: unsafe { bytes.add(start) }, capacity: size, filled: 0 };
let decompressor = match decompressor {
Some(decompressor) => decompressor,
None => {
decompressor.insert(zstd::bulk::Decompressor::new().expect("Failed to decompress embedded prelude"))
}
};
let written = decompressor
.decompress_to_buffer(&PRELUDE[compressed_start..compressed_start + compressed], &mut output)
.expect("Failed to decompress embedded prelude");
assert_eq!(written, size, "Failed to decompress embedded prelude");
self.states[frame].store(DONE, Ordering::Release);
}
fn finish(&self) -> &[u8] {
let mut decompressor = None;
for frame in 0..FRAMES.len() {
self.run(frame, &mut decompressor);
}
for state in &self.states {
while state.load(Ordering::Acquire) != DONE {
std::hint::spin_loop();
}
}
unsafe { std::slice::from_raw_parts(UnsafeCell::raw_get(self.bytes.as_ptr()).cast::<u8>(), self.bytes.len()) }
}
}
struct Region {
start: *mut u8,
capacity: usize,
filled: usize,
}
unsafe impl WriteBuf for Region {
fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.start, self.filled) }
}
fn capacity(&self) -> usize {
self.capacity
}
fn as_mut_ptr(&mut self) -> *mut u8 {
self.start
}
unsafe fn filled_until(&mut self, n: usize) {
self.filled = n;
}
}
#[cfg(test)]
mod tests {
use mago_database::DatabaseReader;
use super::*;
#[test]
fn the_embedded_prelude_matches_its_plain_encoding() {
let (plain, _): (Prelude, usize) = bincode::serde::decode_from_slice(
include_bytes!(concat!(env!("OUT_DIR"), "/prelude.bin")),
bincode::config::standard(),
)
.unwrap();
let loaded = load();
assert!(plain.metadata.class_likes.keys().eq(loaded.metadata.class_likes.keys()));
assert_eq!(plain.metadata.class_likes.capacity(), loaded.metadata.class_likes.capacity());
assert_eq!(plain.metadata, loaded.metadata);
assert_eq!(plain.symbol_references, loaded.symbol_references);
let files = |prelude: &Prelude| {
let mut files: Vec<_> =
prelude.database.files().map(|file| (file.id, file.name.clone(), file.contents.clone())).collect();
files.sort_unstable_by_key(|(id, ..)| *id);
files
};
assert!(files(&plain) == files(&loaded));
}
}