melinoe 0.10.0

Zero-sized, branded, multi-token phantom capabilities for compile-time data-access and thread-synchronization proofs (a generalized evolution of GhostCell) for the Mnemosyne memory ecosystem.
Documentation
use core::num::NonZeroUsize;

/// Shard sizing policy for partitioned scoped-thread execution.
///
/// The plan controls only how a region is tiled into non-empty
/// [`WriterShard`](crate::region::WriterShard)s. It does not introduce locks,
/// atomics, queues, or worker pools; each shard is still moved into one worker
/// and joined before the call returns.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PartitionPlan {
    /// Split into at most this many non-empty shards.
    Parts(NonZeroUsize),
    /// Split into at most hardware-parallel shard count.
    ///
    /// This uses `std::thread::available_parallelism()` and falls back to one
    /// shard when the platform cannot report it. Consumers with a richer
    /// topology provider can pass its validated processor count to
    /// [`PartitionPlan::parts`] without adding a dependency to Melinoe.
    AvailableParallelism,
    /// Split into non-empty shards containing at most this many cells.
    ChunkSize(NonZeroUsize),
}

impl PartitionPlan {
    /// Create a fixed-part plan, clamping zero to one.
    #[inline]
    #[must_use]
    pub const fn parts(parts: usize) -> Self {
        Self::Parts(nonzero_or_one(parts))
    }

    /// Create a plan based on the process's reported hardware parallelism.
    #[inline]
    #[must_use]
    pub const fn available_parallelism() -> Self {
        Self::AvailableParallelism
    }

    /// Create a fixed-chunk-size plan, clamping zero to one.
    #[inline]
    #[must_use]
    pub const fn chunk_size(chunk_size: usize) -> Self {
        Self::ChunkSize(nonzero_or_one(chunk_size))
    }

    /// Resolve the plan to a concrete per-shard chunk size for a region of
    /// length `len`.
    ///
    /// Empty regions resolve to `1`, so callers can pass the result directly to
    /// slice `chunks`/Melinoe shard chunking without risking a zero chunk size.
    #[inline]
    #[must_use]
    pub fn chunk_len_for(self, len: usize) -> usize {
        match self {
            Self::Parts(parts) => chunk_for_parts(len, parts.get()),
            Self::AvailableParallelism => {
                let parts = std::thread::available_parallelism().map_or(1, NonZeroUsize::get);
                chunk_for_parts(len, parts)
            }
            Self::ChunkSize(chunk_size) => chunk_size.get(),
        }
    }

    /// Resolve the plan for the partition driver.
    ///
    /// The shard *count* is intentionally not computed here: it is derived once,
    /// at the single source of truth, from the [`ShardChunks`](crate::region)
    /// iterator's exact size when the driver reserves worker capacity.
    #[inline]
    pub(super) fn resolve(self, len: usize) -> usize {
        self.chunk_len_for(len)
    }
}

#[inline]
const fn nonzero_or_one(value: usize) -> NonZeroUsize {
    if let Some(nz) = NonZeroUsize::new(value) {
        nz
    } else {
        // SAFETY: 1 is non-zero
        unsafe { NonZeroUsize::new_unchecked(1) }
    }
}

#[inline]
fn chunk_for_parts(len: usize, parts: usize) -> usize {
    // ceil(len / parts), clamped to `>= 1` so callers never receive a zero chunk
    // size. `partition_count` is the crate's single overflow-safe ceiling, so
    // this agrees with `ParChunks::len`/`ShardChunks::size_hint` by construction.
    crate::region::partition_count(len, parts).max(1)
}