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
use std::collections::HashMap;
use std::sync::Arc;
use shared_memory::Shmem;
use serde::{Deserialize, Serialize};
/// Layout configuration for a single variable (received from Server)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShmVariableConfig {
pub name: String, // Local name (e.g., "holding_0")
pub gm_name: String, // Global Memory name
pub offset: usize,
pub size: usize,
#[serde(rename = "type")] // Handle "type" keyword in JSON
pub data_type: String,
pub direction: String,
}
/// Manages the shared memory connection and pointers
pub struct ShmContext {
#[allow(dead_code)] // Keep shmem alive
shmem: Shmem,
pointers: HashMap<String, usize>, // variable_name -> offset
}
impl ShmContext {
pub fn new(os_id: &str, configs: Vec<ShmVariableConfig>) -> Result<Self, Box<dyn std::error::Error>> {
// Open existing SHM (created by Server)
let shmem = shared_memory::ShmemConf::new().os_id(os_id).open()?;
let mut pointers = HashMap::new();
for config in configs {
// Validate bounds (optional but recommended)
if config.offset + config.size > shmem.len() {
// Log warning or error
continue;
}
pointers.insert(config.name.clone(), config.offset);
}
Ok(Self { shmem, pointers })
}
/// Get a raw pointer to a variable.
/// UNSAFE: Caller must ensure thread safety and type correctness.
pub unsafe fn get_pointer(&self, name: &str) -> Option<*mut u8> {
self.pointers.get(name).map(|&offset| unsafe { self.shmem.as_ptr().add(offset) })
}
/// Resolve a list of variable names to an `ShmMap` of pointers.
///
/// Names not found in the layout are silently skipped.
pub fn resolve(&self, names: &[String]) -> ShmMap {
let mut map = HashMap::new();
for name in names {
if let Some(ptr) = unsafe { self.get_pointer(name) } {
map.insert(name.clone(), ShmPtr::new(ptr));
}
}
ShmMap::new(map)
}
}
/// Wrapper for a shared-memory pointer, stored as `usize` for Send+Sync safety.
#[derive(Debug, Clone, Copy)]
pub struct ShmPtr(pub usize);
impl ShmPtr {
pub fn new<T>(ptr: *mut T) -> Self {
Self(ptr as usize)
}
pub fn as_ptr<T>(&self) -> *mut T {
self.0 as *mut T
}
}
// SAFETY: ShmPtr stores an address as usize (no raw pointer) — Send+Sync is safe.
unsafe impl Send for ShmPtr {}
unsafe impl Sync for ShmPtr {}
/// A typed map of shared-memory variable names to their resolved pointers.
#[derive(Debug, Clone)]
pub struct ShmMap {
pointers: Arc<HashMap<String, ShmPtr>>,
}
impl ShmMap {
pub fn new(pointers: HashMap<String, ShmPtr>) -> Self {
Self {
pointers: Arc::new(pointers),
}
}
/// Get a typed pointer to a variable.
pub fn get<T>(&self, name: &str) -> Option<*mut T> {
self.pointers.get(name).map(|p| p.as_ptr())
}
/// Get the raw address of a variable.
pub fn get_raw(&self, name: &str) -> Option<usize> {
self.pointers.get(name).map(|p| p.0)
}
/// Read a value from shared memory.
///
/// # Safety
/// Caller must ensure `T` matches the type of data at the pointer.
pub unsafe fn read<T: Copy>(&self, name: &str) -> Option<T> {
self.get::<T>(name).map(|ptr| unsafe { *ptr })
}
/// Write a value to shared memory.
///
/// # Safety
/// Caller must ensure `T` matches the type of data at the pointer.
pub unsafe fn write<T: Copy>(&self, name: &str, value: T) -> bool {
if let Some(ptr) = self.get::<T>(name) {
unsafe { *ptr = value };
true
} else {
false
}
}
/// Check if a variable name exists in the map.
pub fn contains(&self, name: &str) -> bool {
self.pointers.contains_key(name)
}
/// Return the number of resolved pointers.
pub fn len(&self) -> usize {
self.pointers.len()
}
/// Return true if no pointers were resolved.
pub fn is_empty(&self) -> bool {
self.pointers.is_empty()
}
/// Iterate over all (name, pointer) entries.
pub fn iter(&self) -> impl Iterator<Item = (&String, &ShmPtr)> {
self.pointers.iter()
}
}