minkv 0.3.0

一个轻量级持久化键值存储,支持内存和文件后端,提供 CLI 和 TCP 服务器
Documentation
//! 一个简单的固定大小内存池,用于展示 `unsafe` 和原始指针操作。
//!
//! # 安全性
//!
//! 该模块使用了 `unsafe` 进行内存分配和原始指针读写,
//! 但内部所有操作都确保不会产生:
//! - 悬垂指针
//! - 缓冲区溢出(调用者需保证写入数据不超过 `BLOCK_SIZE`)
//! - 数据竞争(通过 `Send` + `Sync` 配合外部同步使用)
//!
//! 此模块仅为学习目的,实际项目中建议使用成熟的分配器或集合。

use std::alloc::{alloc, dealloc, Layout};
use std::ptr::NonNull;

const BLOCK_SIZE: usize = 128;
const BLOCK_ALIGN: usize = 8;

/// 一个简单的固定大小内存池。
///
/// 预先分配 `blocks` 个大小为 [`BLOCK_SIZE`] 的连续内存块,
/// 通过索引分配、释放和访问。
///
/// ```rust
/// use minkv::SimpleMemoryPool;
/// let mut pool = SimpleMemoryPool::new(2);
/// let idx = pool.alloc_block().unwrap();
/// unsafe {
///     pool.write_str(idx, "hello");
///     assert_eq!(pool.read_str(idx), Some("hello"));
/// }
/// pool.free_block(idx);
/// ```
pub struct SimpleMemoryPool {
    memory: NonNull<u8>,
    total_blocks: usize,
    free_list: Vec<usize>,
    block_usage: Vec<bool>,
}

impl SimpleMemoryPool {
    /// 创建包含 `blocks` 个块的内存池,每个块大小为 128 字节。
    pub fn new(blocks: usize) -> Self {
        let layout = Layout::from_size_align(blocks * BLOCK_SIZE, BLOCK_ALIGN)
            .expect("无法创建 layout");
        let memory = unsafe {
            let ptr = alloc(layout);
            NonNull::new(ptr).expect("内存分配失败")
        };

        let mut free_list = Vec::with_capacity(blocks);
        for i in 0..blocks {
            free_list.push(i);
        }

        SimpleMemoryPool {
            memory,
            total_blocks: blocks,
            free_list,
            block_usage: vec![false; blocks],
        }
    }

    /// 分配一个空闲块,返回块索引。
    pub fn alloc_block(&mut self) -> Option<usize> {
        let idx = self.free_list.pop()?;
        self.block_usage[idx] = true;
        Some(idx)
    }

    /// 释放指定块,使其可被再次分配。
    pub fn free_block(&mut self, idx: usize) {
        if idx < self.total_blocks && self.block_usage[idx] {
            self.block_usage[idx] = false;
            self.free_list.push(idx);
        }
    }

    /// 获取指定块的原始指针(需确保块已分配)。
    pub fn block_ptr(&self, idx: usize) -> Option<*mut u8> {
        if idx < self.total_blocks && self.block_usage[idx] {
            let offset = idx * BLOCK_SIZE;
            unsafe { Some(self.memory.as_ptr().add(offset)) }
        } else {
            None
        }
    }

    /// 将字符串写入块(`unsafe`,调用者需保证字符串长度不超过 `BLOCK_SIZE - 1`)。
    ///
    /// 末尾会写入一个 `0` 作为终止符。
    pub unsafe fn write_str(&self, idx: usize, s: &str) {
        if let Some(ptr) = self.block_ptr(idx) {
            let bytes = s.as_bytes();
            std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
            *ptr.add(bytes.len()) = 0;
        }
    }

    /// 从块中读取字符串(`unsafe`,假设数据以 `0` 结尾)。
    ///
    /// 返回的 `&str` 有效期与内存池本身一致。
    pub unsafe fn read_str(&self, idx: usize) -> Option<&str> {
        self.block_ptr(idx).map(|ptr| {
            let len = (0..BLOCK_SIZE)
                .find(|&i| *ptr.add(i) == 0)
                .unwrap_or(BLOCK_SIZE);
            let slice = std::slice::from_raw_parts(ptr, len);
            std::str::from_utf8_unchecked(slice)
        })
    }
}

impl Drop for SimpleMemoryPool {
    fn drop(&mut self) {
        let layout = Layout::from_size_align(self.total_blocks * BLOCK_SIZE, BLOCK_ALIGN)
            .unwrap();
        unsafe {
            dealloc(self.memory.as_ptr(), layout);
        }
    }
}

// 内存池本身不包含内部可变性,但手动实现 Send + Sync 使它可以跨线程传递。
// 实际使用时,外部必须保证同步。
unsafe impl Send for SimpleMemoryPool {}
unsafe impl Sync for SimpleMemoryPool {}