Files
OxideJS-New/crates/oxide_kernel/src/shape_forge.rs
T

310 lines
11 KiB
Rust

use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, RwLock};
use dashmap::DashMap;
use crate::{kernel_debug, kernel_trace};
/// 隐藏类(shape)的整数标识符。
pub type ShapeId = u32;
/// 属性名在字符串 intern 表([`crate::string_forge::PermInterner`])中的 id。
pub type StringIndex = u32;
/// 空 shape 的保留 id:表示"无属性"的根节点,构造对象时以此为初始 shape。
pub const EMPTY_SHAPE_ID: ShapeId = 1;
const EMPTY_SENTINEL: StringIndex = u32::MAX;
/// 单个隐藏类节点:记录新增的属性名、父节点与根到自身的深度。
///
/// shape 构成一棵树,从根([`EMPTY_SHAPE_ID`])出发每添加一个属性产生一个子节点;
/// `depth` 即该节点的属性总数。
#[derive(Debug, Clone)]
pub struct Shape {
pub id: ShapeId,
pub property_name: StringIndex,
pub parent: Option<ShapeId>,
/// 从根到本 shape 的非哨兵属性数。
pub depth: u32,
}
/// 共享隐藏类存储。
///
/// 并发模型:
/// - `shapes` 以追加为主,受 `RwLock` 保护。
/// - `transitions` 是以 `(parent_shape, property_name)` 为键的分片 `DashMap`。
/// - `positions` 缓存 `(shape_id, prop_name) → slot`,供 O(1) 重复查询。
/// - 分片数固定,保证跨机器的争用行为可复现,不依赖 DashMap 的 CPU 相关默认值。
pub struct ShapeForge {
shapes: RwLock<Vec<Option<Arc<Shape>>>>,
transitions: DashMap<u64, ShapeId>,
positions: DashMap<u64, u32>,
next_id: AtomicU32,
overflow_map: RwLock<HashMap<u64, ShapeId>>,
overflow_active: AtomicBool,
}
impl ShapeForge {
/// 创建空 forge,并预置 [`EMPTY_SHAPE_ID`] 根节点。
pub fn new() -> Self {
let forge = Self {
shapes: RwLock::new(Vec::with_capacity(256)),
transitions: DashMap::with_capacity_and_shard_amount(256, 16),
positions: DashMap::with_capacity_and_shard_amount(256, 16),
next_id: AtomicU32::new(2),
overflow_map: RwLock::new(HashMap::new()),
overflow_active: AtomicBool::new(false),
};
{
let mut shapes = forge.shapes.write().unwrap();
let empty = Arc::new(Shape {
id: EMPTY_SHAPE_ID,
property_name: EMPTY_SENTINEL,
parent: None,
depth: 0,
});
debug_assert_eq!(shapes.len(), 0);
shapes.push(Some(empty));
}
forge
}
/// 把 `(parent_shape, prop_name)` 打包为单个 u64 key,用作 transition / position 缓存的键。
pub fn pack_key(parent_id: ShapeId, prop_name: StringIndex) -> u64 {
((parent_id as u64) << 32) | (prop_name as u64)
}
/// 取(或创建)`parent_id` 加属性 `prop_name` 对应的 shape id,实现哈希一致性。
///
/// 相同 `(parent, prop_name)` 组合返回同一 id;超过 24 位上限后转入 overflow map 兜底。
pub fn make_shape(&self, parent_id: ShapeId, prop_name: StringIndex) -> ShapeId {
let key = Self::pack_key(parent_id, prop_name);
kernel_trace!("ShapeForge lookup: parent={} prop={}", parent_id, prop_name);
if let Some(entry) = self.transitions.get(&key) {
kernel_debug!("ShapeForge transition cached parent={} prop={} -> id={}", parent_id, prop_name, *entry);
return *entry;
}
if self.overflow_active.load(Ordering::Relaxed) {
if let Some(id) = self.overflow_map.read().unwrap().get(&key) {
kernel_debug!("ShapeForge overflow cached parent={} prop={} -> id={}", parent_id, prop_name, *id);
return *id;
}
}
let new_id = self.next_id.fetch_add(1, Ordering::Relaxed);
debug_assert!(new_id <= 0x00FF_FFFF, "shape_id {} exceeds 24-bit limit (16M)", new_id,);
if new_id > 0x00FF_FFFF {
self.overflow_active.store(true, Ordering::Relaxed);
let mut map = self.overflow_map.write().unwrap();
if let Some(id) = map.get(&key) {
return *id;
}
map.insert(key, new_id);
kernel_debug!("ShapeForge overflow parent={} prop={} -> id={}", parent_id, prop_name, new_id);
return new_id;
}
// 从父节点计算深度(O(1)——只读缓存值)。
let parent_depth = {
let shapes = self.shapes.read().unwrap();
shapes
.get((parent_id - 1) as usize)
.and_then(|s| s.as_ref())
.map(|s| s.depth)
.unwrap_or(0)
};
let shape = Arc::new(Shape {
id: new_id,
property_name: prop_name,
parent: Some(parent_id),
depth: parent_depth + 1,
});
{
let mut shapes = self.shapes.write().unwrap();
while shapes.len() < new_id as usize {
shapes.push(None);
}
shapes[(new_id - 1) as usize] = Some(shape);
}
let entry = self.transitions.entry(key).or_insert(new_id);
let id = *entry.value();
if id == new_id {
kernel_debug!("ShapeForge transition parent={} prop={} -> id={}", parent_id, prop_name, new_id);
} else {
kernel_debug!("ShapeForge transition cached parent={} prop={} -> id={}", parent_id, prop_name, id);
}
id
}
/// 按 id 取出 shape 节点的 `Arc` 副本;id 不存在时返回 `None`。
pub fn get_shape(&self, id: ShapeId) -> Option<Arc<Shape>> {
let shapes = self.shapes.read().unwrap();
shapes.get((id - 1) as usize).and_then(|s| s.clone())
}
/// 当前登记在册的 shape 节点数(含根节点)。
pub fn len(&self) -> usize {
self.shapes.read().unwrap().len()
}
/// 是否尚无任何 shape(正常情况下始终为 false,因根节点恒存在)。
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// 清空所有非根 shape、transition、position 缓存与 overflow 态,id 计数器复位。
///
/// 用于 session 重建:丢弃一次性对象产生的形状,只保留跨 session 共享的根节点。
/// overflow 态一并复位(不清则旧 overflow 条目把 key 映射回已被截断的 id,
/// 造成静默错深度与去重失效),id 空间完整复位。
pub fn clear_transient(&self) {
let mut shapes = self.shapes.write().unwrap();
if shapes.len() > 1 {
shapes.truncate(1);
}
drop(shapes);
self.transitions.clear();
self.positions.clear();
self.overflow_active.store(false, Ordering::Relaxed);
self.overflow_map.write().unwrap().clear();
self.next_id.store(2, Ordering::Relaxed);
}
/// 查询属性在给定 shape 对应对象存储中的槽位下标;沿父链上溯查找并缓存结果。
pub fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u32> {
let cache_key = Self::pack_key(shape_id, prop_name);
if let Some(pos) = self.positions.get(&cache_key) {
return Some(*pos);
}
let shapes = self.shapes.read().unwrap();
let root = shapes.get((shape_id - 1) as usize)?.as_ref()?;
let total_depth = root.depth;
let mut prop_steps: u32 = 0;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
let s = shapes.get((id - 1) as usize).and_then(|s| s.clone())?;
if s.property_name != EMPTY_SENTINEL {
if s.property_name == prop_name {
let pos = total_depth.checked_sub(prop_steps + 1)?;
drop(shapes);
self.positions.insert(cache_key, pos);
return Some(pos);
}
prop_steps += 1;
}
cursor = s.parent;
}
None
}
/// 判断 shape 或其祖先链上是否存在指定属性。
pub fn has_property(&self, shape_id: ShapeId, prop_name: StringIndex) -> bool {
let shapes = self.shapes.read().unwrap();
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
Some(s) => {
if s.property_name == prop_name && s.property_name != EMPTY_SENTINEL {
return true;
}
cursor = s.parent;
}
None => return false,
}
}
false
}
/// 返回 shape 的属性数量(即根到该节点的深度);未知 id 返回 0。
pub fn shape_prop_count(&self, shape_id: ShapeId) -> u32 {
let shapes = self.shapes.read().unwrap();
shapes
.get((shape_id - 1) as usize)
.and_then(|s| s.as_ref())
.map(|s| s.depth)
.unwrap_or(0)
}
}
impl Default for ShapeForge {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Barrier;
/// overflow 态复位:曾触发 overflow 后 `clear_transient` 须一并清空 overflow
/// 标志与 overflow 映射,后续 `make_shape` 从新空间取小 id 而非旧映射 id。
#[test]
fn clear_transient_resets_overflow_state() {
let forge = ShapeForge::new();
let key = ShapeForge::pack_key(EMPTY_SHAPE_ID, 1_000_500);
forge.overflow_active.store(true, Ordering::Relaxed);
forge.overflow_map.write().unwrap().insert(key, 0xDEAD);
forge.clear_transient();
assert!(!forge.overflow_active.load(Ordering::Relaxed));
assert!(forge.overflow_map.read().unwrap().is_empty());
let id = forge.make_shape(EMPTY_SHAPE_ID, 1_000_500);
assert_eq!(id, 2);
}
#[test]
#[cfg_attr(debug_assertions, should_panic(expected = "exceeds 24-bit"))]
fn overflow_debug_assert_fires() {
let forge = ShapeForge::new();
forge.next_id.store(0x0100_0000, Ordering::Relaxed);
let _ = forge.make_shape(EMPTY_SHAPE_ID, 1_000_400);
}
#[test]
fn overflow_fallback_works() {
let forge = ShapeForge::new();
let key = ShapeForge::pack_key(EMPTY_SHAPE_ID, 1_000_500);
forge.overflow_active.store(true, Ordering::Relaxed);
forge.overflow_map.write().unwrap().insert(key, 0xDEAD);
let id = forge.make_shape(EMPTY_SHAPE_ID, 1_000_500);
assert_eq!(id, 0xDEAD);
}
#[test]
fn overflow_concurrent_same_key() {
let forge = Arc::new(ShapeForge::new());
let key: StringIndex = 1_000_600;
forge.overflow_active.store(true, Ordering::Relaxed);
let barrier = Arc::new(Barrier::new(2));
let f1 = Arc::clone(&forge);
let b1 = Arc::clone(&barrier);
let h1 = std::thread::spawn(move || {
b1.wait();
f1.make_shape(EMPTY_SHAPE_ID, key)
});
let f2 = Arc::clone(&forge);
let b2 = Arc::clone(&barrier);
let h2 = std::thread::spawn(move || {
b2.wait();
f2.make_shape(EMPTY_SHAPE_ID, key)
});
let id1 = h1.join().unwrap();
let id2 = h2.join().unwrap();
assert_eq!(id1, id2);
assert!(id1 > 0);
}
}