use core::alloc::Layout;
use core::mem::size_of;
use core::ptr::copy_nonoverlapping;
use core::sync::atomic::{AtomicU32, Ordering};

use super::arena::Arena;
use super::constants::{
    MAX_ATTACHED_MINIHEAPS_PER_CLASS, MAX_SMALL_ALLOCATION, MIN_SHUFFLE_VECTOR_LENGTH,
    MINIHEAP_REFILL_GOAL_SIZE, NUM_SIZE_CLASSES, is_below_partial_threshold,
};
use super::fault::{self, ActiveMeshGuard};
use super::meshing::bitmaps_meshable;
use super::miniheap::{MiniHeap, MiniHeapId};
use super::page::{page_count, page_size, runtime_slots_per_span};
use super::platform;
use super::pool::MiniHeapPool;
use super::raw_sys;
use super::rng::Mwc;
use super::shuffle::ShuffleEntry;
use super::size_map::{byte_size_for_class, size_class_for};
use super::stats::{MeshStats, StatsState};
use super::sync::{FutexMutex, futex_wait_for_value, futex_wake_all};
use super::thread_local_heap::ThreadLocalHeap;

#[derive(Debug)]
pub struct MeshAllocator {
    arena: Arena,
    pool: MiniHeapPool,
    bootstrap_thread: *mut ThreadLocalHeap,
    compaction_candidates: *mut MiniHeapId,
    meshing_rng: Mwc,
    mesh_epoch: AtomicU32,
    pool_lock: FutexMutex,
    stats: StatsState,
}

#[derive(Clone, Copy, Debug)]
struct ResolvedPtr {
    owner_id: MiniHeapId,
    slot: usize,
}

impl MeshAllocator {
    pub fn new(arena_size: usize, miniheap_capacity: u32) -> raw_sys::Result<Self> {
        fault::ensure_fault_mediation_installed()?;
        let bootstrap_thread = unsafe {
            platform::map_anonymous(
                size_of::<ThreadLocalHeap>(),
                raw_sys::PROT_READ | raw_sys::PROT_WRITE,
            )? as *mut ThreadLocalHeap
        };
        unsafe {
            bootstrap_thread.write(ThreadLocalHeap::new()?);
        }
        let compaction_candidates = unsafe {
            platform::map_anonymous(
                miniheap_capacity as usize * size_of::<MiniHeapId>(),
                raw_sys::PROT_READ | raw_sys::PROT_WRITE,
            )? as *mut MiniHeapId
        };

        Ok(Self {
            arena: Arena::with_size(arena_size)?,
            pool: MiniHeapPool::with_capacity(miniheap_capacity)?,
            bootstrap_thread,
            compaction_candidates,
            meshing_rng: Mwc::from_os_seed()?,
            mesh_epoch: AtomicU32::new(0),
            pool_lock: FutexMutex::new(),
            stats: StatsState::new(),
        })
    }

    #[inline(always)]
    pub fn arena(&self) -> &Arena {
        &self.arena
    }

    #[inline(always)]
    pub fn pool(&self) -> &MiniHeapPool {
        &self.pool
    }

    #[inline(always)]
    pub fn live_miniheap_count(&self) -> u32 {
        self.pool.live_len()
    }

