Fulcrum/src/bitcoin/heapoptional.h
2025-03-20 21:21:56 -05:00

121 lines
6.2 KiB
C++

//
// Fulcrum - A fast & nimble SPV Server for Bitcoin Cash
// Copyright (C) 2019-2025 Calin A. Culianu <calin.culianu@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program (see LICENSE.txt). If not, see
// <https://www.gnu.org/licenses/>.
//
#pragma once
#include <functional> // for std::equal_to, etc
#include <memory>
#include <utility>
namespace bitcoin {
/// An optional that stores its value on the heap, rather than in-line, in
/// order to save memory. This is implemented using a std::unique_ptr. This
/// optional is intended to be a drop-in replacement for a std::optional
/// but with the in-line cost of a unique_ptr. Like an optional but unlike a
/// unique_ptr, it can be treated as a value type. It is copy-constructible
/// and copy-assignable (does a deep copy). It also can be
/// copy/move-constructed or copy/move-assigned from a T.
///
/// Intended to be used for "heavy" optional data members that are null in the
/// common case, but take non-trivial amounts of memory when they are not
/// null. In this way, the common-case ends up using less memory than a normal
/// in-lined optional would.
template <typename T>
class HeapOptional {
std::unique_ptr<T> p{};
public:
using element_type = T;
constexpr HeapOptional() noexcept = default;
explicit HeapOptional(const T & t) { *this = t; }
explicit HeapOptional(T && t) noexcept { *this = std::move(t); }
/// Construct the HeapOptional in-place using argument forwarding
template <typename ...Args>
explicit HeapOptional(Args && ...args) { emplace(std::forward<Args>(args)...); }
HeapOptional(const HeapOptional & o) { *this = o; }
HeapOptional(HeapOptional && o) = default;
/// Create the new object in-place. Deletes previous object (if any) first.
template <typename ...Args>
void emplace(Args && ...args) { p = std::make_unique<T>(std::forward<Args>(args)...); }
HeapOptional & operator=(const HeapOptional & o) {
if (o.p) p = std::make_unique<T>(*o.p);
else p.reset();
return *this;
}
HeapOptional & operator=(HeapOptional && o) noexcept = default;
HeapOptional & operator=(const T & t) { p = std::make_unique<T>(t); return *this; }
HeapOptional & operator=(T && t) { p = std::make_unique<T>(std::move(t)); return *this; }
operator bool() const { return static_cast<bool>(p); }
T & operator*() { return *p; }
const T & operator*() const { return *p; }
T * get() { return p.get(); }
const T * get() const { return p.get(); }
T * operator->() { return p.operator->(); }
const T * operator->() const { return p.operator->(); }
void reset(T * t = nullptr) { p.reset(t); }
T * release() { return p.release(); }
//--- Comparison operators: ==, !=, <, does deep compare of pointed-to T values
// (only SFINAE-enabled if underlying type T supports these ops)
auto operator==(const HeapOptional & o) const -> decltype(std::declval<std::equal_to<T>>()(std::declval<T>(),
std::declval<T>())) {
if (p && o.p) return std::equal_to{}(*p, *o.p); // compare by pointed-to value if both are not null
return std::equal_to{}(p, o.p); // compare the unique_ptr's if either are null
}
// compare to a value directly
auto operator==(const T & t) const -> decltype(std::declval<std::equal_to<T>>()(std::declval<T>(),
std::declval<T>())) {
if (!p) return false; // we never compare equal to a real value if we are null
return std::equal_to{}(*p, t); // compare by pointed-to value if we not null
}
#if __cplusplus < 202000L /* C++20 auto-gens these so we don't need to bother to compile them in */
auto operator!=(const HeapOptional & o) const -> decltype(std::declval<std::not_equal_to<T>>()(std::declval<T>(),
std::declval<T>())) {
if (p && o.p) return std::not_equal_to{}(*p, *o.p); // compare by pointed-to value if both are not null
return std::not_equal_to{}(p, o.p); // compare the unique_ptr's if either are null
}
// compare to a value directly
auto operator!=(const T & t) const -> decltype(std::declval<std::not_equal_to<T>>()(std::declval<T>(),
std::declval<T>())) {
if (!p) return true; // we are not equal to t if we are nullptr
return std::not_equal_to{}(*p, t); // compare by pointed-to value
}
#endif
auto operator<(const HeapOptional & o) const -> decltype(std::declval<std::less<T>>()(std::declval<T>(),
std::declval<T>())) {
if (p && o.p) return std::less{}(*p, *o.p); // compare by pointed-to value if both are not null
return std::less{}(p, o.p); // compare the unique_ptr's if either are null
}
// compare to a value directly
auto operator<(const T & t) const -> decltype(std::declval<std::less<T>>()(std::declval<T>(), std::declval<T>())) {
if (!p) return true; // if we are null we are always less
return std::less{}(*p, t); // compare to the pointed-to value
}
};
} // namespace bitcoin