datex_core/runtime/
memory.rs

1use crate::collections::HashMap;
2use crate::global::protocol_structures::instructions::RawFullPointerAddress;
3use crate::libs::core::{CoreLibPointerId, load_core_lib};
4use crate::references::reference::Reference;
5use crate::references::type_reference::TypeReference;
6use crate::references::value_reference::ValueReference;
7use crate::stdlib::rc::Rc;
8use crate::stdlib::vec::Vec;
9use crate::types::error::IllegalTypeError;
10use crate::utils::time::Time;
11use crate::values::core_values::endpoint::Endpoint;
12use crate::values::pointer::PointerAddress;
13use binrw::io::Cursor;
14use core::cell::RefCell;
15use core::prelude::rust_2024::*;
16use core::result::Result;
17
18#[derive(Debug, Default)]
19pub struct Memory {
20    local_endpoint: Endpoint,
21    local_counter: u64,  // counter for local pointer ids
22    last_timestamp: u64, // last timestamp used for a new local pointer id
23    pointers: HashMap<PointerAddress, Reference>, // all pointers
24}
25
26impl Memory {
27    /// Creates a new, Memory instance with the core library loaded.
28    pub fn new(endpoint: Endpoint) -> Memory {
29        let mut memory = Memory {
30            local_endpoint: endpoint,
31            local_counter: 0,
32            last_timestamp: 0,
33            pointers: HashMap::new(),
34        };
35        // load core library
36        load_core_lib(&mut memory);
37        memory
38    }
39
40    /// Registers a new reference in memory. If the reference has no PointerAddress, a new local one is generated.
41    /// If the reference is already registered (has a PointerAddress), the existing address is returned and no new registration is done.
42    /// Returns the PointerAddress of the registered reference.
43    pub fn register_reference(
44        &mut self,
45        reference: &Reference,
46    ) -> PointerAddress {
47        let pointer_address = reference.pointer_address();
48        // check if reference is already registered (if it has an address, we assume it is registered)
49        if let Some(ref address) = pointer_address
50            && self.pointers.contains_key(address)
51        {
52            return address.clone();
53        }
54        // auto-generate new local id if no id is set
55        let pointer_address = if let Some(address) = pointer_address {
56            address
57        } else {
58            let pointer_address = self.get_new_local_address();
59            reference.set_pointer_address(pointer_address.clone());
60            pointer_address
61        };
62
63        self.pointers
64            .insert(pointer_address.clone(), reference.clone());
65        pointer_address
66    }
67
68    /// Returns a reference stored at the given PointerAddress, if it exists.
69    pub fn get_reference(
70        &self,
71        pointer_address: &PointerAddress,
72    ) -> Option<&Reference> {
73        self.pointers.get(pointer_address)
74    }
75
76    pub fn get_value_reference(
77        &self,
78        pointer_address: &PointerAddress,
79    ) -> Option<&Rc<RefCell<ValueReference>>> {
80        self.get_reference(pointer_address).and_then(|r| match r {
81            Reference::ValueReference(v) => Some(v),
82            _ => None,
83        })
84    }
85
86    pub fn get_type_reference(
87        &self,
88        pointer_address: &PointerAddress,
89    ) -> Option<&Rc<RefCell<TypeReference>>> {
90        self.get_reference(pointer_address).and_then(|r| match r {
91            Reference::TypeReference(t) => Some(t),
92            _ => None,
93        })
94    }
95
96    /// Helper function to get a core value directly from memory
97    pub fn get_core_reference(
98        &self,
99        pointer_id: CoreLibPointerId,
100    ) -> &Reference {
101        self.get_reference(&pointer_id.into())
102            .expect("core reference not found in memory")
103    }
104
105    /// Helper function to get a core type directly from memory if it can be used as a type
106    pub fn get_core_type_reference(
107        &self,
108        pointer_id: CoreLibPointerId,
109    ) -> Result<Rc<RefCell<TypeReference>>, IllegalTypeError> {
110        let reference = self
111            .get_reference(&pointer_id.into())
112            .ok_or(IllegalTypeError::TypeNotFound)?;
113        match reference {
114            Reference::TypeReference(def) => Ok(def.clone()),
115            _ => Err(IllegalTypeError::TypeNotFound),
116        }
117    }
118
119    /// Helper function to get a core type directly from memory, asserting that is can be used as a type
120    /// Panics if the core type is not found or cannot be used as a type.
121    pub fn get_core_type_reference_unchecked(
122        &self,
123        pointer_id: CoreLibPointerId,
124    ) -> Rc<RefCell<TypeReference>> {
125        // FIXME #415: Mark as unchecked
126        self.get_core_type_reference(pointer_id)
127            .expect("core type not found or cannot be used as a type")
128    }
129
130    /// Takes a RawFullPointerAddress and converts it to a PointerAddress::Local or PointerAddress::Remote,
131    /// depending on whether the pointer origin id matches the local endpoint.
132    pub fn get_pointer_address_from_raw_full_address(
133        &self,
134        raw_address: RawFullPointerAddress,
135    ) -> PointerAddress {
136        if let Ok(endpoint) = raw_address.endpoint()
137            && endpoint == self.local_endpoint
138        {
139            // TODO #639: check if it makes sense to take the last 5 bytes only here
140            let last_bytes = &raw_address.id[raw_address.id.len() - 5..];
141            PointerAddress::Local(last_bytes.try_into().unwrap())
142        } else {
143            // combine raw_address.endpoint and raw_address.id to [u8; 26]
144            let writer = Cursor::new(Vec::new());
145            let mut bytes = writer.into_inner();
146            bytes.extend_from_slice(&raw_address.id);
147            PointerAddress::Remote(<[u8; 26]>::try_from(bytes).unwrap())
148        }
149    }
150
151    /// Creates a new unique local PointerAddress.
152    pub fn get_new_local_address(&mut self) -> PointerAddress {
153        let timestamp = Time::now();
154        // new timestamp, reset counter
155        if timestamp != self.last_timestamp {
156            self.last_timestamp = timestamp;
157            self.local_counter = 0;
158        }
159        // same timestamp as last time, increment counter to prevent collision
160        else {
161            self.local_counter += 1;
162        }
163        self.local_counter += 1;
164
165        // create id: 4 bytes timestamp + 1 byte counter
166        let id: [u8; 5] = [
167            (timestamp >> 24) as u8,
168            (timestamp >> 16) as u8,
169            (timestamp >> 8) as u8,
170            timestamp as u8,
171            (self.local_counter & 0xFF) as u8,
172        ];
173        PointerAddress::Local(id)
174    }
175}
176
177impl Reference {
178    /// Returns the PointerAddress of this reference, if it has one.
179    /// Otherwise, it registers the reference in the given memory and returns the newly assigned PointerAddress.
180    pub fn ensure_pointer_address(
181        &self,
182        memory: &RefCell<Memory>,
183    ) -> PointerAddress {
184        self.pointer_address()
185            .unwrap_or_else(|| memory.borrow_mut().register_reference(self))
186    }
187}