//
// Syd: rock-solid application kernel
// src/sealbox.rs: SealBox<T> for type-safe memory sealing/protecting
//
// Copyright (c) 2025, 2026 Ali Polatel <alip@chesswob.org>
// Big thanks and respect to cehteh in ##rust for the invaluable ideas
// leading to the eventual implementation of this library.
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    borrow::Borrow,
    collections::{HashMap, VecDeque},
    ffi::CStr,
    fmt,
    hash::Hash,
    marker::PhantomData,
    mem::{self, MaybeUninit},
    num::NonZeroUsize,
    ops::{Deref, DerefMut, Index, IndexMut},
    ptr,
    ptr::NonNull,
    result::Result,
    sync::LazyLock,
};

use ahash::HashMapExt;
use libc::c_void;
use libseccomp::ScmpSyscall;
use nix::{
    errno::Errno,
    sys::mman::{mmap_anonymous, mprotect, munmap, MapFlags, ProtFlags},
    unistd::{sysconf, SysconfVar},
};

use crate::{
    compat::set_vma_anon_name,
    config::HAVE_MADV_GUARD,
    hash::{SydHashMap, SydIndexMap, SydRandomState},
};

// Note mseal(2) may not be available,
// and libc::SYS_mseal may not be defined.
// Therefore we query the number using libseccomp.
static SYS_MSEAL: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    ScmpSyscall::from_name("mseal")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
});

/// Check for mseal(2) support in the running Linux kernel.
pub fn check_mseal_support() -> bool {
    let num = if let Some(num) = *SYS_MSEAL {
        num
    } else {
        // 32-bit do not support mseal(2) yet!
        return false;
    };

    // SAFETY: Call with invalid arguments for support check.
    // mseal(2) with length == 0 is a no-op.
    Errno::result(unsafe { libc::syscall(num, 0, 0, 0) }).is_ok()
}

/// Check for madvise(2) MADV_GUARD_{INSTALL,REMOVE} support.
pub fn check_madvise_guard_support() -> bool {
    *HAVE_MADV_GUARD
}

/// Safe wrapper for mseal(2) system call.
pub fn mseal(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // 32-bit do not support mseal(2) yet!
    let num = SYS_MSEAL.ok_or(Errno::ENOSYS)?;

    // SAFETY: In libc we trust.
    Errno::result(unsafe { libc::syscall(num, addr.as_ptr(), len.get(), 0) }).map(drop)
}

/// Safe wrapper for mprotect(2) with PROT_NONE.
pub fn mprotect_none(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    unsafe { mprotect(addr, len.get(), ProtFlags::PROT_NONE) }
}

/// Safe wrapper for mprotect(2) with PROT_READ.
pub fn mprotect_readonly(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    unsafe { mprotect(addr, len.get(), ProtFlags::PROT_READ) }
}

/// Safe wrapper for mprotect(2) with PROT_EXEC.
pub fn mprotect_xonly(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    unsafe { mprotect(addr, len.get(), ProtFlags::PROT_EXEC) }
}

// Libc does not yet define MADV_GUARD_{INSTALL,REMOVE}.
const MADV_GUARD_INSTALL: i32 = 102;
const MADV_GUARD_REMOVE: i32 = 103;

/// Safe wrapper for madvise(2) MADV_GUARD_INSTALL.
pub fn madvise_guard_install(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    Errno::result(unsafe { libc::madvise(addr.as_ptr(), len.get(), MADV_GUARD_INSTALL) }).map(drop)
}

/// Safe wrapper for madvise(2) MADV_GUARD_REMOVE.
pub fn madvise_guard_remove(addr: NonNull<c_void>, len: NonZeroUsize) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    Errno::result(unsafe { libc::madvise(addr.as_ptr(), len.get(), MADV_GUARD_REMOVE) }).map(drop)
}

/// Get the system page size.
pub fn getpagesize() -> Result<usize, Errno> {
    sysconf(SysconfVar::PAGE_SIZE)?
        .ok_or(Errno::EINVAL)?
        .try_into()
        .or(Err(Errno::EINVAL))
}

/// A box for a single `T` in a page-aligned, read/write anonymous mapping,
/// which can be sealed to read-only. Supports uninitialized allocation,
/// raw pointers, and zero-boilerplate conversion from `MaybeUninit`.
#[derive(Debug)]
pub struct SealBox<T> {
    map_ptr: NonNull<c_void>,
    map_len: NonZeroUsize,
    map_nul: bool, // If true, this is a guard page.
    _marker: PhantomData<T>,
}

impl<T> SealBox<T> {
    /// Allocate and initialize `data` on a page-aligned, read/write anonymous mapping.
    pub fn new(data: T) -> Result<Self, Errno> {
        let page = getpagesize()?;
        let size = size_of::<T>();
        let (map_len, map_prot) = if size == 0 {
            // SAFETY: Zero-size, map a guard page.
            (page, ProtFlags::PROT_NONE)
        } else {
            let map_len = size.checked_next_multiple_of(page).ok_or(Errno::EINVAL)?;
            (map_len, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE)
        };
        let map_len = NonZeroUsize::new(map_len).ok_or(Errno::EINVAL)?;

        // SAFETY: valid `length` and flags guaranteed.
        let map_ptr = unsafe { mmap_anonymous(None, map_len, map_prot, MapFlags::MAP_PRIVATE)? };

        if size != 0 {
            // SAFETY: `map_ptr` is writable for `size` bytes
            unsafe {
                let dst = map_ptr.cast::<T>().as_ptr();
                ptr::write(dst, data);
            }
        }

        Ok(SealBox {
            map_ptr,
            map_len,
            map_nul: size == 0,
            _marker: PhantomData,
        })
    }

    /// Seal the mapping and return a read-only `Sealed<T>`.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<Sealed<T>, Errno> {
        if !self.map_nul {
            // Non-zero sized map, map readonly.
            mprotect_readonly(self.map_ptr, self.map_len)?;
        } else if check_madvise_guard_support() {
            // Zero sized map, install lightweight guard page.
            madvise_guard_install(self.map_ptr, self.map_len)?;
        }

        // Set VMA name in debug mode.
        // Note, this may return EINVAL if CONFIG_ANON_VMA_NAME is not set,
        // therefore we ignore errors here.
        if vma_name.is_some() {
            let _ = set_vma_anon_name(self.map_ptr, self.map_len, vma_name);
        }

        // ENOSYS: mseal(2) is not implemented (Linux>=6.10)
        // EPERM: Sealing is supported only on 64-bit CPUs, 32-bit is not supported.
        match mseal(self.map_ptr, self.map_len) {
            Ok(_) | Err(Errno::EPERM | Errno::ENOSYS) => {}
            Err(errno) => return Err(errno),
        }

        // SAFETY: Mapping is now read-only and will live for process lifetime.
        let ptr = self.map_ptr.as_ptr().cast::<T>();
        let sealed = Sealed {
            ptr,
            map_ptr: self.map_ptr,
            map_len: self.map_len,
        };

        mem::forget(self);

        Ok(sealed)
    }

    /// Get a raw mutable pointer to the contained `T`.
    pub fn as_mut_ptr(&self) -> *mut T {
        self.map_ptr.as_ptr().cast::<T>()
    }

    /// Get a mutable Rust reference to the contained `T`.
    pub fn get_mut(&mut self) -> &mut T {
        // SAFETY: mapping is RW and sized for T
        unsafe { &mut *self.map_ptr.as_ptr().cast::<T>() }
    }

    /// Allocate and immediately seal a single value.
    pub fn seal_value(data: T, vma_name: Option<&CStr>) -> Result<Sealed<T>, Errno> {
        Self::new(data)?.seal(vma_name)
    }
}

impl<T> SealBox<MaybeUninit<T>> {
    /// Allocate space for a `T` but do *not* initialize it.
    pub fn new_uninit() -> Result<Self, Errno> {
        let page = getpagesize()?;
        let size = size_of::<T>();
        let (map_len, map_prot) = if size == 0 {
            // SAFETY: Zero-size, map a guard page.
            (page, ProtFlags::PROT_NONE)
        } else {
            let map_len = size.checked_next_multiple_of(page).ok_or(Errno::EINVAL)?;
            (map_len, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE)
        };
        let map_len = NonZeroUsize::new(map_len).ok_or(Errno::EINVAL)?;

        // SAFETY: valid `length` and flags guaranteed.
        let map_ptr = unsafe { mmap_anonymous(None, map_len, map_prot, MapFlags::MAP_PRIVATE)? };

        Ok(SealBox {
            map_ptr,
            map_len,
            map_nul: size == 0,
            _marker: PhantomData,
        })
    }

    /// Overwrite the (uninitialized) slot with `value`.
    pub fn write(&mut self, value: T) {
        // SAFETY: mapping is RW and has space for T
        unsafe { ptr::write(self.map_ptr.as_ptr().cast::<T>(), value) }
    }

    /// Consume a `SealBox<MaybeUninit<T>>`, asserting you have initialized it,
    /// and turn it into a `SealBox<T>`.
    ///
    /// # Safety
    /// You must have fully initialized the `T` in the mapping.
    pub unsafe fn assume_init(self) -> SealBox<T> {
        let map_ptr = self.map_ptr;
        let map_len = self.map_len;
        let map_nul = self.map_nul;
        mem::forget(self);
        SealBox {
            map_ptr,
            map_len,
            map_nul,
            _marker: PhantomData,
        }
    }
}

impl<T> Deref for SealBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        // SAFETY: mapping is valid and readable.
        unsafe { &*self.map_ptr.as_ptr().cast::<T>() }
    }
}

impl<T> Drop for SealBox<T> {
    fn drop(&mut self) {
        // SAFETY: valid mapping to unmap.
        let _ = unsafe { munmap(self.map_ptr, self.map_len.get()) };
    }
}

/// A sealed read-only reference to `T`.
pub struct Sealed<T> {
    ptr: *const T,
    map_ptr: NonNull<c_void>,
    map_len: NonZeroUsize,
}

impl<T> Sealed<T> {
    /// Get a raw pointer to the sealed data.
    pub fn as_ptr(&self) -> *const T {
        self.ptr
    }

    /// Get the underlying mapping pointer and size.
    pub fn mapping(&self) -> (NonNull<c_void>, NonZeroUsize) {
        (self.map_ptr, self.map_len)
    }
}

impl<T> Deref for Sealed<T> {
    type Target = T;
    fn deref(&self) -> &T {
        // SAFETY: `ptr` is valid and mapping is read-only.
        unsafe { &*self.ptr }
    }
}

/// A box for a `[E]` slice in a page-aligned, read/write anonymous mapping,
/// which can be sealed to read-only.
#[derive(Debug)]
pub struct SealBoxSlice<E> {
    map_ptr: NonNull<c_void>,
    map_len: NonZeroUsize,
    len: usize,
    _marker: PhantomData<E>,
}