    pub fn stats(&self) -> MeshStats {
        let page = page_size();
        let (reusable_span_count, reusable_pages) = self.arena.reusable_span_stats();
        let counters = self.stats.snapshot();
        let mut stats = MeshStats {
            arena_size: self.arena.arena_size(),
            reserved_bytes: self.arena.reserved_pages() as usize * page,
            reusable_span_count,
            reusable_span_bytes: reusable_pages as usize * page,
            live_miniheaps: self.pool.live_len(),
            small_allocations: counters.small_allocations,
            small_deallocations: counters.small_deallocations,
            large_allocations: counters.large_allocations,
            large_deallocations: counters.large_deallocations,
            compact_calls: counters.compact_calls,
            meshes_performed: counters.meshes_performed,
            meshed_pages: counters.meshed_pages,
            meshed_bytes: counters.meshed_bytes,
            ..MeshStats::default()
        };

        let mut candidate_heaps_by_class = [0u32; NUM_SIZE_CLASSES];
        let mut candidate_pages_by_class = [0u32; NUM_SIZE_CLASSES];
        let mut candidate_free_bytes_by_class = [0usize; NUM_SIZE_CLASSES];
        let mut candidate_span_bytes_by_class = [0usize; NUM_SIZE_CLASSES];

        let len = self.pool.len();
        let mut id_value = 1u32;
        while id_value <= len {
            let id = MiniHeapId::new(id_value);
            if let Some(heap) = self.pool.get(id) {
                if heap.is_large_alloc() {
                    stats.live_large_allocations += 1;
                    stats.live_large_bytes += heap.span_size();
                    stats.retained_large_span_bytes += heap.span_size();
                    id_value += 1;
                    continue;
                }

                stats.live_small_heaps += 1;
                stats.live_small_bytes += heap.in_use_count() as usize * heap.object_size();
                stats.virtual_small_span_bytes += heap.span_size();

                if heap.is_meshed() {
                    stats.meshed_small_heaps += 1;
                } else {
                    stats.retained_small_span_bytes += heap.span_size();
                }

                if heap.is_full() {
                    stats.full_small_heaps += 1;
                } else if !heap.is_empty() {
                    stats.partial_small_heaps += 1;
                }

                if !heap.is_attached() && !heap.is_full() && !heap.is_meshed() {
                    stats.reusable_small_heaps += 1;
                }

                if self.heap_is_compaction_candidate(heap.size_class(), heap) {
                    let class = heap.size_class() as usize;
                    stats.compaction.candidate_heaps += 1;
                    stats.compaction.candidate_pages += heap.span().length;
                    stats.compaction.candidate_free_bytes += heap.bytes_free();
                    candidate_heaps_by_class[class] += 1;
                    candidate_pages_by_class[class] += heap.span().length;
                    candidate_free_bytes_by_class[class] += heap.bytes_free();
                    candidate_span_bytes_by_class[class] = heap.span_size();
                }
            }
            id_value += 1;
        }

        let mut class = 1usize;
        while class < NUM_SIZE_CLASSES {
            let span_bytes = candidate_span_bytes_by_class[class];
            if let Some(pair_bound_by_free) =
                candidate_free_bytes_by_class[class].checked_div(span_bytes)
            {
                let pair_bound_by_count = candidate_heaps_by_class[class] / 2;
                let best_case_meshes = pair_bound_by_count.min(pair_bound_by_free as u32);
                let pages_per_mesh =
                    candidate_pages_by_class[class] / candidate_heaps_by_class[class].max(1);
                stats.compaction.best_case_meshes += best_case_meshes;
                stats.compaction.best_case_reclaimable_pages += best_case_meshes * pages_per_mesh;
                stats.compaction.best_case_reclaimable_bytes +=
                    best_case_meshes as usize * span_bytes;
            }
            class += 1;
        }

        stats
    }

    pub fn allocate(&mut self, size: usize) -> Option<*mut u8> {
        let thread_heap = unsafe { &mut *self.bootstrap_thread };
        self.allocate_with_thread(size, thread_heap)
    }

    pub fn allocate_with_thread(
        &mut self,
        size: usize,
        thread_heap: &mut ThreadLocalHeap,
    ) -> Option<*mut u8> {
        let size = size.max(1);
        if size <= MAX_SMALL_ALLOCATION {
            let class = size_class_for(size)?;
            if let Some(ptr) = self.try_allocate_small_local(thread_heap, class) {
                return Some(ptr);
            }
            return self.allocate_small_with_thread(thread_heap, class);
        }

        self.allocate_large(size)
    }

    pub fn allocate_layout(&mut self, layout: Layout) -> Option<*mut u8> {
        let thread_heap = unsafe { &mut *self.bootstrap_thread };
        self.allocate_layout_with_thread(thread_heap, layout)
    }

    pub fn allocate_layout_with_thread(
        &mut self,
        thread_heap: &mut ThreadLocalHeap,
        layout: Layout,
    ) -> Option<*mut u8> {
        let aligned_size = round_up_to_alignment(layout.size().max(1), layout.align())?;
        if aligned_size <= MAX_SMALL_ALLOCATION && layout.align() <= page_size() {
            let class = size_class_for(aligned_size)?;
            if byte_size_for_class(class).is_multiple_of(layout.align()) {
                if let Some(ptr) = self.try_allocate_small_local(thread_heap, class) {
                    return Some(ptr);
                }
                return self.allocate_small_with_thread(thread_heap, class);
            }
        }

        self.allocate_large_aligned(aligned_size, layout.align())
    }

