Skip to main content

tvm_ffi/
optional.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19//! In-place mirror of C++ `ffi::Optional<T>` (`include/tvm/ffi/optional.h`).
20//!
21//! C++ `ffi::Optional<T>` is uniformly backed by a single 16-byte `TVMFFIAny`
22//! regardless of `T`, with `type_index == kTVMFFINone` meaning `nullopt`. This
23//! makes the layout independent of the contained type, so a single Rust type
24//! mirrors every `T`.
25//!
26//! [`Optional<T>`] is `#[repr(transparent)]` over [`Any`] (the same 16-byte
27//! `TVMFFIAny` cell) and decodes such a field's bytes in place — no FFI call, no
28//! reflection getter/setter. It is named `Optional` (not `Option`) to distinguish
29//! it from Rust's [`std::option::Option`], matching the C++ `ffi::Optional` name.
30//!
31//! It replaces the earlier per-`T` mirrors (`OptionPod<T>` / `OptionStr` /
32//! `OptionObjRef<T>`): those tracked the three now-removed C++ storage layouts
33//! (`std::optional<T>`, the `String`/`Bytes` sentinel cell, and an `ObjectRef`
34//! pointer). With the uniform `TVMFFIAny` backing they collapse into this one
35//! type.
36
37use crate::any::Any;
38use crate::string::String;
39use crate::type_traits::AnyCompatible;
40use std::fmt::{self, Debug};
41use std::marker::PhantomData;
42use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
43
44/// In-place mirror of C++ `ffi::Optional<T>`: a single 16-byte `TVMFFIAny` cell
45/// (wrapped as [`Any`]) whose `type_index == kTVMFFINone` encodes `nullopt`.
46///
47/// Layout-compatible with the C++ type (`size_of == 16`); see the [module
48/// docs](self). Reuses [`Any`]'s reference-counting `Clone`/`Drop`, which are a
49/// no-op on the `nullopt` cell (`type_index` below `kTVMFFIStaticObjectBegin`).
50#[repr(transparent)]
51pub struct Optional<T: AnyCompatible> {
52    // Holds either the value's `TVMFFIAny` representation or a `kTVMFFINone` cell.
53    data: Any,
54    _marker: PhantomData<T>,
55}
56
57// Must stay 16 bytes / `TVMFFIAny`-aligned to overlay a C++ `ffi::Optional<T>`
58// field in place, independent of `T`.
59const _: () = assert!(
60    std::mem::size_of::<Optional<i64>>() == 16
61        && std::mem::size_of::<Optional<String>>() == 16
62        && std::mem::align_of::<Optional<i64>>() == std::mem::align_of::<crate::TVMFFIAny>()
63);
64
65impl<T: AnyCompatible> Optional<T> {
66    /// An engaged optional holding `value`.
67    #[inline]
68    pub fn some(value: T) -> Self {
69        Self {
70            data: Any::from(value),
71            _marker: PhantomData,
72        }
73    }
74
75    /// A disengaged optional (`nullopt`, a `kTVMFFINone` cell).
76    #[inline]
77    pub fn none() -> Self {
78        Self {
79            data: Any::new(),
80            _marker: PhantomData,
81        }
82    }
83
84    /// Whether a value is present.
85    #[inline]
86    pub fn has_value(&self) -> bool {
87        self.data.type_index() != TypeIndex::kTVMFFINone as i32
88    }
89
90    /// Whether the optional is `nullopt`.
91    #[inline]
92    pub fn is_none(&self) -> bool {
93        !self.has_value()
94    }
95
96    /// Decodes the value in place, cloning it out (ref-counted `inc_ref` for
97    /// object payloads). Returns `None` when `nullopt`. No FFI call.
98    #[inline]
99    pub fn get(&self) -> Option<T> {
100        self.data.try_as::<T>()
101    }
102
103    /// Takes the value out, consuming self (moves the payload, no `inc_ref`).
104    #[inline]
105    pub fn into_option(self) -> Option<T> {
106        if self.has_value() {
107            // Move the value out of the owning `Any` without dropping it, then
108            // transfer ownership of the payload into `T` (no inc/dec ref).
109            let mut raw = unsafe { Any::into_raw_ffi_any(self.data) };
110            Some(unsafe { T::move_from_any_after_check(&mut raw) })
111        } else {
112            None
113        }
114    }
115
116    /// Overwrites the value in place, dropping the previous one first (dec-ref'd
117    /// if it was an object payload).
118    #[inline]
119    pub fn set(&mut self, value: Option<T>) {
120        // Assignment drops the old `Any` (dec_ref if object) before storing the new.
121        self.data = match value {
122            Some(v) => Any::from(v),
123            None => Any::new(),
124        };
125    }
126}
127
128impl Optional<String> {
129    /// Borrows the engaged string as `&str`, or `None` when `nullopt`.
130    ///
131    /// Reinterprets the in-cell string without cloning: an engaged cell holds a
132    /// `String`'s exact 16-byte representation, so `&Any` can be viewed as
133    /// `&String`.
134    #[inline]
135    pub fn as_str(&self) -> Option<&str> {
136        if self.has_value() {
137            // SAFETY: an engaged `Optional<String>` cell is byte-identical to a
138            // `String` (both are `#[repr(transparent)]` over the 16-byte cell),
139            // and the borrow is tied to `&self`.
140            let s = unsafe { &*(&self.data as *const Any as *const String) };
141            Some(s.as_str())
142        } else {
143            None
144        }
145    }
146}
147
148impl<T: AnyCompatible> Default for Optional<T> {
149    /// `nullopt`, matching the C++ default constructor.
150    #[inline]
151    fn default() -> Self {
152        Self::none()
153    }
154}
155
156impl<T: AnyCompatible> Clone for Optional<T> {
157    #[inline]
158    fn clone(&self) -> Self {
159        Self {
160            // `Any::clone` inc_refs an object payload; a `nullopt` cell is a no-op.
161            data: self.data.clone(),
162            _marker: PhantomData,
163        }
164    }
165}
166
167impl<T: AnyCompatible + PartialEq> PartialEq for Optional<T> {
168    #[inline]
169    fn eq(&self, other: &Self) -> bool {
170        self.get() == other.get()
171    }
172}
173
174impl<T: AnyCompatible + Eq> Eq for Optional<T> {}
175
176impl<T: AnyCompatible> From<Option<T>> for Optional<T> {
177    #[inline]
178    fn from(value: Option<T>) -> Self {
179        match value {
180            Some(v) => Self::some(v),
181            None => Self::none(),
182        }
183    }
184}
185
186impl<T: AnyCompatible> From<Optional<T>> for Option<T> {
187    #[inline]
188    fn from(value: Optional<T>) -> Self {
189        value.into_option()
190    }
191}
192
193impl<T: AnyCompatible + Debug> Debug for Optional<T> {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self.get() {
196            Some(v) => write!(f, "Optional::Some({v:?})"),
197            None => f.write_str("Optional::None"),
198        }
199    }
200}