sz-rust-core 1.2.0

SZ-Rust 核心库:HTTP 服务器、路由、控制器、中间件,对标 ThinkPHP 8
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
//! 内存池模块 — P3 性能优化
//!
//! 提供区域分配器(bump allocator)用于热点路径零堆分配。
//!
//! ## 模块
//!
//! - [`MemPool`] trait:统一内存池接口
//! - [`StackPool`]:栈分配后端(固定容量 `[u8; CAP]`,无依赖)
//! - [`create_pool`]:工厂函数
//!
//! ## 用法
//!
//! ```rust,ignore
//! use sz_rust_core::mem_pool::{MemPool, StackPool};
//!
//! let pool = StackPool::<1024>::new();
//! let s = unsafe { pool.alloc_str("hello") };
//! assert_eq!(s, "hello");
//! ```

#![allow(unsafe_code)]

use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};

// ============================================================================
// MemPool trait
// ============================================================================

/// 内存池 trait — 区域分配器统一接口
///
/// 用于热点路径零堆分配:在固定容量缓冲区内分配字符串/字节切片,
/// 请求结束后 `reset()` 整体回收。
///
/// ## Safety(unsafe API 契约,2026-08-16 收紧)
///
/// `alloc_str` / `alloc_bytes` 返回的引用在 `reset()` **之前**有效;
/// `reset()` 后所有先前返回的引用**立即失效**(后续 alloc 会覆盖同一区域)。
/// 调用方必须保证:在使用返回引用期间不调用 `reset()`,否则构成 use-after-free。
///
/// 实现者需确保:
/// - `alloc_str` / `alloc_bytes` 返回的引用在 `reset()` 之前有效
/// - `reset()` 后所有先前返回的引用失效
/// - 线程安全:`&self` 方法可被多线程并发调用,并发 `reset` 与 `alloc` 需要调用方同步
///
/// # 为什么是 unsafe fn(设计决策)
///
/// 本 trait 用 `&self` + 生命周期延长实现零拷贝分配,Rust 借用检查器无法
/// 表达"引用在 reset 前有效"这一不变量(`reset` 是共享借用 `&self`)。
/// 若保持 safe fn,Safe Rust 调用方可在不知情时触发 UB(reset 后使用引用)。
/// 因此 `alloc_str`/`alloc_bytes` 为 **unsafe fn**:调用方必须显式承担契约。
pub trait MemPool: Send + Sync {
    /// 在池内分配字符串切片(零拷贝,返回池内引用)
    ///
    /// 如果池容量不足,回退返回输入切片本身(零分配)。
    ///
    /// ## Safety
    ///
    /// 返回的引用在 `reset()` 之前有效。调用方需保证在使用返回引用期间
    /// 不调用 `reset()`(包括其他线程的 `reset()`)。
    unsafe fn alloc_str<'a>(&self, s: &'a str) -> &'a str;

    /// 在池内分配字节切片(零拷贝,返回池内引用)
    ///
    /// 如果池容量不足,回退返回输入切片本身。
    ///
    /// ## Safety
    ///
    /// 同 [`MemPool::alloc_str`]:返回引用在 `reset()` 前有效,期间不得调用 `reset()`。
    unsafe fn alloc_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8];

    /// 重置池,回收所有内存(整体回收到起始位置)
    fn reset(&self);

    /// 已使用字节数
    fn used_bytes(&self) -> usize;
}

// ============================================================================
// StackPool — 栈分配后端
// ============================================================================

/// 栈分配内存池(固定容量,无依赖)
///
/// 使用 `[u8; CAP]` 数组作为后端,`pos` 跟踪分配位置。
/// 适用于请求级临时分配,请求结束后 `reset()` 回收。
///
/// ## 线程安全
///
/// 使用 `UnsafeCell` + `AtomicUsize` 实现内部可变性,
/// 通过 `AtomicUsize::fetch_add` 原子递增分配位置,
/// 多线程并发分配安全(但分配的引用在 `reset` 后失效)。
pub struct StackPool<const CAP: usize> {
    buffer: UnsafeCell<[u8; CAP]>,
    pos: AtomicUsize,
}

impl<const CAP: usize> StackPool<CAP> {
    /// 创建 StackPool
    pub const fn new() -> Self {
        Self {
            buffer: UnsafeCell::new([0u8; CAP]),
            pos: AtomicUsize::new(0),
        }
    }

    /// 池总容量
    pub const fn capacity() -> usize {
        CAP
    }

    /// 剩余可用字节数
    pub fn remaining(&self) -> usize {
        CAP - self.pos.load(Ordering::Acquire)
    }
}

// Safety: StackPool 使用 AtomicUsize 管理分配位置,
// UnsafeCell 的内容通过原子操作安全访问。
unsafe impl<const CAP: usize> Send for StackPool<CAP> {}
unsafe impl<const CAP: usize> Sync for StackPool<CAP> {}

