Skip to main content

datex_core/runtime/
memory.rs

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