terminals_core/substrate/projection.rs
1//! Projection trait — the core abstraction for ComputeAtom views.
2//!
3//! Each projection is a deterministic pure function over a byte slice.
4//! Projections define their own memory layout (byte_size) and contribute
5//! to the atom's shape hash (σ).
6
7/// Unique identifier for a projection type.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
9pub enum ProjectionId {
10 Splat,
11 Kuramoto,
12 Expert,
13 Graph,
14 Thermal,
15}
16
17impl ProjectionId {
18 /// Deterministic hash contribution for the projection type itself.
19 pub fn type_hash(self) -> u32 {
20 match self {
21 ProjectionId::Splat => 0x5A1A_0001,
22 ProjectionId::Kuramoto => 0x4B55_0002,
23 ProjectionId::Expert => 0x3E58_0003,
24 ProjectionId::Graph => 0x4752_0004,
25 ProjectionId::Thermal => 0x5448_0005,
26 }
27 }
28}
29
30/// A projection is a typed view over a contiguous byte region within a ComputeAtom.
31///
32/// Implementations must be deterministic: same bytes in → same state out.
33/// `byte_size()` is const per type (known at compile time).
34pub trait Projection: Sized {
35 /// Fixed byte size of this projection's memory region.
36 fn byte_size() -> usize;
37
38 /// Projection type identifier.
39 fn id() -> ProjectionId;
40
41 /// Read projection state from a byte slice.
42 /// Panics if `buf.len() < Self::byte_size()`.
43 fn read(buf: &[u8]) -> Self;
44
45 /// Write projection state into a byte slice.
46 /// Panics if `buf.len() < Self::byte_size()`.
47 fn write(&self, buf: &mut [u8]);
48
49 /// Contribution to the atom's shape hash (FNV-1a input).
50 fn shape_hash_contribution(&self) -> u32;
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_projection_id_type_hash_unique() {
59 let ids = [
60 ProjectionId::Splat,
61 ProjectionId::Kuramoto,
62 ProjectionId::Expert,
63 ProjectionId::Graph,
64 ProjectionId::Thermal,
65 ];
66 let hashes: Vec<u32> = ids.iter().map(|id| id.type_hash()).collect();
67 for i in 0..hashes.len() {
68 for j in (i + 1)..hashes.len() {
69 assert_ne!(hashes[i], hashes[j], "Hash collision between {:?} and {:?}", ids[i], ids[j]);
70 }
71 }
72 }
73}