    pub fn deallocate(&mut self, ptr: *mut u8) {
        let thread_heap = unsafe { &mut *self.bootstrap_thread };
        self.deallocate_with_thread(ptr, thread_heap);
    }

    pub fn deallocate_with_thread(&mut self, ptr: *mut u8, thread_heap: &mut ThreadLocalHeap) {
        if ptr.is_null() {
            return;
        }

        let Some(resolved) = self.resolve_pointer(ptr) else {
            return;
        };
        let id = resolved.owner_id;
        let Some(heap) = self.pool.get(id) else {
            return;
        };

        if heap.is_large_alloc() {
            let span = heap.span();
            self.stats.record_large_deallocation();
            let _ = heap.free_offset(0);
            self.arena.clear_miniheap(span);
            self.arena.release_span(span);
            let _ = {
                let _guard = self.pool_lock.lock();
                self.pool.release(id)
            };
            return;
        }

        let slot = resolved.slot;
        let class = heap.size_class();
        let thread_id = thread_heap.thread_id();
        self.stats.record_small_deallocation();

        if heap.current_thread() == thread_id && heap.is_attached() && !heap.is_meshed() {
            let state = thread_heap.class_mut(class);
            let attached_idx = state.find_attached(id);
            if let Some(attached_idx) = attached_idx
                && !state.shuffle.is_full()
            {
                let cached = state.shuffle.count_entries_for_offset(attached_idx as u16);
                if cached + 1 == heap.max_count() as usize {
                    self.release_class_attached(thread_heap, class);
                    if let Some(heap) = self.pool.get(id) {
                        let _ = heap.free_offset(slot);
                    }
                    self.reclaim_empty_detached_heap(id);
                    return;
                }
                if cached + 1 < heap.max_count() as usize {
                    state
                        .shuffle
                        .push(ShuffleEntry::new(attached_idx as u16, slot as u16));
                    return;
                }
            }
        }

        let state = thread_heap.class_mut(class);
        let _ = heap.free_offset(slot);
        if heap.is_attached()
            && is_below_partial_threshold(heap.in_use_count(), heap.max_count() as u32)
        {
            heap.unset_attached();
            if let Some(attached_idx) = state.find_attached(id) {
                state.attached_ids[attached_idx as usize] = MiniHeapId::new(0);
                state.attached_heaps[attached_idx as usize] = core::ptr::null();
            }
        }

        if heap.is_empty() && !heap.is_meshed() {
            self.reclaim_empty_detached_heap(id);
        }
    }

    pub fn deallocate_layout(&mut self, ptr: *mut u8, _layout: Layout) {
        self.deallocate(ptr);
    }

    pub fn try_deallocate_local(&self, ptr: *mut u8, thread_heap: &mut ThreadLocalHeap) -> bool {
        if ptr.is_null() {
            return true;
        }

        if self.mesh_epoch.load(Ordering::Acquire) & 1 != 0 {
            return false;
        }

        let Some(resolved) = self.resolve_pointer(ptr) else {
            return true;
        };
        let id = resolved.owner_id;
        let Some(heap) = self.pool.get(id) else {
            return true;
        };
        if heap.is_large_alloc()
            || heap.current_thread() != thread_heap.thread_id()
            || heap.is_meshed()
            || !heap.contains_ptr(self.arena.base_ptr() as usize, ptr)
        {
            return false;
        }

        let class = heap.size_class();
        let slot = resolved.slot;
        let state = thread_heap.class_mut(class);
        let Some(attached_idx) = state.find_attached(id) else {
            return false;
        };
        if state.shuffle.is_full() {
            return false;
        }

        let cached = state.shuffle.count_entries_for_offset(attached_idx as u16);
        if cached + 1 >= heap.max_count() as usize {
            return false;
        }

        state
            .shuffle
            .push(ShuffleEntry::new(attached_idx as u16, slot as u16));
        self.stats.record_small_deallocation();
        true
    }

    /// # Safety
    ///
    /// `ptr` must have been allocated by this allocator with `layout`, and must not be used
    /// after this call if a new allocation is returned.
    pub unsafe fn reallocate(
        &mut self,
        ptr: *mut u8,
        layout: Layout,
        new_size: usize,
    ) -> Option<*mut u8> {
        let thread_heap = unsafe { &mut *self.bootstrap_thread };
        unsafe { self.reallocate_with_thread(ptr, layout, new_size, thread_heap) }
    }

