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}