impl<E> SealBoxSlice<E> {
    /// Allocate a `&[E]` on a page-aligned, read/write anonymous mapping.
    ///
    /// Elements are cloned one by one into the new buffer, so `E: Clone` is required.
    pub fn from_slice(slice: &[E]) -> Result<Self, Errno>
    where
        E: Clone,
    {
        let page = getpagesize()?;
        let len = slice.len();
        if len == 0 {
            // SAFETY: Zero-size, map a guard page.
            let map_len = NonZeroUsize::new(page).ok_or(Errno::EINVAL)?;

            // SAFETY: `map_len` is page-aligned.
            let map_ptr = unsafe {
                mmap_anonymous(None, map_len, ProtFlags::PROT_NONE, MapFlags::MAP_PRIVATE)
            }?;
            return Ok(SealBoxSlice {
                map_ptr,
                map_len,
                len: 0,
                _marker: PhantomData,
            });
        }

        let elem_size = size_of::<E>();
        if elem_size == 0 {
            return Err(Errno::EINVAL);
        }
        let data_size = elem_size.checked_mul(len).ok_or(Errno::EINVAL)?;

        let map_len = data_size
            .checked_next_multiple_of(page)
            .ok_or(Errno::EINVAL)?;
        let map_len = NonZeroUsize::new(map_len).ok_or(Errno::EINVAL)?;

        // SAFETY: valid `length` and flags guaranteed.
        let map_ptr = unsafe {
            mmap_anonymous(
                None,
                map_len,
                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
                MapFlags::MAP_PRIVATE,
            )?
        };

        let dst = map_ptr.cast::<E>().as_ptr();
        for (idx, item) in slice.iter().enumerate() {
            // SAFETY: `map_ptr` is writable for `data_size` bytes.
            unsafe { ptr::write(dst.add(idx), item.clone()) };
        }

        Ok(SealBoxSlice {
            map_ptr,
            map_len,
            len,
            _marker: PhantomData,
        })
    }

    /// Allocate a `VecDeque<E>` similarly.
    ///
    /// Elements are cloned one by one into the new buffer, so `E: Clone` is required.
    pub fn new_deque(mut deque: VecDeque<E>) -> Result<Self, Errno>
    where
        E: Clone,
    {
        Self::from_slice(deque.make_contiguous())
    }

    /// Seal the mapping and return a read-only `SealedSlice<E>`.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<E>, Errno> {
        if self.len > 0 {
            // Non-zero sized slice, map read-only.
            mprotect_readonly(self.map_ptr, self.map_len)?;
        } else if check_madvise_guard_support() {
            // Zero sized slice, install lightweight guard page.
            madvise_guard_install(self.map_ptr, self.map_len)?;
        }

        // Set VMA name in debug mode.
        // Note, this may return EINVAL if CONFIG_ANON_VMA_NAME is not set,
        // therefore we ignore errors here.
        if vma_name.is_some() {
            let _ = set_vma_anon_name(self.map_ptr, self.map_len, vma_name);
        }

        // ENOSYS: mseal(2) is not implemented (Linux>=6.10)
        // EPERM: Sealing is supported only on 64-bit CPUs, 32-bit is not supported.
        match mseal(self.map_ptr, self.map_len) {
            Ok(_) | Err(Errno::EPERM | Errno::ENOSYS) => {}
            Err(errno) => return Err(errno),
        }

        // SAFETY: Mapping is now read-only and will live for process lifetime.
        let ptr = self.map_ptr.as_ptr().cast::<E>();
        let sealed = SealedSlice {
            ptr,
            len: self.len,
            map_ptr: self.map_ptr,
            map_len: self.map_len,
        };

        mem::forget(self);

        Ok(sealed)
    }
}

impl<E> Drop for SealBoxSlice<E> {
    fn drop(&mut self) {
        // SAFETY: valid mapping to unmap.
        let _ = unsafe { munmap(self.map_ptr, self.map_len.get()) };
    }
}

/// A sealed read-only slice `[E]`.
pub struct SealedSlice<E> {
    ptr: *const E,
    len: usize,
    map_ptr: NonNull<c_void>,
    map_len: NonZeroUsize,
}

impl<E> SealedSlice<E> {
    /// Get a raw pointer to the slice data.
    pub fn as_ptr(&self) -> *const E {
        self.ptr
    }

    /// Get the length of the sealed slice.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Return `true` if there are no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the underlying mapping pointer and size.
    pub fn mapping(&self) -> (NonNull<c_void>, NonZeroUsize) {
        (self.map_ptr, self.map_len)
    }
}

impl<E> Deref for SealedSlice<E> {
    type Target = [E];
    fn deref(&self) -> &[E] {
        // SAFETY: `ptr` and `len` define a valid slice in the mapping.
        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
    }
}

impl<K: Ord, V> SealBoxSlice<(K, V)> {
    /// Allocate a `HashMap<K, V>` as a sorted slice of `(K, V)`.
    pub fn from_hashmap(map: HashMap<K, V>) -> Result<Self, Errno>
    where
        K: Clone,
        V: Clone,
    {
        let mut vec: Vec<(K, V)> = map.into_iter().collect();
        vec.sort_by(|a, b| a.0.cmp(&b.0));
        Self::from_slice(&vec)
    }

    /// Allocate an `SydHashMap<K, V>` as a sorted slice of `(K, V)`.
    pub fn from_sydhashmap(map: SydHashMap<K, V>) -> Result<Self, Errno>
    where
        K: Clone,
        V: Clone,
    {
        let mut vec: Vec<(K, V)> = map.into_iter().collect();
        vec.sort_by(|a, b| a.0.cmp(&b.0));
        Self::from_slice(&vec)
    }

    /// Allocate an `SydIndexMap<K, V>` as a sorted slice of `(K, V)`.
    pub fn from_sydindexmap(map: SydIndexMap<K, V>) -> Result<Self, Errno>
    where
        K: Clone,
        V: Clone,
    {
        let mut vec: Vec<(K, V)> = map.into_iter().collect();
        vec.sort_by(|a, b| a.0.cmp(&b.0));
        Self::from_slice(&vec)
    }
}

/// Seal a single value into a read-only mapping.
pub trait SealableValue: Sized {
    /// Type of the single value.
    type Out;
    /// Memory sealing function.
    fn seal(self, vma_name: Option<&CStr>) -> Result<Self::Out, Errno>;
}

impl<T> SealableValue for T {
    type Out = Sealed<T>;
    fn seal(self, vma_name: Option<&CStr>) -> Result<Self::Out, Errno> {
        SealBox::new(self)?.seal(vma_name)
    }
}

/// Seal a sequence into a read-only slice mapping.
pub trait SealableSlice<E> {
    /// Memory sealing function to seal the slice.
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<E>, Errno>
    where
        E: Clone;
}

impl<E> SealableSlice<E> for Vec<E> {
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<E>, Errno>
    where
        E: Clone,
    {
        SealBoxSlice::from_slice(&self)?.seal(vma_name)
    }
}

impl<E> SealableSlice<E> for VecDeque<E> {
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<E>, Errno>
    where
        E: Clone,
    {
        SealBoxSlice::new_deque(self)?.seal(vma_name)
    }
}

impl<K: Ord + Clone, V: Clone> SealableSlice<(K, V)> for HashMap<K, V> {
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<(K, V)>, Errno> {
        SealBoxSlice::from_hashmap(self)?.seal(vma_name)
    }
}

impl<K: Hash + Eq + Ord + Clone, V: Clone> SealableSlice<(K, V)> for SydHashMap<K, V> {
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<(K, V)>, Errno> {
        SealBoxSlice::from_sydhashmap(self)?.seal(vma_name)
    }
}

impl<K: Hash + Eq + Ord + Clone, V: Clone> SealableSlice<(K, V)> for SydIndexMap<K, V> {
    fn seal(self, vma_name: Option<&CStr>) -> Result<SealedSlice<(K, V)>, Errno> {
        SealBoxSlice::from_sydindexmap(self)?.seal(vma_name)
    }
}

/// A `Copy` value which can be either unsealed or sealed in a read-only mapping.
pub enum Sealable<T: Copy> {
    /// The unsealed state.
    Unsealed(T),
    /// The sealed, read-only state.
    Sealed(Sealed<T>),
}

impl<T: Copy> Sealable<T> {
    /// Wrap an unsealed value.
    pub fn new(val: T) -> Self {
        Sealable::Unsealed(val)
    }

    /// Seal the value into a read-only mmap.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<Self, Errno> {
        match self {
            Sealable::Unsealed(v) => {
                let sealed = SealBox::new(v)?.seal(vma_name)?;
                Ok(Sealable::Sealed(sealed))
            }
            Sealable::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Get a reference to the inner value.
    pub fn get(&self) -> &T {
        match self {
            Sealable::Unsealed(v) => v,
            Sealable::Sealed(s) => s,
        }
    }
}

impl<T: Copy> Deref for Sealable<T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.get()
    }
}

impl<T: Copy> DerefMut for Sealable<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            Sealable::Unsealed(v) => v,
            Sealable::Sealed(_) => panic!("cannot mutably borrow a sealable"),
        }
    }
}

impl<T: Copy + fmt::Debug> fmt::Debug for Sealable<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("Sealable").field(self.get()).finish()
    }
}

impl<T: Copy + PartialEq> PartialEq for Sealable<T> {
    fn eq(&self, other: &Self) -> bool {
        *self.get() == *other.get()
    }
}

impl<T: Copy + Eq> Eq for Sealable<T> {}

impl<T: Copy + Default> Default for Sealable<T> {
    fn default() -> Self {
        Sealable::Unsealed(T::default())
    }
}

/// A Vec-like container that can be sealed into a read-only mmap.
pub enum SealableVec<T> {
    /// The unsealed, growable `Vec<T>` state.
    Unsealed(Vec<T>),
    /// The sealed, read-only slice state.
    Sealed(SealedSlice<T>),
}

impl<T> SealableVec<T> {
    /// Create from an existing `Vec<T>`.
    pub fn new(vec: Vec<T>) -> Self {
        Self::Unsealed(vec)
    }