    /// # Safety
    ///
    /// `ptr` must have been allocated by this allocator with `layout`, and must not be used
    /// after this call if a new allocation is returned.
    pub unsafe fn reallocate_with_thread(
        &mut self,
        ptr: *mut u8,
        layout: Layout,
        new_size: usize,
        thread_heap: &mut ThreadLocalHeap,
    ) -> Option<*mut u8> {
        if ptr.is_null() {
            return self.allocate_layout_with_thread(
                thread_heap,
                Layout::from_size_align(new_size.max(1), layout.align()).ok()?,
            );
        }
        if new_size == 0 {
            self.deallocate_with_thread(ptr, thread_heap);
            return None;
        }

        let new_layout = Layout::from_size_align(new_size, layout.align()).ok()?;
        let new_ptr = self.allocate_layout_with_thread(thread_heap, new_layout)?;
        unsafe {
            copy_nonoverlapping(ptr, new_ptr, layout.size().min(new_size));
        }
        self.deallocate_with_thread(ptr, thread_heap);
        Some(new_ptr)
    }

    pub fn compact(&mut self) -> usize {
        let thread_heap = unsafe { &mut *self.bootstrap_thread };
        self.compact_with_thread(thread_heap)
    }

    pub fn compact_with_thread(&mut self, thread_heap: &mut ThreadLocalHeap) -> usize {
        let _epoch_guard = MeshingEpochGuard::new(core::ptr::addr_of!(self.mesh_epoch));
        self.stats.record_compact_call();
        self.shutdown_thread(thread_heap);

        let mut meshes = 0usize;
        for class_idx in 1..NUM_SIZE_CLASSES {
            meshes += self.mesh_class_candidates(class_idx as u8);
        }

        meshes
    }

    pub fn try_allocate_small_local(
        &self,
        thread_heap: &mut ThreadLocalHeap,
        class: u8,
    ) -> Option<*mut u8> {
        if self.mesh_epoch.load(Ordering::Acquire) & 1 != 0 {
            return None;
        }
        if thread_heap.class(class).shuffle.is_exhausted() && !self.local_refill(thread_heap, class)
        {
            return None;
        }

        let state = thread_heap.class_mut(class);
        let entry = state.shuffle.pop()?;
        let heap = state.heap_at(entry.miniheap_offset as usize)?;
        self.stats.record_small_allocation();
        Some(heap.ptr_from_offset(self.arena.base_ptr() as usize, entry.slot_index as usize))
    }

    fn allocate_small_with_thread(
        &mut self,
        thread_heap: &mut ThreadLocalHeap,
        class: u8,
    ) -> Option<*mut u8> {
        self.global_refill(thread_heap, class)?;
        self.try_allocate_small_local(thread_heap, class)
    }

    fn allocate_large(&mut self, size: usize) -> Option<*mut u8> {
        self.allocate_large_aligned(size, 1)
    }

    fn allocate_large_aligned(&mut self, size: usize, align: usize) -> Option<*mut u8> {
        let page_align = page_alignment_for(align)?;
        let (_, span) = self.arena.allocate_bytes(size, page_align)?;
        let (id, heap) = {
            let _guard = self.pool_lock.lock();
            self.pool.allocate(span, 1, size)?
        };
        self.arena.track_miniheap(span, id);
        self.stats.record_large_allocation();
        heap.malloc_at(self.arena.base_ptr() as usize, 0)
    }

    fn local_refill(&self, thread_heap: &mut ThreadLocalHeap, class: u8) -> bool {
        let state = thread_heap.class_mut(class);
        let count = state.attached_len as usize;
        if count == 0 {
            return false;
        }

        let mut scanned = 0usize;
        while scanned < count && state.shuffle.is_exhausted() {
            let idx = (state.attached_cursor as usize) % count;
            state.attached_cursor = ((idx + 1) % count) as u8;
            let heap_ptr = state.attached_heaps[idx];
            if !heap_ptr.is_null() {
                let heap = unsafe { &*heap_ptr };
                if !heap.is_full() {
                    let _ = state.shuffle.refill_from_heap(idx as u16, heap);
                }
            }
            scanned += 1;
        }

        !state.shuffle.is_exhausted()
    }

