aboutsummaryrefslogtreecommitdiff
path: root/src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h')
-rw-r--r--src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h b/src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h
new file mode 100644
index 00000000000..02b33372f8a
--- /dev/null
+++ b/src/gfxstream/guest/android-emu/aemu/base/fit/Nullable.h
@@ -0,0 +1,265 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <assert.h>
+
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace gfxstream::guest {
+namespace fit {
+
+// Determines whether a type can be compared with nullptr.
+template <typename T, typename Comparable = bool>
+struct IsComparableWithNull : public std::false_type {};
+template <typename T>
+struct IsComparableWithNull<T, decltype(std::declval<const T&>() == nullptr)>
+ : public std::true_type {};
+
+// Suppress the warning when the compiler can see that a nullable value is
+// never equal to nullptr.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Waddress"
+template <typename T, std::enable_if_t<IsComparableWithNull<T>::value, bool> = true>
+constexpr inline bool isNull(T&& value) {
+ return std::forward<T>(value) == nullptr;
+}
+#pragma GCC diagnostic pop
+
+template <typename T, std::enable_if_t<!IsComparableWithNull<T>::value, bool> = false>
+constexpr inline bool isNull(T&&) {
+ return false;
+}
+
+// Determines whether a type can be initialized, assigned, and compared
+// with nullptr.
+template <typename T>
+struct IsNullable
+ : public std::integral_constant<bool,
+ std::is_constructible<T, decltype(nullptr)>::value &&
+ std::is_assignable<T&, decltype(nullptr)>::value &&
+ IsComparableWithNull<T>::value> {};
+template <>
+struct IsNullable<void> : public std::false_type {};
+
+// Holds a value or nullptr.
+//
+// This class is similar to |std::optional<T>| except that it uses less
+// storage when the value type can be initialized, assigned, and compared
+// with nullptr.
+//
+// For example:
+// - sizeof(fit::nullable<void*>) == sizeof(void*)
+// - sizeof(std::optional<void*>) == sizeof(struct { bool; void*; })
+// - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; })
+// - sizeof(std::optional<int>) == sizeof(struct { bool; int; })
+//
+// TODO(fxbug.dev/4681): fit::nullable does not precisely mirror
+// std::optional. This should be corrected to avoid surprises when switching
+// between the types.
+template <typename T,
+ bool = (IsNullable<T>::value && std::is_constructible<T, T&&>::value &&
+ std::is_assignable<T&, T&&>::value)>
+class Nullable final {
+public:
+ using value_type = T;
+
+ ~Nullable() = default;
+ constexpr Nullable() = default;
+
+ explicit constexpr Nullable(decltype(nullptr)) {}
+ explicit constexpr Nullable(T value) : mOpt(std::move(value)) {}
+
+ constexpr Nullable(const Nullable& other) = default;
+ constexpr Nullable& operator=(const Nullable& other) = default;
+
+ constexpr Nullable(Nullable&& other) = default;
+ constexpr Nullable& operator=(Nullable&& other) = default;
+
+ constexpr T& value() & { return mOpt.value(); }
+ constexpr const T& value() const& { return mOpt.value(); }
+ constexpr T&& value() && { return std::move(mOpt.value()); }
+ constexpr const T&& value() const&& { return std::move(mOpt.value()); }
+
+ template <typename U = T>
+ constexpr T valueOr(U&& default_value) const {
+ return mOpt.value_or(std::forward<U>(default_value));
+ }
+
+ constexpr T* operator->() { return &*mOpt; }
+ constexpr const T* operator->() const { return &*mOpt; }
+ constexpr T& operator*() { return *mOpt; }
+ constexpr const T& operator*() const { return *mOpt; }
+
+ constexpr bool hasValue() const { return mOpt.has_value(); }
+ explicit constexpr operator bool() const { return hasValue(); }
+
+ constexpr Nullable& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ constexpr Nullable& operator=(T value) {
+ mOpt = std::move(value);
+ return *this;
+ }
+
+ constexpr void reset() { mOpt.reset(); }
+
+ constexpr void swap(Nullable& other) { mOpt.swap(other.mOpt); }
+
+private:
+ std::optional<T> mOpt;
+};
+
+template <typename T>
+class Nullable<T, true> final {
+public:
+ using value_type = T;
+
+ constexpr Nullable() : mValue(nullptr) {}
+ explicit constexpr Nullable(decltype(nullptr)) : mValue(nullptr) {}
+ explicit constexpr Nullable(T value) : mValue(std::move(value)) {}
+ constexpr Nullable(const Nullable& other) = default;
+ constexpr Nullable(Nullable&& other) : mValue(std::move(other.value_)) {}
+ ~Nullable() = default;
+
+ constexpr T& value() & {
+ if (hasValue()) {
+ return mValue;
+ } else {
+ __builtin_abort();
+ }
+ }
+ constexpr const T& value() const& {
+ if (hasValue()) {
+ return mValue;
+ } else {
+ __builtin_abort();
+ }
+ }
+ constexpr T&& value() && {
+ if (hasValue()) {
+ return std::move(mValue);
+ } else {
+ __builtin_abort();
+ }
+ }
+ constexpr const T&& value() const&& {
+ if (hasValue()) {
+ return std::move(mValue);
+ } else {
+ __builtin_abort();
+ }
+ }
+
+ template <typename U = T>
+ constexpr T valueOr(U&& default_value) const {
+ return hasValue() ? mValue : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ constexpr T* operator->() { return &mValue; }
+ constexpr const T* operator->() const { return &mValue; }
+ constexpr T& operator*() { return mValue; }
+ constexpr const T& operator*() const { return mValue; }
+
+ constexpr bool hasValue() const { return !(mValue == nullptr); }
+ explicit constexpr operator bool() const { return hasValue(); }
+
+ constexpr Nullable& operator=(const Nullable& other) = default;
+ constexpr Nullable& operator=(Nullable&& other) {
+ mValue = std::move(other.value_);
+ return *this;
+ }
+
+ constexpr Nullable& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ constexpr Nullable& operator=(T value) {
+ mValue = std::move(value);
+ return *this;
+ }
+
+ constexpr void reset() { mValue = nullptr; }
+
+ constexpr void swap(Nullable& other) {
+ using std::swap;
+ swap(mValue, other.value_);
+ }
+
+private:
+ T mValue;
+};
+
+template <typename T>
+void swap(Nullable<T>& a, Nullable<T>& b) {
+ a.swap(b);
+}
+
+template <typename T>
+constexpr bool operator==(const Nullable<T>& lhs, decltype(nullptr)) {
+ return !lhs.hasValue();
+}
+template <typename T>
+constexpr bool operator!=(const Nullable<T>& lhs, decltype(nullptr)) {
+ return lhs.hasValue();
+}
+
+template <typename T>
+constexpr bool operator==(decltype(nullptr), const Nullable<T>& rhs) {
+ return !rhs.hasValue();
+}
+template <typename T>
+constexpr bool operator!=(decltype(nullptr), const Nullable<T>& rhs) {
+ return rhs.hasValue();
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const Nullable<T>& lhs, const Nullable<U>& rhs) {
+ return (lhs.hasValue() == rhs.hasValue()) && (!lhs.hasValue() || *lhs == *rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const Nullable<T>& lhs, const Nullable<U>& rhs) {
+ return (lhs.hasValue() != rhs.hasValue()) || (lhs.hasValue() && *lhs != *rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const Nullable<T>& lhs, const U& rhs) {
+ return (lhs.hasValue() != isNull(rhs)) && (!lhs.hasValue() || *lhs == rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const Nullable<T>& lhs, const U& rhs) {
+ return (lhs.hasValue() == isNull(rhs)) || (lhs.hasValue() && *lhs != rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const T& lhs, const Nullable<U>& rhs) {
+ return (isNull(lhs) != rhs.hasValue()) && (!rhs.hasValue() || lhs == *rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const T& lhs, const Nullable<U>& rhs) {
+ return (isNull(lhs) == rhs.hasValue()) || (rhs.hasValue() && lhs != *rhs);
+}
+
+} // namespace fit
+} // namespace gfxstream::guest