impl<const CAP: usize> MemPool for StackPool<CAP> {
    unsafe fn alloc_str<'a>(&self, s: &'a str) -> &'a str {
        let bytes = s.as_bytes();
        let len = bytes.len();
        if len == 0 {
            return "";
        }

        let start = self.pos.fetch_add(len, Ordering::AcqRel);
        if start + len > CAP {
            self.pos.fetch_sub(len, Ordering::AcqRel);
            return s;
        }

        // Safety: start + len <= CAP,buffer 有效
        let buf = unsafe { &mut *self.buffer.get() };
        buf[start..start + len].copy_from_slice(bytes);

        // Safety: 从 buf 切片构造 &str,字节来自有效 UTF-8 字符串。
        // 生命周期延长为 'a:区域分配器语义保证引用在 reset 前有效
        // (调用方通过 unsafe 块显式承担该契约,见 trait Safety 文档)。
        unsafe { std::str::from_utf8_unchecked(&buf[start..start + len]) }
    }

    unsafe fn alloc_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] {
        let len = b.len();
        if len == 0 {
            return &[];
        }

        let start = self.pos.fetch_add(len, Ordering::AcqRel);
        if start + len > CAP {
            self.pos.fetch_sub(len, Ordering::AcqRel);
            return b;
        }

        let buf = unsafe { &mut *self.buffer.get() };
        buf[start..start + len].copy_from_slice(b);
        &buf[start..start + len]
    }

    fn reset(&self) {
        self.pos.store(0, Ordering::Release);
    }

    fn used_bytes(&self) -> usize {
        self.pos.load(Ordering::Acquire)
    }
}

impl<const CAP: usize> Default for StackPool<CAP> {
    fn default() -> Self {
        Self::new()
    }
}

impl<const CAP: usize> std::fmt::Debug for StackPool<CAP> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "StackPool<{CAP}> used={}/{}", self.used_bytes(), CAP)
    }
}

// ============================================================================
// BumpaloPool — bumpalo 后端(可选 feature)
// ============================================================================

#[cfg(feature = "bumpalo-pool")]
mod bumpalo_backend {
    use super::*;
    use bumpalo::Bump;
    use std::sync::Mutex;

    /// bumpalo 内存池(可选 feature `bumpalo-pool`)
    ///
    /// 包装 `bumpalo::Bump`,提供与 [`StackPool`] 相同的 [`MemPool`] 接口。
    /// 适用于不确定容量的场景。
    pub struct BumpaloPool {
        bump: Mutex<Bump>,
        used: AtomicUsize,
    }

    impl BumpaloPool {
        /// 创建 BumpaloPool
        pub fn new() -> Self {
            Self {
                bump: Mutex::new(Bump::new()),
                used: AtomicUsize::new(0),
            }
        }
    }

    impl Default for BumpaloPool {
        fn default() -> Self {
            Self::new()
        }
    }

    impl MemPool for BumpaloPool {
        unsafe fn alloc_str<'a>(&self, s: &'a str) -> &'a str {
            let bump = self.bump.lock().unwrap_or_else(|e| e.into_inner());
            let allocated = bump.alloc_str(s);
            self.used.fetch_add(s.len(), Ordering::Relaxed);
            // Safety: 分配的引用在 reset 前有效,bump 不会移动已分配内存
            // (调用方通过 unsafe 块显式承担契约,见 trait Safety 文档)。
            unsafe { std::mem::transmute::<&str, &'a str>(allocated) }
        }

        unsafe fn alloc_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] {
            let bump = self.bump.lock().unwrap_or_else(|e| e.into_inner());
            let allocated = bump.alloc_slice_copy(b);
            self.used.fetch_add(b.len(), Ordering::Relaxed);
            unsafe { std::mem::transmute::<&[u8], &'a [u8]>(allocated) }
        }

        fn reset(&self) {
            let mut bump = self.bump.lock().unwrap_or_else(|e| e.into_inner());
            bump.reset();
            self.used.store(0, Ordering::Release);
        }

        fn used_bytes(&self) -> usize {
            self.used.load(Ordering::Acquire)
        }
    }

    impl std::fmt::Debug for BumpaloPool {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "BumpaloPool used={}", self.used_bytes())
        }
    }
}

#[cfg(feature = "bumpalo-pool")]
pub use bumpalo_backend::BumpaloPool;

// ============================================================================
// 配置与工厂函数
// ============================================================================

/// 内存池类型
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemPoolType {
    /// bumpalo 后端(需 `bumpalo-pool` feature)
    Bumpalo,
    /// 栈分配后端
    Stack,
    /// 不使用内存池
    None,
}

/// 内存池配置
#[derive(Debug, Clone)]
pub struct MemPoolConfig {
    /// 池类型
    pub pool_type: MemPoolType,
    /// 容量(字节,仅 Stack 类型有效)
    pub capacity: usize,
}

impl Default for MemPoolConfig {
    fn default() -> Self {
        Self {
            pool_type: MemPoolType::Stack,
            capacity: 4096,
        }
    }
}