    fn global_refill(&mut self, thread_heap: &mut ThreadLocalHeap, class: u8) -> Option<()> {
        self.release_class_attached(thread_heap, class);

        let object_size = byte_size_for_class(class);
        let object_count = miniheap_object_count(object_size);
        let page_count = page_count(object_size * object_count) as u32;
        let thread_id = thread_heap.thread_id();

        let mut bytes_free = self.attach_reusable_heaps(thread_heap, class);
        while bytes_free < MINIHEAP_REFILL_GOAL_SIZE && !thread_heap.class(class).attached_full() {
            let (_, span) = self.arena.page_alloc(page_count, 1)?;
            let (id, heap) = {
                let _guard = self.pool_lock.lock();
                self.pool.allocate(span, object_count as u16, object_size)?
            };
            self.arena.track_miniheap(span, id);

            let slot = thread_heap
                .class_mut(class)
                .push_attached(id, heap as *const MiniHeap)?;
            heap.set_attached(thread_id);
            heap.set_shuffle_vector_offset(slot);
            bytes_free += heap.bytes_free();
        }

        thread_heap.class_mut(class).shuffle.clear();
        if self.local_refill(thread_heap, class) {
            Some(())
        } else {
            None
        }
    }

    fn attach_reusable_heaps(&mut self, thread_heap: &mut ThreadLocalHeap, class: u8) -> usize {
        let mut bytes_free = 0usize;
        let len = self.pool.len();
        let thread_id = thread_heap.thread_id();

        let mut id_val = 1u32;
        while id_val <= len
            && bytes_free < MINIHEAP_REFILL_GOAL_SIZE
            && !thread_heap.class(class).attached_full()
        {
            let id = MiniHeapId::new(id_val);
            if let Some(heap) = self.pool.get(id)
                && heap.size_class() == class
                && !heap.is_attached()
                && !heap.is_full()
                && !heap.is_meshed()
                && let Some(slot) = thread_heap
                    .class_mut(class)
                    .push_attached(id, heap as *const MiniHeap)
            {
                heap.set_attached(thread_id);
                heap.set_shuffle_vector_offset(slot);
                bytes_free += heap.bytes_free();
            }
            id_val += 1;
        }

        bytes_free
    }

    fn release_all_attached(&mut self, thread_heap: &mut ThreadLocalHeap) {
        for class in 1..NUM_SIZE_CLASSES as u8 {
            self.release_class_attached(thread_heap, class);
        }
    }

    pub fn shutdown_thread(&mut self, thread_heap: &mut ThreadLocalHeap) {
        self.release_all_attached(thread_heap);
    }

    fn reclaim_empty_detached_heap(&mut self, id: MiniHeapId) {
        let Some(heap) = self.pool.get(id) else {
            return;
        };
        if !heap.is_empty() || heap.is_meshed() || heap.is_attached() || heap.has_meshed_partner() {
            return;
        }

        let span = heap.span();
        self.arena.clear_miniheap(span);
        self.arena.release_span(span);
        let _ = {
            let _guard = self.pool_lock.lock();
            self.pool.release(id)
        };
    }

    fn release_class_attached(&mut self, thread_heap: &mut ThreadLocalHeap, class: u8) {
        let mut released_ids = [MiniHeapId::new(0); MAX_ATTACHED_MINIHEAPS_PER_CLASS];
        let released_len;
        {
            let state = thread_heap.class_mut(class);
            for entry in state.shuffle.active_entries() {
                let attached_idx = entry.miniheap_offset as usize;
                if attached_idx >= state.attached_len as usize {
                    continue;
                }

                if let Some(heap) = state.heap_at(attached_idx) {
                    let _ = heap.free_offset(entry.slot_index as usize);
                }
            }

            released_len = state.attached_len as usize;
            for (idx, id) in released_ids.iter_mut().enumerate().take(released_len) {
                *id = state.attached_ids[idx];
                if let Some(heap) = state.heap_at(idx) {
                    heap.unset_attached();
                }
            }
            state.clear_attached();
        }

        for id in released_ids.into_iter().take(released_len) {
            if id != MiniHeapId::new(0) {
                self.reclaim_empty_detached_heap(id);
            }
        }
    }

