Skip to main content

qcode/
space.rs

1//! Memory spaces: uniformly-addressed regions that varnodes live in.
2//!
3//! Every [`Varnode`](crate::value::Varnode) belongs to exactly one [`Space`].
4//! The space vocabulary itself lives in `pcode-types`, shared with the SLEIGH
5//! decoder; this module re-exports it and adds the qcode-specific handles that
6//! distinguish shared spaces from per-function temporaries.
7
8use serde::{Deserialize, Serialize};
9
10pub use pcode_types::space::{SPACE_CONST, Space, SpaceId, SpaceRef, SpaceStore, SpaceType};
11
12use crate::value::{FunctionId, LocalTempSpaceId, TempSpaceId};
13
14/// A memory-space handle stored inside one function body.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum LocalMemorySpaceId {
17    Shared(SpaceId),
18    Temp(LocalTempSpaceId),
19}
20
21impl LocalMemorySpaceId {
22    pub const fn qualify(self, function: FunctionId) -> MemorySpaceId {
23        match self {
24            Self::Shared(id) => MemorySpaceId::Shared(id),
25            Self::Temp(local) => MemorySpaceId::Temp(TempSpaceId::new(function, local)),
26        }
27    }
28
29    /// Returns the shared space while producers still use only module storage.
30    pub const fn shared(self) -> Option<SpaceId> {
31        match self {
32            Self::Shared(id) => Some(id),
33            Self::Temp(_) => None,
34        }
35    }
36}
37
38impl From<SpaceId> for LocalMemorySpaceId {
39    fn from(id: SpaceId) -> Self {
40        Self::Shared(id)
41    }
42}
43
44impl PartialEq<SpaceId> for LocalMemorySpaceId {
45    fn eq(&self, other: &SpaceId) -> bool {
46        self.shared() == Some(*other)
47    }
48}
49
50impl PartialEq<LocalMemorySpaceId> for SpaceId {
51    fn eq(&self, other: &LocalMemorySpaceId) -> bool {
52        other == self
53    }
54}
55
56/// A module/API memory-space handle. Local temporary spaces retain their owner.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum MemorySpaceId {
59    Shared(SpaceId),
60    Temp(TempSpaceId),
61}
62
63impl MemorySpaceId {
64    pub fn localize(self, function: FunctionId) -> LocalMemorySpaceId {
65        match self {
66            Self::Shared(id) => LocalMemorySpaceId::Shared(id),
67            Self::Temp(id) => LocalMemorySpaceId::Temp(id.localize(function)),
68        }
69    }
70
71    pub const fn owning_function(self) -> Option<FunctionId> {
72        match self {
73            Self::Shared(_) => None,
74            Self::Temp(id) => Some(id.func),
75        }
76    }
77
78    pub const fn shared(self) -> Option<SpaceId> {
79        match self {
80            Self::Shared(id) => Some(id),
81            Self::Temp(_) => None,
82        }
83    }
84}
85
86impl From<SpaceId> for MemorySpaceId {
87    fn from(id: SpaceId) -> Self {
88        Self::Shared(id)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn temporary_memory_space_qualification_preserves_owner() {
98        let function = FunctionId::from(3);
99        let local = LocalTempSpaceId::from(4);
100        let qualified = LocalMemorySpaceId::Temp(local).qualify(function);
101
102        assert_eq!(
103            qualified,
104            MemorySpaceId::Temp(TempSpaceId::new(function, local))
105        );
106        assert_eq!(qualified.owning_function(), Some(function));
107        assert_eq!(
108            qualified.localize(function),
109            LocalMemorySpaceId::Temp(local)
110        );
111        assert_eq!(
112            MemorySpaceId::Shared(SpaceId::from(2)).owning_function(),
113            None
114        );
115    }
116
117    #[test]
118    #[cfg(debug_assertions)]
119    #[should_panic(expected = "TempSpaceId::localize: foreign id")]
120    fn temporary_memory_space_rejects_foreign_localization() {
121        MemorySpaceId::Temp(TempSpaceId::new(
122            FunctionId::from(1),
123            LocalTempSpaceId::from(0),
124        ))
125        .localize(FunctionId::from(2));
126    }
127}