/// 工厂函数:根据配置创建内存池
pub fn create_pool(config: &MemPoolConfig) -> Option<Box<dyn MemPool>> {
    match config.pool_type {
        MemPoolType::Stack => match config.capacity {
            1024 => Some(Box::new(StackPool::<1024>::new())),
            2048 => Some(Box::new(StackPool::<2048>::new())),
            4096 => Some(Box::new(StackPool::<4096>::new())),
            8192 => Some(Box::new(StackPool::<8192>::new())),
            16384 => Some(Box::new(StackPool::<16384>::new())),
            32768 => Some(Box::new(StackPool::<32768>::new())),
            65536 => Some(Box::new(StackPool::<65536>::new())),
            _ => Some(Box::new(StackPool::<4096>::new())),
        },
        #[cfg(feature = "bumpalo-pool")]
        MemPoolType::Bumpalo => Some(Box::new(BumpaloPool::new())),
        #[cfg(not(feature = "bumpalo-pool"))]
        MemPoolType::Bumpalo => None,
        MemPoolType::None => None,
    }
}

// ============================================================================
// 单元测试
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_stack_pool_alloc_str() {
        let pool = StackPool::<256>::new();
        let s1 = unsafe { pool.alloc_str("hello") };
        let s2 = unsafe { pool.alloc_str("world") };
        assert_eq!(s1, "hello");
        assert_eq!(s2, "world");
        assert_eq!(pool.used_bytes(), 10);
    }

    #[test]
    fn test_stack_pool_alloc_bytes() {
        let pool = StackPool::<256>::new();
        let b1 = unsafe { pool.alloc_bytes(&[1, 2, 3]) };
        let b2 = unsafe { pool.alloc_bytes(&[4, 5]) };
        assert_eq!(b1, &[1, 2, 3]);
        assert_eq!(b2, &[4, 5]);
        assert_eq!(pool.used_bytes(), 5);
    }

    #[test]
    fn test_stack_pool_capacity_overflow() {
        let pool = StackPool::<8>::new();
        let s1 = unsafe { pool.alloc_str("hello") };
        assert_eq!(s1, "hello");
        let s2 = unsafe { pool.alloc_str("world") };
        assert_eq!(s2, "world");
        assert_eq!(pool.used_bytes(), 5);
    }

    #[test]
    fn test_stack_pool_reset() {
        let pool = StackPool::<256>::new();
        let _ = unsafe { pool.alloc_str("hello") };
        assert_eq!(pool.used_bytes(), 5);
        pool.reset();
        assert_eq!(pool.used_bytes(), 0);
    }

    #[test]
    fn test_stack_pool_used_bytes() {
        let pool = StackPool::<256>::new();
        assert_eq!(pool.used_bytes(), 0);
        let _ = unsafe { pool.alloc_str("abc") };
        assert_eq!(pool.used_bytes(), 3);
        let _ = unsafe { pool.alloc_bytes(&[1, 2]) };
        assert_eq!(pool.used_bytes(), 5);
    }

    #[test]
    fn test_stack_pool_empty_alloc() {
        let pool = StackPool::<256>::new();
        let s = unsafe { pool.alloc_str("") };
        assert_eq!(s, "");
        assert_eq!(pool.used_bytes(), 0);
        let b = unsafe { pool.alloc_bytes(&[][..]) };
        assert!(b.is_empty());
        assert_eq!(pool.used_bytes(), 0);
    }

    #[test]
    fn test_stack_pool_remaining() {
        let pool = StackPool::<256>::new();
        assert_eq!(pool.remaining(), 256);
        let _ = unsafe { pool.alloc_str("hello") };
        assert_eq!(pool.remaining(), 251);
    }

    #[test]
    fn test_stack_pool_capacity() {
        assert_eq!(StackPool::<1024>::capacity(), 1024);
        assert_eq!(StackPool::<4096>::capacity(), 4096);
    }

    #[test]
    fn test_stack_pool_request_isolation() {
        let pool = StackPool::<256>::new();
        let s1 = unsafe { pool.alloc_str("request1") };
        assert_eq!(s1, "request1");
        pool.reset();
        let s2 = unsafe { pool.alloc_str("request2") };
        assert_eq!(s2, "request2");
        assert_eq!(pool.used_bytes(), 8);
    }

    #[test]
    fn test_create_pool_stack() {
        let config = MemPoolConfig::default();
        let pool = create_pool(&config).unwrap();
        let s = unsafe { pool.alloc_str("hello") };
        assert_eq!(s, "hello");
    }

    #[test]
    fn test_create_pool_none() {
        let config = MemPoolConfig {
            pool_type: MemPoolType::None,
            capacity: 0,
        };
        assert!(create_pool(&config).is_none());
    }

    #[test]
    fn test_create_pool_various_capacities() {
        for &cap in &[1024, 2048, 4096, 8192, 16384, 32768, 65536] {
            let config = MemPoolConfig {
                pool_type: MemPoolType::Stack,
                capacity: cap,
            };
            let pool = create_pool(&config).unwrap();
            let s = unsafe { pool.alloc_str("test") };
            assert_eq!(s, "test");
        }
    }

    #[test]
    fn test_mempool_config_default() {
        let config = MemPoolConfig::default();
        assert_eq!(config.pool_type, MemPoolType::Stack);
        assert_eq!(config.capacity, 4096);
    }
}