    fn heap_is_compaction_candidate(&self, class: u8, heap: &MiniHeap) -> bool {
        heap.size_class() == class
            && !heap.is_attached()
            && !heap.is_full()
            && !heap.is_meshed()
            && heap.object_size() < page_size()
            && is_below_partial_threshold(heap.in_use_count(), heap.max_count() as u32)
    }

    fn mesh_pair(&mut self, dst_id: MiniHeapId, src_id: MiniHeapId) -> raw_sys::Result<()> {
        let dst = self.pool.get(dst_id).expect("valid dst id");
        let src = self.pool.get(src_id).expect("valid src id");
        let span_size = dst.span_size();
        let arena_base = self.arena.base_ptr() as usize;
        let object_size = dst.object_size();
        let src_snapshot = src.bitmap().snapshot();
        let keep = dst.ptr_from_offset(arena_base, 0);
        let remove = src.ptr_from_offset(arena_base, 0);
        let barrier = ActiveMeshGuard::begin(remove, span_size)?;
        let scratch = match unsafe { self.arena.begin_mesh(remove, span_size) } {
            Ok(scratch) => scratch,
            Err(error) => {
                barrier.finish();
                return Err(error);
            }
        };

        for slot in src_snapshot.iter_set_bits() {
            let src_ptr = unsafe { scratch.add(slot * object_size) };
            let dst_ptr = dst.ptr_from_offset(arena_base, slot);
            unsafe {
                copy_nonoverlapping(src_ptr, dst_ptr, object_size);
            }
            let _ = dst.bitmap().try_set(slot);
            let _ = src.free_offset(slot);
        }

        let previous_family_head = dst.next_meshed();
        src.track_meshed_span(previous_family_head);
        dst.track_meshed_span(src_id);
        src.set_meshed();

        if let Err(error) = unsafe { self.arena.finalize_mesh(keep, remove, scratch, span_size) } {
            let _ = unsafe { self.arena.abort_mesh(remove, scratch, span_size) };
            barrier.finish();
            return Err(error);
        }
        barrier.finish();
        self.arena.free_phys(remove, span_size)?;
        self.stats.record_mesh(dst.span().length, span_size);
        Ok(())
    }

    fn mesh_class_candidates(&mut self, class: u8) -> usize {
        let mut candidate_len = self.collect_compaction_candidates(class);
        if candidate_len < 2 {
            return 0;
        }
        self.shuffle_compaction_candidates(candidate_len);

        let mut meshes = 0usize;
        while candidate_len > 1 {
            let left_index = candidate_len - 1;
            let left_id = unsafe { *self.compaction_candidates.add(left_index) };
            candidate_len -= 1;

            let mut match_index = 0usize;
            while match_index < candidate_len {
                let right_id = unsafe { *self.compaction_candidates.add(match_index) };
                let mesh_result = if let (Some(left), Some(right)) =
                    (self.pool.get(left_id), self.pool.get(right_id))
                {
                    if self.heap_is_compaction_candidate(class, left)
                        && self.heap_is_compaction_candidate(class, right)
                        && bitmaps_meshable(left.bitmap(), right.bitmap())
                    {
                        if left.has_meshed_partner() && !right.has_meshed_partner() {
                            self.mesh_pair(left_id, right_id)
                        } else if right.has_meshed_partner() && !left.has_meshed_partner() {
                            self.mesh_pair(right_id, left_id)
                        } else if !right.has_meshed_partner() {
                            self.mesh_pair(left_id, right_id)
                        } else if !left.has_meshed_partner() {
                            self.mesh_pair(right_id, left_id)
                        } else {
                            Err(raw_sys::Error(raw_sys::EAGAIN))
                        }
                    } else {
                        Err(raw_sys::Error(raw_sys::EAGAIN))
                    }
                } else {
                    Err(raw_sys::Error(raw_sys::EAGAIN))
                };

                if mesh_result.is_ok() {
                    meshes += 1;
                    if match_index != candidate_len - 1 {
                        unsafe {
                            let replacement = *self.compaction_candidates.add(candidate_len - 1);
                            self.compaction_candidates
                                .add(match_index)
                                .write(replacement);
                        }
                    }
                    candidate_len -= 1;
                    break;
                }
                match_index += 1;
            }
        }

        meshes
    }