    /// Create with a specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        Self::Unsealed(Vec::with_capacity(cap))
    }

    /// Seal into a read-only mmap, returning a new sealed container.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<SealableVec<T>, Errno>
    where
        T: Clone,
    {
        match self {
            Self::Unsealed(v) => {
                let sealed = SealBoxSlice::from_slice(&v)?.seal(vma_name)?;
                Ok(Self::Sealed(sealed))
            }
            _ => Err(Errno::EPERM),
        }
    }

    /// Return the number of elements.
    pub fn len(&self) -> usize {
        match self {
            Self::Unsealed(v) => v.len(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Return `true` if there are no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Return the current capacity (sealed==len).
    pub fn capacity(&self) -> usize {
        match self {
            Self::Unsealed(v) => v.capacity(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Get a reference to the element at `idx`.
    pub fn get(&self, idx: usize) -> Option<&T> {
        match self {
            Self::Unsealed(v) => v.get(idx),
            Self::Sealed(s) => s.get(idx),
        }
    }

    /// Get a reference to the first element.
    pub fn first(&self) -> Option<&T> {
        self.get(0)
    }

    /// Get a reference to the last element.
    pub fn last(&self) -> Option<&T> {
        self.get(self.len().checked_sub(1)?)
    }

    /// Return `true` if any element equals `x`.
    pub fn contains<U>(&self, x: &U) -> bool
    where
        T: PartialEq<U>,
    {
        self.iter().any(|e| e == x)
    }

    /// Return the position of the first element matching `predicate`.
    pub fn position<P>(&self, predicate: P) -> Option<usize>
    where
        P: FnMut(&T) -> bool,
    {
        self.iter().position(predicate)
    }

    /// Return an iterator over all elements.
    pub fn iter(&self) -> std::slice::Iter<'_, T> {
        match self {
            Self::Unsealed(v) => v.iter(),
            Self::Sealed(s) => s.iter(),
        }
    }

    /// Return a borrowed slice of all elements.
    pub fn as_slice(&self) -> &[T] {
        self
    }

    /// Push `value` if unsealed, else return `Err(EPERM)`.
    pub fn push(&mut self, value: T) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.push(value);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Pop the last element if unsealed, else return `None`.
    pub fn pop(&mut self) -> Option<T> {
        match self {
            Self::Unsealed(v) => v.pop(),
            Self::Sealed(_) => None,
        }
    }

    /// Insert at `index` if unsealed, else return `Err(EPERM)`.
    pub fn insert(&mut self, index: usize, element: T) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.insert(index, element);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Remove and return element at `index` if unsealed, else return `None`.
    pub fn remove(&mut self, index: usize) -> Option<T> {
        match self {
            Self::Unsealed(v) => Some(v.remove(index)),
            Self::Sealed(_) => None,
        }
    }

    /// Clear all elements if unsealed.
    pub fn clear(&mut self) {
        if let Self::Unsealed(v) = self {
            v.clear();
        }
    }

    /// Reserve capacity for `additional` more elements if unsealed.
    pub fn reserve(&mut self, additional: usize) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.reserve(additional);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Shrink to fit if unsealed.
    pub fn shrink_to_fit(&mut self) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.shrink_to_fit();
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Append `other` Vec if unsealed, else return `Err(EPERM)`.
    pub fn append(&mut self, other: &mut Vec<T>) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.append(other);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Split off at `at` if unsealed, else return `Err(EPERM)`.
    pub fn split_off(&mut self, at: usize) -> Result<Vec<T>, Errno> {
        match self {
            Self::Unsealed(v) => Ok(v.split_off(at)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Retain only elements matching `f` if unsealed, else return `Err(EPERM)`.
    pub fn retain<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&T) -> bool,
    {
        match self {
            Self::Unsealed(v) => {
                v.retain(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Retain only elements matching `f` if unsealed, else return `Err(EPERM)`.
    pub fn retain_mut<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&mut T) -> bool,
    {
        match self {
            Self::Unsealed(v) => {
                v.retain_mut(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Extend from `iter` if unsealed, else return `Err(EPERM)`.
    pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.extend(iter);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Drain `range` if unsealed, else return `Err(EPERM)`.
    pub fn drain<R>(&mut self, range: R) -> Result<std::vec::Drain<'_, T>, Errno>
    where
        R: std::ops::RangeBounds<usize>,
    {
        match self {
            Self::Unsealed(v) => Ok(v.drain(range)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }
}

impl<T> Deref for SealableVec<T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Unsealed(v) => v.as_slice(),
            Self::Sealed(s) => s.deref(),
        }
    }
}

impl<T> DerefMut for SealableVec<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            Self::Unsealed(v) => v.as_mut_slice(),
            Self::Sealed(_) => panic!("cannot mutably borrow a sealed Vec"),
        }
    }
}

impl<T> Index<usize> for SealableVec<T> {
    type Output = T;
    fn index(&self, i: usize) -> &T {
        &self.deref()[i]
    }
}

impl<T> IndexMut<usize> for SealableVec<T> {
    fn index_mut(&mut self, i: usize) -> &mut T {
        &mut self.deref_mut()[i]
    }
}

impl<T: PartialEq> PartialEq for SealableVec<T> {
    fn eq(&self, other: &Self) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl<T: Eq> Eq for SealableVec<T> {}

impl<T> Default for SealableVec<T> {
    fn default() -> Self {
        Self::Unsealed(Vec::new())
    }
}

impl<T: fmt::Debug> fmt::Debug for SealableVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

impl<T> From<Vec<T>> for SealableVec<T> {
    fn from(v: Vec<T>) -> Self {
        SealableVec::Unsealed(v)
    }
}

/// A VecDeque-like container that can be sealed into a read-only mmap.
pub enum SealableVecDeque<T> {
    /// The unsealed, growable `VecDeque<T>` state.
    Unsealed(VecDeque<T>),
    /// The sealed, read-only slice state.
    Sealed(SealedSlice<T>),
}

impl<T> SealableVecDeque<T> {
    /// Create from an existing `VecDeque<T>`.
    pub fn new(dq: VecDeque<T>) -> Self {
        Self::Unsealed(dq)
    }

    /// Create with a specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        Self::Unsealed(VecDeque::with_capacity(cap))
    }

    /// Seal into a read-only mmap, returning a new sealed container.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<SealableVecDeque<T>, Errno>
    where
        T: Clone,
    {
        match self {
            Self::Unsealed(dq) => {
                let sealed = SealBoxSlice::new_deque(dq)?.seal(vma_name)?;
                Ok(Self::Sealed(sealed))
            }
            _ => Err(Errno::EPERM),
        }
    }

    /// Return the number of elements.
    pub fn len(&self) -> usize {
        match self {
            Self::Unsealed(dq) => dq.len(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Return `true` if there are no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Return the current capacity (sealed==len).
    pub fn capacity(&self) -> usize {
        match self {
            Self::Unsealed(dq) => dq.capacity(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Get a reference to the element at `idx`.
    pub fn get(&self, idx: usize) -> Option<&T> {
        match self {
            Self::Unsealed(dq) => dq.get(idx),
            Self::Sealed(s) => s.get(idx),
        }
    }

    /// Get a reference to the front element.
    pub fn front(&self) -> Option<&T> {
        self.get(0)
    }

    /// Get a reference to the back element.
    pub fn back(&self) -> Option<&T> {
        self.get(self.len().checked_sub(1)?)
    }

    /// Return `true` if any element equals `x`.
    pub fn contains<U>(&self, x: &U) -> bool
    where
        T: PartialEq<U>,
    {
        self.iter().any(|e| e == x)
    }

    /// Return an iterator over all elements (linearizing head+tail for unsealed).
    pub fn iter(&self) -> std::iter::Chain<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>> {
        match self {
            Self::Unsealed(dq) => {
                let (head, tail) = dq.as_slices();
                head.iter().chain(tail.iter())
            }
            Self::Sealed(s) => s.iter().chain([].iter()),
        }
    }

    /// Return the two contiguous slices (tail, head) or entire slice if sealed.
    pub fn as_slices(&self) -> (&[T], &[T]) {
        match self {
            Self::Unsealed(dq) => dq.as_slices(),
            Self::Sealed(s) => (s.deref(), &[]),
        }
    }

    /// Push `elem` to the back if unsealed, else return `Err(EPERM)`.
    pub fn push_back(&mut self, elem: T) -> Result<(), Errno> {
        match self {
            Self::Unsealed(dq) => {
                dq.push_back(elem);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Push `elem` to the front if unsealed, else return `Err(EPERM)`.
    pub fn push_front(&mut self, elem: T) -> Result<(), Errno> {
        match self {
            Self::Unsealed(dq) => {
                dq.push_front(elem);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Pop from the back if unsealed, else return `None`.
    pub fn pop_back(&mut self) -> Option<T> {
        match self {
            Self::Unsealed(dq) => dq.pop_back(),
            Self::Sealed(_) => None,
        }
    }

    /// Pop from the front if unsealed, else return `None`.
    pub fn pop_front(&mut self) -> Option<T> {
        match self {
            Self::Unsealed(dq) => dq.pop_front(),
            Self::Sealed(_) => None,
        }
    }

    /// Remove and return element at `index` if unsealed, else return `None`.
    pub fn remove(&mut self, index: usize) -> Option<T> {
        match self {
            Self::Unsealed(v) => v.remove(index),
            Self::Sealed(_) => None,
        }
    }

    /// Clear all elements if unsealed.
    pub fn clear(&mut self) {
        if let Self::Unsealed(dq) = self {
            dq.clear();
        }
    }

    /// Reserve capacity for `additional` more elements if unsealed.
    pub fn reserve(&mut self, additional: usize) -> Result<(), Errno> {
        match self {
            Self::Unsealed(dq) => {
                dq.reserve(additional);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Rearranges the internal storage of this deque so it is one contiguous slice.
    pub fn make_contiguous(&mut self) -> Result<(), Errno> {
        match self {
            Self::Unsealed(dq) => {
                dq.make_contiguous();
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Shrink to fit if unsealed.
    pub fn shrink_to_fit(&mut self) -> Result<(), Errno> {
        match self {
            Self::Unsealed(dq) => {
                dq.shrink_to_fit();
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Retain only elements matching `f` if unsealed, else return `Err(EPERM)`.
    pub fn retain<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&T) -> bool,
    {
        match self {
            Self::Unsealed(v) => {
                v.retain(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Retain only elements matching `f` if unsealed, else return `Err(EPERM)`.
    pub fn retain_mut<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&mut T) -> bool,
    {
        match self {
            Self::Unsealed(v) => {
                v.retain_mut(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Extend from `iter` if unsealed, else return `Err(EPERM)`.
    pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) -> Result<(), Errno> {
        match self {
            Self::Unsealed(v) => {
                v.extend(iter);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Drain `range` if unsealed, else return `Err(EPERM)`.
    pub fn drain<R>(&mut self, range: R) -> Result<std::collections::vec_deque::Drain<'_, T>, Errno>
    where
        R: std::ops::RangeBounds<usize>,
    {
        match self {
            Self::Unsealed(v) => Ok(v.drain(range)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }
}

impl<T> Deref for SealableVecDeque<T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Unsealed(dq) => {
                let (head, _) = dq.as_slices();
                head
            }
            Self::Sealed(s) => s.deref(),
        }
    }
}

impl<T> DerefMut for SealableVecDeque<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            Self::Unsealed(dq) => {
                let (head, _) = dq.as_mut_slices();
                head
            }
            Self::Sealed(_) => panic!("cannot mutably borrow a sealed VecDeque"),
        }
    }
}

impl<T> Index<usize> for SealableVecDeque<T> {
    type Output = T;
    fn index(&self, i: usize) -> &T {
        &self.deref()[i]
    }
}

impl<T> IndexMut<usize> for SealableVecDeque<T> {
    fn index_mut(&mut self, i: usize) -> &mut T {
        &mut self.deref_mut()[i]
    }
}

impl<T: PartialEq> PartialEq for SealableVecDeque<T> {
    fn eq(&self, other: &Self) -> bool {
        self.deref() == other.deref()
    }
}

impl<T: Eq> Eq for SealableVecDeque<T> {}

impl<T> Default for SealableVecDeque<T> {
    fn default() -> Self {
        Self::Unsealed(VecDeque::new())
    }
}

impl<T: fmt::Debug> fmt::Debug for SealableVecDeque<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

impl<T> From<VecDeque<T>> for SealableVecDeque<T> {
    fn from(dq: VecDeque<T>) -> Self {
        Self::Unsealed(dq)
    }
}

// Allow iteration by reference over SealableVec<T>
impl<'a, T> IntoIterator for &'a SealableVec<T> {
    type Item = &'a T;
    type IntoIter = std::slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

// Allow iteration by reference over SealableVecDeque<T>
impl<'a, T> IntoIterator for &'a SealableVecDeque<T> {
    type Item = &'a T;
    type IntoIter = std::iter::Chain<std::slice::Iter<'a, T>, std::slice::Iter<'a, T>>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

/// A HashMap-like container that can be sealed into a read-only mmap of sorted entries.
pub enum SealableHashMap<K, V> {
    /// The unsealed, growable `HashMap<K, V>` state.
    Unsealed(HashMap<K, V>),
    /// The sealed, read-only slice state of sorted `(K, V)` pairs.
    Sealed(SealedSlice<(K, V)>),
}

impl<K, V> SealableHashMap<K, V> {
    /// Create from an existing `HashMap<K, V>`.
    pub fn new(map: HashMap<K, V>) -> Self {
        Self::Unsealed(map)
    }

    /// Create with a specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        Self::Unsealed(HashMap::with_capacity(cap))
    }

    /// Seal into a read-only mmap of sorted `(K, V)` pairs.
    ///
    /// In the sealed state, lookups use binary search on the sorted slice.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<Self, Errno>
    where
        K: Ord + Clone,
        V: Clone,
    {
        match self {
            Self::Unsealed(map) => {
                let sealed = SealBoxSlice::from_hashmap(map)?.seal(vma_name)?;
                Ok(Self::Sealed(sealed))
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the number of elements.
    pub fn len(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.len(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Returns `true` if the map contains no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get a reference to the value corresponding to the key.
    ///
    /// In the sealed state, uses binary search on the sorted slice.
    pub fn get<Q>(&self, key: &Q) -> Option<&V>
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.get(key),
            Self::Sealed(s) => s
                .binary_search_by(|(k, _)| k.borrow().cmp(key))
                .ok()
                .map(|idx| &s[idx].1),
        }
    }

    /// Returns `true` if the map contains the specified key.
    pub fn contains_key<Q>(&self, key: &Q) -> bool
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        self.get(key).is_some()
    }

    /// Inserts a key-value pair into the map.
    ///
    /// Returns the old value if the key was already present.
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>, Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => Ok(m.insert(key, value)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Removes a key from the map, returning the value if it was present.
    ///
    /// In the sealed state, always returns `None`.
    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
    where
        K: Borrow<Q> + Eq + Hash,
        Q: Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.remove(key),
            Self::Sealed(_) => None,
        }
    }

    /// Clears the map if unsealed; does nothing if sealed.
    pub fn clear(&mut self) {
        if let Self::Unsealed(m) = self {
            m.clear();
        }
    }

    /// Reserve capacity for at least `additional` more elements if unsealed.
    ///
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn reserve(&mut self, additional: usize) -> Result<(), Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => {
                m.reserve(additional);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the current capacity: for unsealed, the hash map's bucket capacity;
    /// for sealed, the number of entries.
    pub fn capacity(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.capacity(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Retain only the entries for which `f(key, value)` returns true.
    pub fn retain<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&K, &mut V) -> bool,
    {
        match self {
            Self::Unsealed(m) => {
                m.retain(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }
}

impl<K, V> Deref for SealableHashMap<K, V> {
    type Target = [(K, V)];
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Unsealed(_) => &[],
            Self::Sealed(s) => s.deref(),
        }
    }
}

impl<K, V> Default for SealableHashMap<K, V> {
    fn default() -> Self {
        Self::Unsealed(HashMap::new())
    }
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for SealableHashMap<K, V> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unsealed(m) => m.fmt(f),
            Self::Sealed(s) => {
                let mut dm = f.debug_map();
                for pair in s.iter() {
                    dm.entry(&pair.0, &pair.1);
                }
                dm.finish()
            }
        }
    }
}

impl<K: Ord + Eq + Hash, V: PartialEq> PartialEq for SealableHashMap<K, V> {
    fn eq(&self, other: &Self) -> bool {
        if self.len() != other.len() {
            return false;
        }
        match self {
            Self::Unsealed(m) => {
                for (k, v) in m {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
            Self::Sealed(s) => {
                for (k, v) in s.iter() {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
        }
        true
    }
}

impl<K: Ord + Eq + Hash, V: Eq> Eq for SealableHashMap<K, V> {}

impl<K, V> From<HashMap<K, V>> for SealableHashMap<K, V> {
    fn from(m: HashMap<K, V>) -> Self {
        Self::Unsealed(m)
    }
}

impl<'a, K, V> IntoIterator for &'a SealableHashMap<K, V>
where
    K: Borrow<K> + fmt::Debug + Ord + Eq + Hash,
{
    type Item = (&'a K, &'a V);
    type IntoIter = Box<dyn Iterator<Item = (&'a K, &'a V)> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            SealableHashMap::Unsealed(m) => Box::new(m.iter()),
            SealableHashMap::Sealed(s) => Box::new(s.iter().map(|(k, v)| (k, v))),
        }
    }
}

/// A HashMap-like container that can be sealed into a read-only mmap of sorted entries.
pub enum SealableSydHashMap<K, V> {
    /// The unsealed, growable `SydHashMap<K, V>` state.
    Unsealed(SydHashMap<K, V>),
    /// The sealed, read-only slice state of sorted `(K, V)` pairs.
    Sealed(SealedSlice<(K, V)>),
}

impl<K, V> SealableSydHashMap<K, V> {
    /// Create from an existing `SydHashMap<K, V>`.
    pub fn new(map: SydHashMap<K, V>) -> Self {
        Self::Unsealed(map)
    }

    /// Create with a specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        Self::Unsealed(SydHashMap::with_capacity(cap))
    }

    /// Seal into a read-only mmap of sorted `(K, V)` pairs.
    ///
    /// In the sealed state, lookups use binary search on the sorted slice.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<Self, Errno>
    where
        K: Ord + Clone,
        V: Clone,
    {
        match self {
            Self::Unsealed(map) => {
                let sealed = SealBoxSlice::from_sydhashmap(map)?.seal(vma_name)?;
                Ok(Self::Sealed(sealed))
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the number of elements.
    pub fn len(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.len(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Returns `true` if the map contains no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get a reference to the value corresponding to the key.
    ///
    /// In the sealed state, uses binary search on the sorted slice.
    pub fn get<Q>(&self, key: &Q) -> Option<&V>
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.get(key),
            Self::Sealed(s) => s
                .binary_search_by(|(k, _)| k.borrow().cmp(key))
                .ok()
                .map(|idx| &s[idx].1),
        }
    }

    /// Returns `true` if the map contains the specified key.
    pub fn contains_key<Q>(&self, key: &Q) -> bool
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        self.get(key).is_some()
    }

    /// Inserts a key-value pair into the map.
    ///
    /// Returns the old value if the key was already present.
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>, Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => Ok(m.insert(key, value)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Removes a key from the map, returning the value if it was present.
    ///
    /// In the sealed state, always returns `None`.
    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
    where
        K: Borrow<Q> + Eq + Hash,
        Q: Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.remove(key),
            Self::Sealed(_) => None,
        }
    }

    /// Clears the map if unsealed; does nothing if sealed.
    pub fn clear(&mut self) {
        if let Self::Unsealed(m) = self {
            m.clear();
        }
    }

    /// Reserve capacity for at least `additional` more elements if unsealed.
    ///
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn reserve(&mut self, additional: usize) -> Result<(), Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => {
                m.reserve(additional);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the current capacity: for unsealed, the hash map's bucket capacity;
    /// for sealed, the number of entries.
    pub fn capacity(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.capacity(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Retain only the entries for which `f(key, value)` returns true.
    pub fn retain<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&K, &mut V) -> bool,
    {
        match self {
            Self::Unsealed(m) => {
                m.retain(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }
}

impl<K, V> Deref for SealableSydHashMap<K, V> {
    type Target = [(K, V)];
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Unsealed(_) => &[],
            Self::Sealed(s) => s.deref(),
        }
    }
}

impl<K, V> Default for SealableSydHashMap<K, V> {
    fn default() -> Self {
        Self::Unsealed(SydHashMap::new())
    }
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for SealableSydHashMap<K, V> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unsealed(m) => m.fmt(f),
            Self::Sealed(s) => {
                let mut dm = f.debug_map();
                for pair in s.iter() {
                    dm.entry(&pair.0, &pair.1);
                }
                dm.finish()
            }
        }
    }
}

impl<K: Ord + Eq + Hash, V: PartialEq> PartialEq for SealableSydHashMap<K, V> {
    fn eq(&self, other: &Self) -> bool {
        if self.len() != other.len() {
            return false;
        }
        match self {
            Self::Unsealed(m) => {
                for (k, v) in m {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
            Self::Sealed(s) => {
                for (k, v) in s.iter() {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
        }
        true
    }
}

impl<K: Ord + Eq + Hash, V: Eq> Eq for SealableSydHashMap<K, V> {}

impl<K, V> From<SydHashMap<K, V>> for SealableSydHashMap<K, V> {
    fn from(m: SydHashMap<K, V>) -> Self {
        Self::Unsealed(m)
    }
}

impl<'a, K, V> IntoIterator for &'a SealableSydHashMap<K, V>
where
    K: Borrow<K> + fmt::Debug + Ord + Eq + Hash,
{
    type Item = (&'a K, &'a V);
    type IntoIter = Box<dyn Iterator<Item = (&'a K, &'a V)> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            SealableSydHashMap::Unsealed(m) => Box::new(m.iter()),
            SealableSydHashMap::Sealed(s) => Box::new(s.iter().map(|(k, v)| (k, v))),
        }
    }
}

/// A HashMap-like container that can be sealed into a read-only mmap of sorted entries.
pub enum SealableSydIndexMap<K, V> {
    /// The unsealed, growable `SydIndexMap<K, V>` state.
    Unsealed(SydIndexMap<K, V>),
    /// The sealed, read-only slice state of sorted `(K, V)` pairs.
    Sealed(SealedSlice<(K, V)>),
}

impl<K, V> SealableSydIndexMap<K, V> {
    /// Create from an existing `SydIndexMap<K, V>`.
    pub fn new(map: SydIndexMap<K, V>) -> Self {
        Self::Unsealed(map)
    }

    /// Create with a specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        Self::Unsealed(SydIndexMap::with_capacity_and_hasher(
            cap,
            SydRandomState::new(),
        ))
    }

    /// Seal into a read-only mmap of sorted `(K, V)` pairs.
    ///
    /// In the sealed state, lookups use binary search on the sorted slice.
    pub fn seal(self, vma_name: Option<&CStr>) -> Result<Self, Errno>
    where
        K: Ord + Clone,
        V: Clone,
    {
        match self {
            Self::Unsealed(map) => {
                let sealed = SealBoxSlice::from_sydindexmap(map)?.seal(vma_name)?;
                Ok(Self::Sealed(sealed))
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the number of elements.
    pub fn len(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.len(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Returns `true` if the map contains no elements.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get a reference to the value corresponding to the key.
    ///
    /// In the sealed state, uses binary search on the sorted slice.
    pub fn get<Q>(&self, key: &Q) -> Option<&V>
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.get(key),
            Self::Sealed(s) => s
                .binary_search_by(|(k, _)| k.borrow().cmp(key))
                .ok()
                .map(|idx| &s[idx].1),
        }
    }

    /// Returns `true` if the map contains the specified key.
    pub fn contains_key<Q>(&self, key: &Q) -> bool
    where
        K: Borrow<Q> + Ord + Eq + Hash,
        Q: Ord + Eq + Hash + ?Sized,
    {
        self.get(key).is_some()
    }

    /// Inserts a key-value pair into the map.
    ///
    /// Returns the old value if the key was already present.
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>, Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => Ok(m.insert(key, value)),
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Removes a key from the map, returning the value if it was present.
    ///
    /// In the sealed state, always returns `None`.
    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
    where
        K: Borrow<Q> + Eq + Hash,
        Q: Eq + Hash + ?Sized,
    {
        match self {
            Self::Unsealed(m) => m.shift_remove(key),
            Self::Sealed(_) => None,
        }
    }

    /// Clears the map if unsealed; does nothing if sealed.
    pub fn clear(&mut self) {
        if let Self::Unsealed(m) = self {
            m.clear();
        }
    }

    /// Reserve capacity for at least `additional` more elements if unsealed.
    ///
    /// In the sealed state, returns `Err(EPERM)`.
    pub fn reserve(&mut self, additional: usize) -> Result<(), Errno>
    where
        K: Eq + Hash,
    {
        match self {
            Self::Unsealed(m) => {
                m.reserve(additional);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }

    /// Returns the current capacity: for unsealed, the hash map's bucket capacity;
    /// for sealed, the number of entries.
    pub fn capacity(&self) -> usize {
        match self {
            Self::Unsealed(m) => m.capacity(),
            Self::Sealed(s) => s.len(),
        }
    }

    /// Retain only the entries for which `f(key, value)` returns true.
    pub fn retain<F>(&mut self, f: F) -> Result<(), Errno>
    where
        F: FnMut(&K, &mut V) -> bool,
    {
        match self {
            Self::Unsealed(m) => {
                m.retain(f);
                Ok(())
            }
            Self::Sealed(_) => Err(Errno::EPERM),
        }
    }
}

impl<K, V> Deref for SealableSydIndexMap<K, V> {
    type Target = [(K, V)];
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Unsealed(_) => &[],
            Self::Sealed(s) => s.deref(),
        }
    }
}

impl<K, V> Default for SealableSydIndexMap<K, V> {
    fn default() -> Self {
        Self::Unsealed(SydIndexMap::default())
    }
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for SealableSydIndexMap<K, V> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unsealed(m) => m.fmt(f),
            Self::Sealed(s) => {
                let mut dm = f.debug_map();
                for pair in s.iter() {
                    dm.entry(&pair.0, &pair.1);
                }
                dm.finish()
            }
        }
    }
}

impl<K: Ord + Eq + Hash, V: PartialEq> PartialEq for SealableSydIndexMap<K, V> {
    fn eq(&self, other: &Self) -> bool {
        if self.len() != other.len() {
            return false;
        }
        match self {
            Self::Unsealed(m) => {
                for (k, v) in m {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
            Self::Sealed(s) => {
                for (k, v) in s.iter() {
                    if other.get(k) != Some(v) {
                        return false;
                    }
                }
            }
        }
        true
    }
}

impl<K: Ord + Eq + Hash, V: Eq> Eq for SealableSydIndexMap<K, V> {}

impl<K, V> From<SydIndexMap<K, V>> for SealableSydIndexMap<K, V> {
    fn from(m: SydIndexMap<K, V>) -> Self {
        Self::Unsealed(m)
    }
}

impl<'a, K, V> IntoIterator for &'a SealableSydIndexMap<K, V>
where
    K: Borrow<K> + fmt::Debug + Ord + Eq + Hash,
{
    type Item = (&'a K, &'a V);
    type IntoIter = Box<dyn DoubleEndedIterator<Item = (&'a K, &'a V)> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            SealableSydIndexMap::Unsealed(m) => Box::new(m.iter()),
            SealableSydIndexMap::Sealed(s) => Box::new(s.iter().map(|(k, v)| (k, v))),
        }
    }
}

// SAFETY: underlying memory mappings are thread-safe and can be moved/shared.
unsafe impl<T: Send> Send for SealBox<T> {}
// SAFETY: ditto.
unsafe impl<T: Sync> Sync for SealBox<T> {}

// SAFETY: ditto.
unsafe impl<T: Send> Send for Sealed<T> {}
// SAFETY: ditto.
unsafe impl<T: Sync> Sync for Sealed<T> {}

// SAFETY: ditto.
unsafe impl<T: Copy + Send> Send for Sealable<T> {}
// SAFETY: ditto.
unsafe impl<T: Copy + Sync> Sync for Sealable<T> {}

// SAFETY: ditto.
unsafe impl<E: Send> Send for SealBoxSlice<E> {}
// SAFETY: ditto.
unsafe impl<E: Sync> Sync for SealBoxSlice<E> {}

// SAFETY: ditto.
unsafe impl<E: Send> Send for SealedSlice<E> {}
// SAFETY: ditto.
unsafe impl<E: Sync> Sync for SealedSlice<E> {}

// SAFETY: ditto.
unsafe impl<T: Send> Send for SealableVec<T> {}
// SAFETY: ditto.
unsafe impl<T: Sync> Sync for SealableVec<T> {}

// SAFETY: ditto.
unsafe impl<T: Send> Send for SealableVecDeque<T> {}
// SAFETY: ditto.
unsafe impl<T: Sync> Sync for SealableVecDeque<T> {}

// SAFETY: ditto.
unsafe impl<K: Send, V: Send> Send for SealableHashMap<K, V> {}
// SAFETY: ditto.
unsafe impl<K: Send, V: Sync> Sync for SealableHashMap<K, V> {}

// SAFETY: ditto.
unsafe impl<K: Send, V: Send> Send for SealableSydHashMap<K, V> {}
// SAFETY: ditto.
unsafe impl<K: Send, V: Sync> Sync for SealableSydHashMap<K, V> {}

// SAFETY: ditto.
unsafe impl<K: Send, V: Send> Send for SealableSydIndexMap<K, V> {}
// SAFETY: ditto.
unsafe impl<K: Send, V: Sync> Sync for SealableSydIndexMap<K, V> {}

#[cfg(test)]
mod tests {
    use nix::{
        errno::Errno,
        sys::{
            mman::{madvise, mremap, MRemapFlags, MmapAdvise, ProtFlags},
            signal::Signal,
            wait::{waitpid, WaitStatus},
        },
        unistd::{fork, ForkResult},
    };

    use super::*;

    fn is_sealed(ptr: NonNull<c_void>, len: NonZeroUsize) -> bool {
        matches!(
            unsafe { mprotect(ptr, len.get(), ProtFlags::PROT_READ | ProtFlags::PROT_WRITE) },
            Err(Errno::EPERM)
        )
    }

    #[test]
    fn test_unmap_sealed_fails() {
        if !check_mseal_support() {
            return;
        }
        let s = SealBox::seal_value(0xDEADu32, None).unwrap();
        let (ptr, size) = s.mapping();
        assert_eq!(
            unsafe { munmap(ptr, size.get()) }.unwrap_err(),
            Errno::EPERM
        );
    }

    #[test]
    fn test_mremap_sealed_fails() {
        if !check_mseal_support() {
            return;
        }
        let s = SealBox::seal_value(1234usize, None).unwrap();
        let (ptr, size) = s.mapping();
        assert_eq!(
            unsafe {
                mremap(
                    ptr,
                    size.get(),
                    size.get(),
                    MRemapFlags::MREMAP_MAYMOVE,
                    None,
                )
            }
            .unwrap_err(),
            Errno::EPERM
        );
    }

    #[test]
    fn test_madvise_dontneed_sealed_fails() {
        if !check_mseal_support() {
            return;
        }
        let s = SealBox::seal_value(66u16, None).unwrap();
        let (ptr, size) = s.mapping();
        assert_eq!(
            unsafe { madvise(ptr, size.get(), MmapAdvise::MADV_DONTNEED) }.unwrap_err(),
            Errno::EPERM
        );
    }

    #[test]
    fn test_madvise_free_sealed_fails() {
        if !check_mseal_support() {
            return;
        }
        let s = SealBox::seal_value(66u16, None).unwrap();
        let (ptr, size) = s.mapping();
        assert_eq!(
            unsafe { madvise(ptr, size.get(), MmapAdvise::MADV_FREE) }.unwrap_err(),
            Errno::EPERM
        );
    }

    #[test]
    fn test_seal_scalar() {
        if !check_mseal_support() {
            return;
        }
        let sb = SealBox::new(7u8).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(*s, 7);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_new_uninit_and_assume_init() {
        if !check_mseal_support() {
            return;
        }
        let mut sb = SealBox::<MaybeUninit<u32>>::new_uninit().unwrap();
        sb.write(42);
        let sb = unsafe { sb.assume_init() };
        let s = sb.seal(None).unwrap();
        assert_eq!(*s, 42);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_seal_slice() {
        if !check_mseal_support() {
            return;
        }
        let sb = SealBoxSlice::from_slice(&[1u16, 2, 3]).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(&*s, &[1, 2, 3]);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_seal_deque() {
        if !check_mseal_support() {
            return;
        }
        let mut dq = VecDeque::new();
        dq.push_back(10u32);
        dq.push_back(20);
        let sb = SealBoxSlice::new_deque(dq).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(&*s, &[10, 20]);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_seal_hashmap() {
        if !check_mseal_support() {
            return;
        }
        let mut m = HashMap::new();
        m.insert("a", 1);
        m.insert("b", 2);
        let sb: SealBoxSlice<(&str, i32)> = SealBoxSlice::from_hashmap(m).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(&*s, &[("a", 1), ("b", 2)]);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_seal_sydhashmap() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SydHashMap::new();
        m.insert("x", 100);
        m.insert("y", 200);
        let sb: SealBoxSlice<(&str, i32)> = SealBoxSlice::from_sydhashmap(m).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(&*s, &[("x", 100), ("y", 200)]);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_seal_sydindexmap() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SydIndexMap::default();
        m.insert("x", 100);
        m.insert("y", 200);
        let sb: SealBoxSlice<(&str, i32)> = SealBoxSlice::from_sydindexmap(m).unwrap();
        let s = sb.seal(None).unwrap();
        assert_eq!(&*s, &[("x", 100), ("y", 200)]);
        let (ptr, size) = s.mapping();
        assert!(is_sealed(ptr, size));
    }

    #[test]
    fn test_multi_page_slice_seal() {
        if !check_mseal_support() {
            return;
        }
        // Allocate a slice larger than one page
        let page = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize;
        let len = page * 2 + 123; // two full pages plus extra bytes
        let data = vec![0xABu8; len];
        // Create and seal the slice
        let sb_slice = SealBoxSlice::from_slice(&data).expect("allocation failed");
        let sealed = sb_slice.seal(None).expect("seal failed");
        // Contents must match
        assert_eq!(&*sealed, &data[..]);
        // Ensure the mapping is read-only
        let (ptr, size) = sealed.mapping();
        assert!(is_sealed(ptr, size));
        // Size should be rounded up to a page multiple and cover the data
        assert_eq!(size.get() % page, 0);
        assert!(size.get() >= len);
    }

    #[test]
    fn test_vec_unsealed_basic() {
        if !check_mseal_support() {
            return;
        }
        let mut sv = SealableVec::new(vec![1, 2, 3]);
        assert_eq!(sv.len(), 3);
        assert!(sv.push(4).is_ok());
        assert_eq!(&*sv, &[1, 2, 3, 4]);
        assert_eq!(sv.pop(), Some(4));
    }

    #[test]
    fn test_vec_sealed_readonly() {
        if !check_mseal_support() {
            return;
        }
        let mut sv = SealableVec::new(vec![10, 20, 30]).seal(None).unwrap();
        assert_eq!(sv.len(), 3);
        assert_eq!(sv.get(1), Some(&20));
        assert!(matches!(sv.push(40), Err(Errno::EPERM)));
    }

    #[test]
    fn test_deque_unsealed_basic() {
        if !check_mseal_support() {
            return;
        }
        let mut sd = SealableVecDeque::from(VecDeque::from([1, 2]));
        sd.push_back(3).unwrap();
        sd.push_front(0).unwrap();
        sd.make_contiguous().unwrap();
        assert_eq!(&*sd, &[0, 1, 2, 3]);
        assert_eq!(sd.pop_front(), Some(0));
    }

    #[test]
    fn test_deque_sealed_readonly() {
        if !check_mseal_support() {
            return;
        }
        let mut sd = SealableVecDeque::from(VecDeque::from([5, 6, 7]))
            .seal(None)
            .unwrap();
        assert_eq!(sd.len(), 3);
        assert_eq!(sd.front(), Some(&5));
        assert!(matches!(sd.push_back(8), Err(Errno::EPERM)));
    }

    #[test]
    fn test_from_slice_string() {
        if !check_mseal_support() {
            return;
        }
        let data = vec!["foo".to_string(), "bar".to_string(), "baz".to_string()];
        let sealed = SealBoxSlice::from_slice(&data)
            .expect("from_slice failed")
            .seal(None)
            .expect("seal failed");
        assert_eq!(&*sealed, &data[..]);
    }

    #[test]
    fn test_slice_independence() {
        if !check_mseal_support() {
            return;
        }
        let mut data = vec!["hello".to_string(), "world".to_string()];
        let sealed = SealBoxSlice::from_slice(&data).unwrap().seal(None).unwrap();
        data[0].clear();
        assert_eq!(&*sealed, &["hello".to_string(), "world".to_string()][..]);
    }

    #[test]
    fn test_multi_page_string() {
        if !check_mseal_support() {
            return;
        }
        let page = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize;
        // pick enough entries so that total data > 2 pages
        let len = (page * 2 / size_of::<String>()) + 5;
        let data = vec!["x".repeat(50); len];
        let sealed = SealBoxSlice::from_slice(&data).unwrap().seal(None).unwrap();
        assert_eq!(sealed.len(), len);
        for s in sealed.iter() {
            assert_eq!(s, &"x".repeat(50));
        }
        let (_ptr, map_len) = sealed.mapping();
        assert_eq!(map_len.get() % page, 0);
        assert!(map_len.get() >= len * size_of::<String>());
    }

    #[test]
    fn test_empty_slice() {
        if !check_mseal_support() {
            return;
        }
        let data: Vec<String> = Vec::new();
        let sealed = SealBoxSlice::from_slice(&data).unwrap().seal(None).unwrap();
        assert!(sealed.is_empty());
        assert_eq!(sealed.len(), 0);
    }

    #[test]
    fn test_zero_sized_type_error() {
        if !check_mseal_support() {
            return;
        }
        // zero-sized, non-empty slice must error.
        let arr = [(), (), ()];
        assert!(SealBoxSlice::from_slice(&arr).is_err());
    }

    #[test]
    fn test_hashmap_default_and_new() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableHashMap<i32, i32> = SealableHashMap::default();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);

        let mut base = HashMap::new();
        base.insert(1, 10);
        let m2 = SealableHashMap::new(base.clone());
        assert!(!m2.is_empty());
        assert_eq!(m2.len(), 1);
        assert_eq!(m2.get(&1), Some(&10));
    }

    #[test]
    fn test_hashmap_unsealed_insert_get_remove() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SealableHashMap::with_capacity(4);
        assert!(m.is_empty());

        assert_eq!(m.insert(1, 100).unwrap(), None);
        assert_eq!(m.insert(1, 200).unwrap(), Some(100));
        assert_eq!(m.get(&1), Some(&200));
        assert!(m.contains_key(&1));

        assert_eq!(m.remove(&1), Some(200));
        assert!(!m.contains_key(&1));
        assert_eq!(m.remove(&1), None);
    }

    #[test]
    fn test_hashmap_unsealed_clear_reserve_capacity() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SealableHashMap::new(HashMap::from([(2, 20), (3, 30)]));
        let old_cap = m.capacity();
        m.reserve(10).unwrap();
        assert!(m.capacity() >= old_cap);

        m.clear();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);
    }

    #[test]
    fn test_hashmap_seal_empty_and_sealed_properties() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableHashMap<&str, i32> = SealableHashMap::default();
        let sealed = m.seal(None).unwrap();

        assert!(sealed.is_empty());
        assert_eq!(sealed.len(), 0);
        assert!(!sealed.contains_key(&"x"));
        assert_eq!(sealed.get(&"x"), None);
        assert_eq!(sealed.capacity(), 0);
    }

    #[test]
    fn test_hashmap_seal_and_readonly_behavior() {
        if !check_mseal_support() {
            return;
        }
        let base = HashMap::from([("a", 1), ("b", 2), ("c", 3)]);
        let m = SealableHashMap::new(base);
        let mut sealed = m.seal(None).unwrap();

        assert_eq!(sealed.len(), 3);
        assert!(sealed.contains_key(&"b"));
        assert_eq!(sealed.get(&"b"), Some(&2));

        // Mutating operations should fail or be no-op
        assert_eq!(sealed.insert("d", 4), Err(Errno::EPERM));
        assert_eq!(sealed.remove(&"a"), None);
        assert_eq!(sealed.reserve(5), Err(Errno::EPERM));

        sealed.clear(); // clear on sealed should do nothing
        assert_eq!(sealed.len(), 3);
    }

    #[test]
    fn test_hashmap_iter_unsealed_and_sealed() {
        if !check_mseal_support() {
            return;
        }
        let entries = vec![(10, "x"), (5, "y"), (20, "z")];
        let mut map = HashMap::new();
        for &(k, v) in &entries {
            map.insert(k, v);
        }

        let unsealed = SealableHashMap::new(map.clone());
        let sealed = SealableHashMap::new(map).seal(None).unwrap();

        let mut u: Vec<_> = unsealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        let mut s: Vec<_> = sealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        u.sort_unstable_by_key(|&(k, _)| k);
        s.sort_unstable_by_key(|&(k, _)| k);

        let mut expected = entries.clone();
        expected.sort_unstable_by_key(|&(k, _)| k);

        assert_eq!(u, expected);
        assert_eq!(s, expected);
    }

    #[test]
    fn test_hashmap_partial_eq_and_eq() {
        if !check_mseal_support() {
            return;
        }
        let mut h1 = HashMap::new();
        h1.insert("k1", 100);
        h1.insert("k2", 200);

        let m1 = SealableHashMap::new(h1.clone());
        let m2 = SealableHashMap::new(h1.clone());
        assert_eq!(m1, m2);

        let sealed1 = SealableHashMap::new(h1.clone()).seal(None).unwrap();
        assert_eq!(m2, sealed1);

        let mut h3 = h1.clone();
        h3.insert("k3", 300);
        let m3 = SealableHashMap::new(h3);
        assert_ne!(m3, sealed1);
    }

    #[test]
    fn test_sydhashmap_default_and_new() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableSydHashMap<i32, i32> = SealableSydHashMap::default();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);

        let mut base = SydHashMap::new();
        base.insert(1, 10);
        let m2 = SealableSydHashMap::new(base.clone());
        assert!(!m2.is_empty());
        assert_eq!(m2.len(), 1);
        assert_eq!(m2.get(&1), Some(&10));
    }

    #[test]
    fn test_sydhashmap_unsealed_insert_get_remove() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SealableSydHashMap::with_capacity(4);
        assert!(m.is_empty());

        assert_eq!(m.insert(1, 100).unwrap(), None);
        assert_eq!(m.insert(1, 200).unwrap(), Some(100));
        assert_eq!(m.get(&1), Some(&200));
        assert!(m.contains_key(&1));

        assert_eq!(m.remove(&1), Some(200));
        assert!(!m.contains_key(&1));
        assert_eq!(m.remove(&1), None);
    }

    #[test]
    fn test_sydhashmap_unsealed_clear_reserve_capacity() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SydHashMap::new();
        m.extend([(2, 20), (3, 30)]);
        let mut m = SealableSydHashMap::from(m);
        let old_cap = m.capacity();
        m.reserve(10).unwrap();
        assert!(m.capacity() >= old_cap);

        m.clear();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);
    }

    #[test]
    fn test_sydhashmap_seal_empty_and_sealed_properties() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableSydHashMap<&str, i32> = SealableSydHashMap::default();
        let sealed = m.seal(None).unwrap();

        assert!(sealed.is_empty());
        assert_eq!(sealed.len(), 0);
        assert!(!sealed.contains_key(&"x"));
        assert_eq!(sealed.get(&"x"), None);
        assert_eq!(sealed.capacity(), 0);
    }

    #[test]
    fn test_sydhashmap_seal_and_readonly_behavior() {
        let mut base = SydHashMap::new();
        base.extend([("a", 1), ("b", 2), ("c", 3)]);
        let m = SealableSydHashMap::new(base);
        let mut sealed = m.seal(None).unwrap();

        assert_eq!(sealed.len(), 3);
        assert!(sealed.contains_key(&"b"));
        assert_eq!(sealed.get(&"b"), Some(&2));

        // Mutating operations should fail or be no-op
        assert_eq!(sealed.insert("d", 4), Err(Errno::EPERM));
        assert_eq!(sealed.remove(&"a"), None);
        assert_eq!(sealed.reserve(5), Err(Errno::EPERM));

        sealed.clear(); // clear on sealed should do nothing
        assert_eq!(sealed.len(), 3);
    }

    #[test]
    fn test_sydhashmap_iter_unsealed_and_sealed() {
        if !check_mseal_support() {
            return;
        }
        let entries = vec![(10, "x"), (5, "y"), (20, "z")];
        let mut map = SydHashMap::new();
        for &(k, v) in &entries {
            map.insert(k, v);
        }

        let unsealed = SealableSydHashMap::new(map.clone());
        let sealed = SealableSydHashMap::new(map).seal(None).unwrap();

        let mut u: Vec<_> = unsealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        let mut s: Vec<_> = sealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        u.sort_unstable_by_key(|&(k, _)| k);
        s.sort_unstable_by_key(|&(k, _)| k);

        let mut expected = entries.clone();
        expected.sort_unstable_by_key(|&(k, _)| k);

        assert_eq!(u, expected);
        assert_eq!(s, expected);
    }

    #[test]
    fn test_sydhashmap_partial_eq_and_eq() {
        if !check_mseal_support() {
            return;
        }
        let mut h1 = SydHashMap::new();
        h1.insert("k1", 100);
        h1.insert("k2", 200);

        let m1 = SealableSydHashMap::new(h1.clone());
        let m2 = SealableSydHashMap::new(h1.clone());
        assert_eq!(m1, m2);

        let sealed1 = SealableSydHashMap::new(h1.clone()).seal(None).unwrap();
        assert_eq!(m2, sealed1);

        let mut h3 = h1.clone();
        h3.insert("k3", 300);
        let m3 = SealableSydHashMap::new(h3);
        assert_ne!(m3, sealed1);
    }

    #[test]
    fn test_sydindexmap_default_and_new() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableSydIndexMap<i32, i32> = SealableSydIndexMap::default();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);

        let mut base = SydIndexMap::default();
        base.insert(1, 10);
        let m2 = SealableSydIndexMap::new(base.clone());
        assert!(!m2.is_empty());
        assert_eq!(m2.len(), 1);
        assert_eq!(m2.get(&1), Some(&10));
    }

    #[test]
    fn test_sydindexmap_unsealed_insert_get_remove() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SealableSydIndexMap::with_capacity(4);
        assert!(m.is_empty());

        assert_eq!(m.insert(1, 100).unwrap(), None);
        assert_eq!(m.insert(1, 200).unwrap(), Some(100));
        assert_eq!(m.get(&1), Some(&200));
        assert!(m.contains_key(&1));

        assert_eq!(m.remove(&1), Some(200));
        assert!(!m.contains_key(&1));
        assert_eq!(m.remove(&1), None);
    }

    #[test]
    fn test_sydindexmap_unsealed_clear_reserve_capacity() {
        if !check_mseal_support() {
            return;
        }
        let mut m = SydIndexMap::default();
        m.extend([(2, 20), (3, 30)]);
        let mut m = SealableSydIndexMap::from(m);
        let old_cap = m.capacity();
        m.reserve(10).unwrap();
        assert!(m.capacity() >= old_cap);

        m.clear();
        assert!(m.is_empty());
        assert_eq!(m.len(), 0);
    }

    #[test]
    fn test_sydindexmap_seal_empty_and_sealed_properties() {
        if !check_mseal_support() {
            return;
        }
        let m: SealableSydIndexMap<&str, i32> = SealableSydIndexMap::default();
        let sealed = m.seal(None).unwrap();

        assert!(sealed.is_empty());
        assert_eq!(sealed.len(), 0);
        assert!(!sealed.contains_key(&"x"));
        assert_eq!(sealed.get(&"x"), None);
        assert_eq!(sealed.capacity(), 0);
    }

    #[test]
    fn test_sydindexmap_seal_and_readonly_behavior() {
        let mut base = SydIndexMap::default();
        base.extend([("a", 1), ("b", 2), ("c", 3)]);
        let m = SealableSydIndexMap::new(base);
        let mut sealed = m.seal(None).unwrap();

        assert_eq!(sealed.len(), 3);
        assert!(sealed.contains_key(&"b"));
        assert_eq!(sealed.get(&"b"), Some(&2));

        // Mutating operations should fail or be no-op
        assert_eq!(sealed.insert("d", 4), Err(Errno::EPERM));
        assert_eq!(sealed.remove(&"a"), None);
        assert_eq!(sealed.reserve(5), Err(Errno::EPERM));

        sealed.clear(); // clear on sealed should do nothing
        assert_eq!(sealed.len(), 3);
    }

    #[test]
    fn test_sydindexmap_iter_unsealed_and_sealed() {
        if !check_mseal_support() {
            return;
        }
        let entries = vec![(10, "x"), (5, "y"), (20, "z")];
        let mut map = SydIndexMap::default();
        for &(k, v) in &entries {
            map.insert(k, v);
        }

        let unsealed = SealableSydIndexMap::new(map.clone());
        let sealed = SealableSydIndexMap::new(map).seal(None).unwrap();

        let mut u: Vec<_> = unsealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        let mut s: Vec<_> = sealed.into_iter().map(|(&k, &v)| (k, v)).collect();
        u.sort_unstable_by_key(|&(k, _)| k);
        s.sort_unstable_by_key(|&(k, _)| k);

        let mut expected = entries.clone();
        expected.sort_unstable_by_key(|&(k, _)| k);

        assert_eq!(u, expected);
        assert_eq!(s, expected);
    }

    #[test]
    fn test_sydindexmap_partial_eq_and_eq() {
        if !check_mseal_support() {
            return;
        }
        let mut h1 = SydIndexMap::default();
        h1.insert("k1", 100);
        h1.insert("k2", 200);

        let m1 = SealableSydIndexMap::new(h1.clone());
        let m2 = SealableSydIndexMap::new(h1.clone());
        assert_eq!(m1, m2);

        let sealed1 = SealableSydIndexMap::new(h1.clone()).seal(None).unwrap();
        assert_eq!(m2, sealed1);

        let mut h3 = h1.clone();
        h3.insert("k3", 300);
        let m3 = SealableSydIndexMap::new(h3);
        assert_ne!(m3, sealed1);
    }

    #[test]
    fn test_hashmap_retain_removes_odds() {
        let mut shm = SealableHashMap::new(HashMap::from([(1, 10), (2, 20), (3, 30), (4, 40)]));
        // Keep only even keys
        shm.retain(|k, _v| *k % 2 == 0).unwrap();
        let collected: Vec<_> = shm.into_iter().map(|(&k, &v)| (k, v)).collect();
        assert_eq!(collected.len(), 2);
        assert!(collected.contains(&(2, 20)));
        assert!(collected.contains(&(4, 40)));
    }

    #[test]
    fn test_hashmap_retain_mutates_values() {
        let mut shm = SealableHashMap::new(HashMap::from([(1, 1), (2, 2), (3, 3)]));
        // Double the values for keys >= 2 and keep those entries
        shm.retain(|k, v| {
            *v *= 2;
            *k >= 2
        })
        .unwrap();
        // After retain, only keys 2 and 3 should remain, with doubled values
        assert_eq!(shm.len(), 2);
        assert_eq!(shm.get(&2), Some(&4));
        assert_eq!(shm.get(&3), Some(&6));
    }

    #[test]
    fn test_hashmap_retain_all() {
        let mut shm = SealableHashMap::new(HashMap::from([('a', 100), ('b', 200)]));
        // Retain everything
        shm.retain(|_, _| true).unwrap();
        assert_eq!(shm.len(), 2);
        assert_eq!(shm.get(&'a'), Some(&100));
        assert_eq!(shm.get(&'b'), Some(&200));
    }

    #[test]
    fn test_hashmap_retain_none() {
        let mut shm = SealableHashMap::new(HashMap::from([("x", 9), ("y", 8)]));
        // Retain nothing
        shm.retain(|_, _| false).unwrap();
        assert!(shm.is_empty());
    }

    #[test]
    fn test_hashmap_retain_on_empty_map() {
        let mut shm: SealableHashMap<i32, i32> = SealableHashMap::default();
        // Should be a no-op but still Ok
        shm.retain(|_, _| unreachable!()).unwrap();
        assert!(shm.is_empty());
    }

    #[test]
    fn test_hashmap_sealed_retain_error() {
        let shm = SealableHashMap::new(HashMap::from([(1, 1)]));
        let mut sealed = shm.seal(None).unwrap();
        // Attempting to retain on sealed map should return EPERM
        let err = sealed.retain(|_, _| true).unwrap_err();
        assert_eq!(err, Errno::EPERM);
    }

    // Tests for SealableSydHashMap::retain
    #[test]
    fn test_sydhashmap_retain_removes_small_values() {
        let mut sahm = SydHashMap::new();
        sahm.extend([(10, 1), (20, 2), (30, 3), (40, 4)]);
        let mut sahm = SealableSydHashMap::new(sahm);
        // Keep only entries with value > 2
        sahm.retain(|_, v| *v > 2).unwrap();
        let mut collected: Vec<_> = sahm.into_iter().map(|(&k, &v)| (k, v)).collect();
        collected.sort_unstable();
        assert_eq!(collected, vec![(30, 3), (40, 4)]);
    }

    #[ignore]
    #[test]
    fn test_sydhashmap_retain_mutates_keys_and_values() {
        // Note: mutation applies only to values
        let mut sahm = SydHashMap::new();
        sahm.extend([
            ("one".to_string(), 1),
            ("two".to_string(), 2),
            ("three".to_string(), 3),
        ]);
        let mut sahm = SealableSydHashMap::new(sahm);
        // For entries with even length keys, add 10 to the value and keep
        sahm.retain(|k, v| {
            if k.len() % 2 == 0 {
                *v += 10;
                true
            } else {
                false
            }
        })
        .unwrap();
        assert_eq!(sahm.len(), 1);
        assert_eq!(sahm.get("two"), Some(&12));
    }

    #[test]
    fn test_sydhashmap_retain_all_and_none() {
        let mut sahm = SydHashMap::new();
        sahm.extend([(1, 100)]);
        let mut sahm = SealableSydHashMap::new(sahm);
        sahm.retain(|_, _| true).unwrap();
        assert_eq!(sahm.len(), 1);
        sahm.retain(|_, _| false).unwrap();
        assert!(sahm.is_empty());
    }

    #[test]
    fn test_sydhashmap_retain_on_empty_map() {
        let mut sahm: SealableSydHashMap<u8, u8> = SealableSydHashMap::default();
        sahm.retain(|_, _| unreachable!()).unwrap();
        assert!(sahm.is_empty());
    }

    #[test]
    fn test_sydhashmap_sealed_retain_error() {
        let mut sahm = SydHashMap::new();
        sahm.extend([(0, 0)]);
        let sahm = SealableSydHashMap::new(sahm);
        let mut sealed = sahm.seal(None).unwrap();
        let err = sealed.retain(|_, _| true).unwrap_err();
        assert_eq!(err, Errno::EPERM);
    }

    // Tests for SealableSydIndexMap::retain
    #[test]
    fn test_sydindexmap_retain_removes_small_values() {
        let mut sahm = SydIndexMap::default();
        sahm.extend([(10, 1), (20, 2), (30, 3), (40, 4)]);
        let mut sahm = SealableSydIndexMap::new(sahm);
        // Keep only entries with value > 2
        sahm.retain(|_, v| *v > 2).unwrap();
        let mut collected: Vec<_> = sahm.into_iter().map(|(&k, &v)| (k, v)).collect();
        collected.sort_unstable();
        assert_eq!(collected, vec![(30, 3), (40, 4)]);
    }

    #[ignore]
    #[test]
    fn test_sydindexmap_retain_mutates_keys_and_values() {
        // Note: mutation applies only to values
        let mut sahm = SydIndexMap::default();
        sahm.extend([
            ("one".to_string(), 1),
            ("two".to_string(), 2),
            ("three".to_string(), 3),
        ]);
        let mut sahm = SealableSydIndexMap::new(sahm);
        // For entries with even length keys, add 10 to the value and keep
        sahm.retain(|k, v| {
            if k.len() % 2 == 0 {
                *v += 10;
                true
            } else {
                false
            }
        })
        .unwrap();
        assert_eq!(sahm.len(), 1);
        assert_eq!(sahm.get("two"), Some(&12));
    }

    #[test]
    fn test_sydindexmap_retain_all_and_none() {
        let mut sahm = SydIndexMap::default();
        sahm.extend([(1, 100)]);
        let mut sahm = SealableSydIndexMap::new(sahm);
        sahm.retain(|_, _| true).unwrap();
        assert_eq!(sahm.len(), 1);
        sahm.retain(|_, _| false).unwrap();
        assert!(sahm.is_empty());
    }

    #[test]
    fn test_sydindexmap_retain_on_empty_map() {
        let mut sahm: SealableSydIndexMap<u8, u8> = SealableSydIndexMap::default();
        sahm.retain(|_, _| unreachable!()).unwrap();
        assert!(sahm.is_empty());
    }

    #[test]
    fn test_sydindexmap_sealed_retain_error() {
        let mut sahm = SydIndexMap::default();
        sahm.extend([(0, 0)]);
        let sahm = SealableSydIndexMap::new(sahm);
        let mut sealed = sahm.seal(None).unwrap();
        let err = sealed.retain(|_, _| true).unwrap_err();
        assert_eq!(err, Errno::EPERM);
    }

    //
    // MADV_GUARD_{INSTALL,REMOVE} tests.
    //

    // Helper get system page size.
    fn page_size() -> NonZeroUsize {
        sysconf(SysconfVar::PAGE_SIZE)
            .unwrap()
            .map(usize::try_from)
            .unwrap()
            .map(NonZeroUsize::try_from)
            .unwrap()
            .unwrap()
    }

    // Helper: Map `PAGE_SIZE` bytes of anonymous read-write memory.
    fn map_region(len: NonZeroUsize) -> NonNull<c_void> {
        let result = unsafe {
            mmap_anonymous(
                None,
                len,
                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
                MapFlags::MAP_PRIVATE,
            )
        };

        assert!(result.is_ok(), "mmap_anonymous:{result:?}");
        result.unwrap()
    }

    // Helper: Unmap a region.
    fn unmap_region(ptr: NonNull<c_void>, len: NonZeroUsize) {
        let result = unsafe { munmap(ptr, len.get()) };
        assert!(result.is_ok(), "munmap:{result:?}");
    }

    #[test]
    fn test_madv_guard_install_idempotent() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let region = map_region(page);

        // Attempt first install; skip if guard not supported.
        match madvise_guard_install(region, page) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(region, page);
                return;
            }
            Err(errno) => panic!("unexpected error on install: {errno}"),
        }

        // Second install should still succeed.
        madvise_guard_install(region, page).expect("second install failed");

        // Clean up.
        let _ = madvise_guard_remove(region, page);
        unmap_region(region, page);
    }

    #[test]
    fn test_madv_guard_remove_idempotent() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let region = map_region(page);

        // Must install first; skip if unsupported.
        match madvise_guard_install(region, page) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(region, page);
                return;
            }
            Err(errno) => panic!("unexpected error on install: {errno}"),
        }

        // First remove.
        madvise_guard_remove(region, page).expect("first remove failed");

        // Second remove is a no-op.
        madvise_guard_remove(region, page).expect("second remove failed");

        unmap_region(region, page);
    }

    #[test]
    fn test_madv_guard_remove_without_install() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let region = map_region(page);

        match madvise_guard_remove(region, page) {
            Ok(()) => {}             // treated as no-op.
            Err(Errno::EINVAL) => {} // OK
            Err(errno) => panic!("unexpected error: {errno}"),
        }

        unmap_region(region, page);
    }

    #[test]
    fn test_madv_guard_install_partial_fault() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let page2 = NonZeroUsize::new(page.get() * 2).unwrap();
        let region = map_region(page2);

        // Install guard only on first page.
        match madvise_guard_install(region, page) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(region, page2);
                return;
            }
            Err(errno) => panic!("unexpected error: {errno}"),
        }

        match unsafe { fork() } {
            Ok(ForkResult::Parent { child }) => match waitpid(child, None).unwrap() {
                WaitStatus::Signaled(_, Signal::SIGSEGV, _) => {}
                other => panic!("expected SIGSEGV, got {other:?}"),
            },
            Ok(ForkResult::Child) => {
                unsafe { (region.as_ptr() as *mut u8).write_volatile(0) };
                std::process::exit(1);
            }
            Err(errno) => panic!("fork failed: {errno}"),
        }

        let _ = madvise_guard_remove(region, page);
        unmap_region(region, page2);
    }

    #[test]
    fn test_madv_guard_install_partial_no_fault() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let page2 = NonZeroUsize::new(page.get() * 2).unwrap();
        let region = map_region(page2);

        match madvise_guard_install(region, page) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(region, page2);
                return;
            }
            Err(errno) => panic!("unexpected error: {errno}"),
        }

        // Writing to second page should succeed.
        match unsafe { fork() } {
            Ok(ForkResult::Parent { child }) => match waitpid(child, None).unwrap() {
                WaitStatus::Exited(_, 0) => {}
                other => panic!("expected exit(0), got {other:?}"),
            },
            Ok(ForkResult::Child) => {
                unsafe { (region.as_ptr().add(page.get()) as *mut u8).write_volatile(42) };
                std::process::exit(0);
            }
            Err(errno) => panic!("fork failed: {errno}"),
        }

        let _ = madvise_guard_remove(region, page);
        unmap_region(region, page2);
    }

    #[test]
    fn test_madv_guard_remove_partial_no_fault() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let page2 = NonZeroUsize::new(page.get() * 2).unwrap();
        let region = map_region(page2);

        // Install guard on both pages, then remove on second page.
        match madvise_guard_install(region, page2) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(region, page2);
                return;
            }
            Err(errno) => panic!("unexpected error: {errno}"),
        }
        let _ = madvise_guard_remove(
            unsafe { NonNull::new_unchecked(region.as_ptr().add(page.get())) },
            page,
        );

        // Writing to second page must succeed.
        match unsafe { fork() } {
            Ok(ForkResult::Parent { child }) => match waitpid(child, None).unwrap() {
                WaitStatus::Exited(_, 0) => {}
                other => panic!("expected exit(0), got {other:?}"),
            },
            Ok(ForkResult::Child) => {
                unsafe { (region.as_ptr().add(page.get()) as *mut u8).write_volatile(99) };
                std::process::exit(0);
            }
            Err(errno) => panic!("fork failed: {errno}"),
        }

        let _ = madvise_guard_remove(region, page2);
        unmap_region(region, page2);
    }

    #[test]
    fn test_madv_guard_multiple_regions() {
        if !check_madvise_guard_support() {
            return;
        }

        let page = page_size();
        let r1 = map_region(page);
        let r2 = map_region(page);

        // Install guard on r1 only.
        match madvise_guard_install(r1, page) {
            Ok(()) => {}
            Err(Errno::EINVAL) => {
                unmap_region(r1, page);
                unmap_region(r2, page);
                return;
            }
            Err(errno) => panic!("unexpected error: {errno}"),
        }

        // r2 should remain writable.
        match unsafe { fork() } {
            Ok(ForkResult::Parent { child }) => match waitpid(child, None).unwrap() {
                WaitStatus::Exited(_, 0) => {}
                other => panic!("expected exit(0), got {other:?}"),
            },
            Ok(ForkResult::Child) => {
                unsafe { (r2.as_ptr() as *mut u8).write_volatile(7) };
                std::process::exit(0);
            }
            Err(errno) => panic!("fork failed: {errno}"),
        }

        // But r1 should fault.
        match unsafe { fork() } {
            Ok(ForkResult::Parent { child }) => match waitpid(child, None).unwrap() {
                WaitStatus::Signaled(_, Signal::SIGSEGV, _) => {}
                other => panic!("expected SIGSEGV, got {other:?}"),
            },
            Ok(ForkResult::Child) => {
                unsafe { (r1.as_ptr() as *mut u8).write_volatile(5) };
                std::process::exit(1);
            }
            Err(errno) => panic!("fork failed: {errno}"),
        }

        let _ = madvise_guard_remove(r1, page);
        unmap_region(r1, page);
        unmap_region(r2, page);
    }
}
