1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Copyright 2018 The Fuchsia Authors. All rights reserved.
16 // Use of this source code is governed by a BSD-style license that can be
17 // found in the LICENSE file.
18 
19 #pragma once
20 
21 #include <assert.h>
22 
23 #include <optional>
24 #include <type_traits>
25 #include <utility>
26 
27 namespace gfxstream::guest {
28 namespace fit {
29 
30 // Determines whether a type can be compared with nullptr.
31 template <typename T, typename Comparable = bool>
32 struct IsComparableWithNull : public std::false_type {};
33 template <typename T>
34 struct IsComparableWithNull<T, decltype(std::declval<const T&>() == nullptr)>
35     : public std::true_type {};
36 
37 // Suppress the warning when the compiler can see that a nullable value is
38 // never equal to nullptr.
39 #pragma GCC diagnostic push
40 #pragma GCC diagnostic ignored "-Waddress"
41 template <typename T, std::enable_if_t<IsComparableWithNull<T>::value, bool> = true>
42 constexpr inline bool isNull(T&& value) {
43     return std::forward<T>(value) == nullptr;
44 }
45 #pragma GCC diagnostic pop
46 
47 template <typename T, std::enable_if_t<!IsComparableWithNull<T>::value, bool> = false>
48 constexpr inline bool isNull(T&&) {
49     return false;
50 }
51 
52 // Determines whether a type can be initialized, assigned, and compared
53 // with nullptr.
54 template <typename T>
55 struct IsNullable
56     : public std::integral_constant<bool,
57                                     std::is_constructible<T, decltype(nullptr)>::value &&
58                                         std::is_assignable<T&, decltype(nullptr)>::value &&
59                                         IsComparableWithNull<T>::value> {};
60 template <>
61 struct IsNullable<void> : public std::false_type {};
62 
63 // Holds a value or nullptr.
64 //
65 // This class is similar to |std::optional<T>| except that it uses less
66 // storage when the value type can be initialized, assigned, and compared
67 // with nullptr.
68 //
69 // For example:
70 // - sizeof(fit::nullable<void*>) == sizeof(void*)
71 // - sizeof(std::optional<void*>) == sizeof(struct { bool; void*; })
72 // - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; })
73 // - sizeof(std::optional<int>) == sizeof(struct { bool; int; })
74 //
75 // TODO(fxbug.dev/42123486): fit::nullable does not precisely mirror
76 // std::optional. This should be corrected to avoid surprises when switching
77 // between the types.
78 template <typename T,
79           bool = (IsNullable<T>::value && std::is_constructible<T, T&&>::value &&
80                   std::is_assignable<T&, T&&>::value)>
81 class Nullable final {
82 public:
83     using value_type = T;
84 
85     ~Nullable() = default;
86     constexpr Nullable() = default;
87 
88     explicit constexpr Nullable(decltype(nullptr)) {}
89     explicit constexpr Nullable(T value) : mOpt(std::move(value)) {}
90 
91     constexpr Nullable(const Nullable& other) = default;
92     constexpr Nullable& operator=(const Nullable& other) = default;
93 
94     constexpr Nullable(Nullable&& other) = default;
95     constexpr Nullable& operator=(Nullable&& other) = default;
96 
97     constexpr T& value() & { return mOpt.value(); }
98     constexpr const T& value() const& { return mOpt.value(); }
99     constexpr T&& value() && { return std::move(mOpt.value()); }
100     constexpr const T&& value() const&& { return std::move(mOpt.value()); }
101 
102     template <typename U = T>
103     constexpr T valueOr(U&& default_value) const {
104         return mOpt.value_or(std::forward<U>(default_value));
105     }
106 
107     constexpr T* operator->() { return &*mOpt; }
108     constexpr const T* operator->() const { return &*mOpt; }
109     constexpr T& operator*() { return *mOpt; }
110     constexpr const T& operator*() const { return *mOpt; }
111 
112     constexpr bool hasValue() const { return mOpt.has_value(); }
113     explicit constexpr operator bool() const { return hasValue(); }
114 
115     constexpr Nullable& operator=(decltype(nullptr)) {
116         reset();
117         return *this;
118     }
119 
120     constexpr Nullable& operator=(T value) {
121         mOpt = std::move(value);
122         return *this;
123     }
124 
125     constexpr void reset() { mOpt.reset(); }
126 
127     constexpr void swap(Nullable& other) { mOpt.swap(other.mOpt); }
128 
129 private:
130     std::optional<T> mOpt;
131 };
132 
133 template <typename T>
134 class Nullable<T, true> final {
135 public:
136     using value_type = T;
137 
138     constexpr Nullable() : mValue(nullptr) {}
139     explicit constexpr Nullable(decltype(nullptr)) : mValue(nullptr) {}
140     explicit constexpr Nullable(T value) : mValue(std::move(value)) {}
141     constexpr Nullable(const Nullable& other) = default;
142     constexpr Nullable(Nullable&& other) : mValue(std::move(other.value_)) {}
143     ~Nullable() = default;
144 
145     constexpr T& value() & {
146         if (hasValue()) {
147             return mValue;
148         } else {
149             __builtin_abort();
150         }
151     }
152     constexpr const T& value() const& {
153         if (hasValue()) {
154             return mValue;
155         } else {
156             __builtin_abort();
157         }
158     }
159     constexpr T&& value() && {
160         if (hasValue()) {
161             return std::move(mValue);
162         } else {
163             __builtin_abort();
164         }
165     }
166     constexpr const T&& value() const&& {
167         if (hasValue()) {
168             return std::move(mValue);
169         } else {
170             __builtin_abort();
171         }
172     }
173 
174     template <typename U = T>
175     constexpr T valueOr(U&& default_value) const {
176         return hasValue() ? mValue : static_cast<T>(std::forward<U>(default_value));
177     }
178 
179     constexpr T* operator->() { return &mValue; }
180     constexpr const T* operator->() const { return &mValue; }
181     constexpr T& operator*() { return mValue; }
182     constexpr const T& operator*() const { return mValue; }
183 
184     constexpr bool hasValue() const { return !(mValue == nullptr); }
185     explicit constexpr operator bool() const { return hasValue(); }
186 
187     constexpr Nullable& operator=(const Nullable& other) = default;
188     constexpr Nullable& operator=(Nullable&& other) {
189         mValue = std::move(other.value_);
190         return *this;
191     }
192 
193     constexpr Nullable& operator=(decltype(nullptr)) {
194         reset();
195         return *this;
196     }
197 
198     constexpr Nullable& operator=(T value) {
199         mValue = std::move(value);
200         return *this;
201     }
202 
203     constexpr void reset() { mValue = nullptr; }
204 
205     constexpr void swap(Nullable& other) {
206         using std::swap;
207         swap(mValue, other.value_);
208     }
209 
210 private:
211     T mValue;
212 };
213 
214 template <typename T>
215 void swap(Nullable<T>& a, Nullable<T>& b) {
216     a.swap(b);
217 }
218 
219 template <typename T>
220 constexpr bool operator==(const Nullable<T>& lhs, decltype(nullptr)) {
221     return !lhs.hasValue();
222 }
223 template <typename T>
224 constexpr bool operator!=(const Nullable<T>& lhs, decltype(nullptr)) {
225     return lhs.hasValue();
226 }
227 
228 template <typename T>
229 constexpr bool operator==(decltype(nullptr), const Nullable<T>& rhs) {
230     return !rhs.hasValue();
231 }
232 template <typename T>
233 constexpr bool operator!=(decltype(nullptr), const Nullable<T>& rhs) {
234     return rhs.hasValue();
235 }
236 
237 template <typename T, typename U>
238 constexpr bool operator==(const Nullable<T>& lhs, const Nullable<U>& rhs) {
239     return (lhs.hasValue() == rhs.hasValue()) && (!lhs.hasValue() || *lhs == *rhs);
240 }
241 template <typename T, typename U>
242 constexpr bool operator!=(const Nullable<T>& lhs, const Nullable<U>& rhs) {
243     return (lhs.hasValue() != rhs.hasValue()) || (lhs.hasValue() && *lhs != *rhs);
244 }
245 
246 template <typename T, typename U>
247 constexpr bool operator==(const Nullable<T>& lhs, const U& rhs) {
248     return (lhs.hasValue() != isNull(rhs)) && (!lhs.hasValue() || *lhs == rhs);
249 }
250 template <typename T, typename U>
251 constexpr bool operator!=(const Nullable<T>& lhs, const U& rhs) {
252     return (lhs.hasValue() == isNull(rhs)) || (lhs.hasValue() && *lhs != rhs);
253 }
254 
255 template <typename T, typename U>
256 constexpr bool operator==(const T& lhs, const Nullable<U>& rhs) {
257     return (isNull(lhs) != rhs.hasValue()) && (!rhs.hasValue() || lhs == *rhs);
258 }
259 template <typename T, typename U>
260 constexpr bool operator!=(const T& lhs, const Nullable<U>& rhs) {
261     return (isNull(lhs) == rhs.hasValue()) || (rhs.hasValue() && lhs != *rhs);
262 }
263 
264 }  // namespace fit
265 }  // namespace gfxstream::guest
266