    fn collect_compaction_candidates(&mut self, class: u8) -> usize {
        let len = self.pool.len();
        let mut candidate_len = 0usize;
        let mut id_val = 1u32;
        while id_val <= len {
            let id = MiniHeapId::new(id_val);
            if let Some(heap) = self.pool.get(id)
                && self.heap_is_compaction_candidate(class, heap)
            {
                unsafe {
                    self.compaction_candidates.add(candidate_len).write(id);
                }
                candidate_len += 1;
            }
            id_val += 1;
        }
        candidate_len
    }

    fn shuffle_compaction_candidates(&mut self, len: usize) {
        if len <= 1 {
            return;
        }

        let mut index = len - 1;
        while index > 0 {
            let swap_index = self.meshing_rng.in_range(0, index);
            unsafe {
                let left = *self.compaction_candidates.add(index);
                let right = *self.compaction_candidates.add(swap_index);
                self.compaction_candidates.add(index).write(right);
                self.compaction_candidates.add(swap_index).write(left);
            }
            index -= 1;
        }
    }

    fn resolve_pointer(&self, ptr: *mut u8) -> Option<ResolvedPtr> {
        loop {
            let start_epoch = self.mesh_epoch.load(Ordering::Acquire);
            if start_epoch & 1 != 0 {
                futex_wait_for_value(&self.mesh_epoch, start_epoch);
                continue;
            }

            let owner_id = self.arena.miniheap_id_for_ptr(ptr)?;
            let slot = self.resolve_family_slot(owner_id, ptr)?;
            let end_epoch = self.mesh_epoch.load(Ordering::Acquire);
            if start_epoch == end_epoch {
                return Some(ResolvedPtr { owner_id, slot });
            }
        }
    }

    fn resolve_family_slot(&self, owner_id: MiniHeapId, ptr: *mut u8) -> Option<usize> {
        let arena_base = self.arena.base_ptr() as usize;
        let owner = self.pool.get(owner_id)?;
        if owner.contains_ptr(arena_base, ptr) {
            return Some(owner.slot_for_ptr(arena_base, ptr));
        }

        let mut current = owner.next_meshed();
        while current.has_value() {
            let heap = self.pool.get(current)?;
            if heap.contains_ptr(arena_base, ptr) {
                return Some(heap.slot_for_ptr(arena_base, ptr));
            }
            current = heap.next_meshed();
        }

        None
    }
}

fn miniheap_object_count(object_size: usize) -> usize {
    let bitmap_limit = runtime_slots_per_span();
    (page_size() / object_size)
        .max(MIN_SHUFFLE_VECTOR_LENGTH)
        .min(bitmap_limit)
}

#[inline(always)]
fn round_up_to_alignment(size: usize, align: usize) -> Option<usize> {
    debug_assert!(align.is_power_of_two());
    let mask = align - 1;
    size.checked_add(mask).map(|value| value & !mask)
}

#[inline(always)]
fn page_alignment_for(align: usize) -> Option<u32> {
    let page = page_size();
    if align <= page {
        return Some(1);
    }

    let pages = align / page;
    if pages * page != align {
        return None;
    }

    u32::try_from(pages).ok()
}

struct MeshingEpochGuard {
    epoch: *const AtomicU32,
}

impl MeshingEpochGuard {
    fn new(epoch: *const AtomicU32) -> MeshingEpochGuard {
        let epoch_ref = unsafe { &*epoch };
        let previous = epoch_ref.fetch_add(1, Ordering::AcqRel);
        debug_assert_eq!(previous & 1, 0);
        MeshingEpochGuard { epoch }
    }
}

impl Drop for MeshingEpochGuard {
    fn drop(&mut self) {
        let epoch = unsafe { &*self.epoch };
        let previous = epoch.fetch_add(1, Ordering::AcqRel);
        debug_assert_eq!(previous & 1, 1);
        futex_wake_all(epoch);
    }
}

impl Drop for MeshAllocator {
    fn drop(&mut self) {
        unsafe {
            core::ptr::drop_in_place(self.bootstrap_thread);
            let _ = platform::munmap(
                self.bootstrap_thread.cast::<u8>(),
                size_of::<ThreadLocalHeap>(),
            );
            let _ = platform::munmap(
                self.compaction_candidates.cast::<u8>(),
                self.pool.capacity() as usize * size_of::<MiniHeapId>(),
            );
        }
    }
}
