Skip to main content

vpi/
handle.rs

1use crate::ObjectType;
2use vpi_sys::{vpiHandle, PLI_INT32};
3
4/// Wrapper around a raw VPI object handle.
5///
6/// This type provides convenience helpers for common handle operations and
7/// iteration over child objects.
8#[derive(Debug, Clone)]
9pub struct Handle {
10    /// Underlying simulator-owned VPI handle pointer.
11    handle: vpiHandle,
12}
13
14impl Default for Handle {
15    fn default() -> Self {
16        Self::null()
17    }
18}
19
20impl PartialEq for Handle {
21    fn eq(&self, other: &Self) -> bool {
22        unsafe { vpi_sys::vpi_compare_objects(self.handle, other.handle) != 0 }
23    }
24}
25
26impl Drop for Handle {
27    fn drop(&mut self) {
28        #[cfg(feature = "release_handle")]
29        if !self.is_null() {
30            unsafe {
31                vpi_sys::vpi_release_handle(self.handle);
32            }
33        }
34    }
35}
36
37impl Handle {
38    /// Creates a null handle.
39    #[must_use]
40    pub fn null() -> Self {
41        Self {
42            handle: std::ptr::null_mut(),
43        }
44    }
45
46    /// Returns `true` if this handle is null.
47    #[must_use]
48    pub fn is_null(&self) -> bool {
49        self.handle.is_null()
50    }
51
52    /// Returns the underlying raw VPI handle.
53    #[must_use]
54    pub fn as_raw(&self) -> vpiHandle {
55        self.handle
56    }
57
58    /// Replaces the current handle with null.
59    ///
60    /// This is used when ownership of the raw handle is not held by this type.
61    pub fn clear(&mut self) {
62        self.handle = std::ptr::null_mut();
63    }
64
65    /// Constructs a [`Handle`] from a raw VPI handle pointer.
66    pub fn from_raw(raw: vpiHandle) -> Self {
67        Self { handle: raw }
68    }
69
70    /// Looks up a handle by hierarchical name.
71    ///
72    /// Wraps `vpi_handle_by_name`. Pass a null handle as `scope` to resolve
73    /// against the root (absolute hierarchical names). Returns a null handle
74    /// when the name cannot be resolved.
75    #[must_use]
76    pub fn handle_by_name(name: &str) -> Self {
77        Self::handle_by_name_and_scope(name, &Handle::null())
78    }
79
80    /// Returns a handle located by name within a scope.
81    ///
82    /// Passing `Handle::null()` as the `scope` will resolve the name against the root of the hierarchy.
83    ///
84    /// Returns a null handle when the object is not found.
85    #[must_use]
86    pub fn handle_by_name_and_scope(name: &str, scope: &Handle) -> Self {
87        let Ok(c_name) = std::ffi::CString::new(name) else {
88            return Self::null();
89        };
90        let scope_raw = if scope.is_null() {
91            std::ptr::null_mut()
92        } else {
93            scope.as_raw()
94        };
95        let handle =
96            unsafe { vpi_sys::vpi_handle_by_name(c_name.as_ptr().cast_mut().cast(), scope_raw) };
97        Self::from_raw(handle)
98    }
99
100    /// Returns an iterator handle for objects of `typ` under this handle.
101    #[must_use]
102    pub fn iterator(&self, typ: ObjectType) -> HandleIterator {
103        let raw = unsafe { vpi_sys::vpi_iterate(typ as PLI_INT32, self.as_raw()) };
104        HandleIterator {
105            iter: Handle::from_raw(raw),
106        }
107    }
108
109    /// Returns a related object handle selected by `typ`.
110    ///
111    /// Returns a null handle when the relation is unavailable.
112    #[must_use]
113    pub fn get(&self, typ: ObjectType) -> Self {
114        let handle = unsafe { vpi_sys::vpi_handle(typ as PLI_INT32, self.as_raw()) };
115        Self::from_raw(handle)
116    }
117
118    /// Returns a child handle by index.
119    ///
120    /// Returns a null handle when `index` is out of range.
121    #[must_use]
122    pub fn handle_by_index(&self, index: i32) -> Self {
123        let handle = unsafe { vpi_sys::vpi_handle_by_index(self.as_raw(), index) };
124        Self::from_raw(handle)
125    }
126
127    /// Iterates across multiple object kinds and flattens all resulting handles.
128    pub fn iterators<'a>(&'a self, typ: &'a [ObjectType]) -> impl Iterator<Item = Handle> + 'a {
129        typ.iter().copied().flat_map(move |t| self.iterator(t))
130    }
131
132    /// Returns a related object handle selected by `typ` using two reference handles.
133    ///
134    /// This wraps `vpi_handle_multi` for APIs that require two source handles.
135    /// Returns a null handle when this handle or `other` is null, or when the
136    /// relation is unavailable.
137    #[must_use]
138    pub fn get_multi(&self, typ: ObjectType, other: &Handle) -> Self {
139        if self.is_null() || other.is_null() {
140            return Self::null();
141        }
142
143        let handle =
144            unsafe { vpi_sys::vpi_handle_multi(typ as PLI_INT32, self.as_raw(), other.as_raw()) };
145        Self::from_raw(handle)
146    }
147
148    /// Returns a child handle by multiple indices.
149    ///
150    /// This wraps `vpi_handle_by_multi_index` and is used for
151    /// multidimensional arrays. Returns a null handle when this handle is null,
152    /// when `indices` is empty, when the index count exceeds VPI limits, or
153    /// when no object exists at the requested index tuple.
154    #[must_use]
155    pub fn handle_by_multi_index(&self, indices: impl AsRef<[i32]>) -> Self {
156        let indices = indices.as_ref();
157        if self.is_null() || indices.is_empty() {
158            return Self::null();
159        }
160
161        let Ok(num_index) = i32::try_from(indices.len()) else {
162            return Self::null();
163        };
164
165        let handle = unsafe {
166            vpi_sys::vpi_handle_by_multi_index(
167                self.as_raw(),
168                num_index as PLI_INT32,
169                indices.as_ptr().cast_mut(),
170            )
171        };
172        Self::from_raw(handle)
173    }
174
175    /// Convenience helper for multi-handle traversal.
176    ///
177    /// First traverses to `typ` via `vpi_handle`, then resolves a
178    /// multidimensional element with `vpi_handle_by_multi_index`.
179    #[must_use]
180    pub fn multi_handle_traversal(&self, typ: ObjectType, indices: impl AsRef<[i32]>) -> Self {
181        self.get(typ).handle_by_multi_index(indices)
182    }
183}
184
185/// Iterator over VPI scan results from `vpi_iterate`/`vpi_scan`.
186pub struct HandleIterator {
187    /// Internal iterator handle consumed by successive `vpi_scan` calls.
188    pub(crate) iter: Handle,
189}
190
191impl Iterator for HandleIterator {
192    type Item = Handle;
193
194    fn next(&mut self) -> Option<Self::Item> {
195        if self.iter.is_null() {
196            return None;
197        }
198
199        let next = Handle::from_raw(unsafe { vpi_sys::vpi_scan(self.iter.as_raw()) });
200
201        if next.is_null() {
202            // The handle is automatically released when the iterator is exhausted
203            self.iter.clear();
204            None
205        } else {
206            Some(next)
207        }
208    }
209}