/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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. */ #pragma once #include #include #include #include #include #include #ifdef _MSC_VER #define THRIFT_CONSTEXPR_IF_NOT_MSVC #else #define THRIFT_CONSTEXPR_IF_NOT_MSVC constexpr #endif namespace apache { namespace thrift { namespace detail { // boxed_value_ptr will soon be replaced with boxed_value (defined below). template class boxed_value_ptr { public: using element_type = T; FOLLY_ERASE constexpr boxed_value_ptr() noexcept = default; FOLLY_ERASE constexpr boxed_value_ptr(const boxed_value_ptr& other) : ptr_(other.copy()) {} FOLLY_ERASE constexpr boxed_value_ptr(boxed_value_ptr&& other) noexcept = default; FOLLY_ERASE constexpr boxed_value_ptr(std::nullptr_t) : ptr_(nullptr) {} FOLLY_ERASE constexpr T& operator*() const noexcept { return *ptr_; } FOLLY_ERASE constexpr T* operator->() const noexcept { return ptr_.get(); } FOLLY_ERASE constexpr explicit operator bool() const noexcept { return bool(ptr_); } FOLLY_ERASE constexpr boxed_value_ptr& operator=( boxed_value_ptr&& other) noexcept = default; FOLLY_ERASE constexpr boxed_value_ptr& operator=(const boxed_value_ptr& other) { return (ptr_ = other.copy(), *this); } FOLLY_ERASE constexpr boxed_value_ptr& operator=(std::nullptr_t) noexcept { return (ptr_ = nullptr, *this); } FOLLY_ERASE constexpr void reset(T* p = nullptr) noexcept { ptr_.reset(p); } FOLLY_ERASE constexpr std::unique_ptr release() noexcept { return std::move(ptr_); } private: friend void swap(boxed_value_ptr& lhs, boxed_value_ptr& rhs) noexcept { using std::swap; swap(lhs.ptr_, rhs.ptr_); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( const boxed_value_ptr& lhs, const boxed_value_ptr& rhs) noexcept { return lhs.ptr_ == rhs.ptr_; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( std::nullptr_t, const boxed_value_ptr& rhs) noexcept { return nullptr == rhs.ptr_; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( const boxed_value_ptr& lhs, std::nullptr_t) noexcept { return lhs.ptr_ == nullptr; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( const boxed_value_ptr& lhs, const boxed_value_ptr& rhs) noexcept { return !(lhs == rhs); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( const boxed_value_ptr& lhs, std::nullptr_t) noexcept { return lhs.ptr_ != nullptr; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( std::nullptr_t, const boxed_value_ptr& rhs) noexcept { return nullptr != rhs.ptr_; } std::unique_ptr ptr_; std::unique_ptr copy() const { return ptr_ == nullptr ? nullptr : std::make_unique(*ptr_); } public: // TODO(afuller): This implicitly inplace implicit constructor // should be removed as it lets T intercept any constructor call, which is // extremely bug prone. template ()...))> FOLLY_ERASE boxed_value_ptr(Args&&... args) : ptr_(std::make_unique(std::forward(args)...)) {} // TODO(afuller): This implicit creation and value assignment should be // removed as it lets T intercept assignment, which is extremely bug prone. template FOLLY_ERASE std::enable_if_t::value, boxed_value_ptr&> operator=(U&& value) { if (ptr_ == nullptr) { ptr_ = std::make_unique(); } *ptr_ = std::forward(value); return *this; } // TODO(afuller): This is not a typical smart pointer feature, and // likely should only exist on value-semantic types like std::optional. template FOLLY_ERASE T& emplace(Args&&... args) { if (ptr_) { *ptr_ = T(std::forward(args)...); } else { ptr_ = std::make_unique(std::forward(args)...); } return *ptr_; } }; // 'boxed_ptr' provides copy-on-write behavior using 'AlignedPtr' that can tag // modes. // // It currently supports two modes, 'Mode::MutOwned' and 'Mode::ConstUnowned'. // 'boxed_ptr' with 'Mode::ConstUnowned' does not own underlying value and needs // to copy the underlying value before allowing mutation access. 'boxed_ptr' // with 'Mode::MutOwned' owns the underlying value and can mutate the underlying // value. template class boxed_ptr { public: using element_type = T; FOLLY_ERASE static constexpr boxed_ptr fromStaticConstant(const T* t) { assert(t != nullptr); return boxed_ptr{t}; } FOLLY_ERASE constexpr boxed_ptr() noexcept = default; FOLLY_ERASE constexpr boxed_ptr(boxed_ptr&& other) noexcept : ptr_(std::move(other.ptr_)) { other.ptr_.setTag(static_cast(Mode::ConstUnowned)); } FOLLY_ERASE constexpr boxed_ptr& operator=(boxed_ptr&& other) noexcept { reset(); ptr_ = std::move(other.ptr_); other.ptr_.setTag(static_cast(Mode::ConstUnowned)); return *this; } FOLLY_ERASE constexpr boxed_ptr(const boxed_ptr& other) = delete; FOLLY_ERASE constexpr boxed_ptr& operator=(const boxed_ptr& other) = delete; FOLLY_ERASE ~boxed_ptr() noexcept { destroy(); } FOLLY_ERASE constexpr explicit boxed_ptr(std::unique_ptr uptr) noexcept : ptr_(uptr.release(), static_cast(Mode::MutOwned)) {} FOLLY_ERASE constexpr const T& operator*() const noexcept { return *ptr_.get(); } FOLLY_ERASE constexpr const T* operator->() const noexcept { return ptr_.get(); } FOLLY_ERASE constexpr explicit operator bool() const noexcept { return bool(ptr_.get()); } FOLLY_ERASE constexpr T* mut() { assert(bool(ptr_.get())); if (getMode() == Mode::ConstUnowned) { ptr_ = copyPtr(); } return ptr_.get(); } FOLLY_ERASE constexpr void reset() { destroy(); } FOLLY_ERASE constexpr void reset(std::unique_ptr p) { destroy(); ptr_.set(p.release(), static_cast(Mode::MutOwned)); } FOLLY_ERASE constexpr boxed_ptr copy() const { if (getMode() == Mode::ConstUnowned) { return fromStaticConstant(ptr_.get()); } boxed_ptr ptr; ptr.ptr_ = copyPtr(); return ptr; } FOLLY_ERASE constexpr T* get() const noexcept { return ptr_.get(); } private: static constexpr int kModeBits = 3; using aligned_pointer_type = type::detail::AlignedPtr; enum Mode : std::uintptr_t { MutOwned = 0, // Unique ownership ConstUnowned = 1, }; FOLLY_ERASE constexpr explicit boxed_ptr(const T* p) noexcept : ptr_( // The const-ness of the pointer will be tracked using the tag // Mode::ConstUnowned. The only mutable accessor method is // boxed_ptr::mut(), which will copy *p in order to return a // mutable pointer. const_cast(p), static_cast(Mode::ConstUnowned)) {} friend void swap(boxed_ptr& lhs, boxed_ptr& rhs) noexcept { using std::swap; swap(lhs.ptr_, rhs.ptr_); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( const boxed_ptr& lhs, const boxed_ptr& rhs) noexcept { return lhs.ptr_.get() == rhs.ptr_.get(); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( std::nullptr_t, const boxed_ptr& rhs) noexcept { return nullptr == rhs.ptr_.get(); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator==( const boxed_ptr& lhs, std::nullptr_t) noexcept { return lhs.ptr_.get() == nullptr; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( const boxed_ptr& lhs, const boxed_ptr& rhs) noexcept { return !(lhs == rhs); } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( const boxed_ptr& lhs, std::nullptr_t) noexcept { return lhs.ptr_.get() != nullptr; } friend THRIFT_CONSTEXPR_IF_NOT_MSVC bool operator!=( std::nullptr_t, const boxed_ptr& rhs) noexcept { return nullptr != rhs.ptr_.get(); } Mode getMode() const { return static_cast(ptr_.getTag()); } void destroy() { if (getMode() == Mode::MutOwned) { delete ptr_.get(); } ptr_.clear(); } aligned_pointer_type copyPtr() const { // This leaves the tag region cleared indicating the new value is // Mode::MutOwned. aligned_pointer_type ptr = ptr_.get() == nullptr ? nullptr : new T(*ptr_.get()); assert(ptr.getTag() == Mode::MutOwned); return ptr; } aligned_pointer_type ptr_; }; // 'boxed_value' provides value semantics to the 'boxed_ptr'. template class boxed_value : public boost::totally_ordered>, public boost::totally_ordered2, T> { public: using element_type = T; static constexpr boxed_value fromStaticConstant(const T* t) { assert(t != nullptr); return boxed_value{t}; } FOLLY_ERASE constexpr boxed_value() noexcept = default; FOLLY_ERASE constexpr boxed_value(boxed_value&& other) noexcept = default; FOLLY_ERASE constexpr boxed_value& operator=(boxed_value&& other) noexcept = default; FOLLY_ERASE ~boxed_value() = default; FOLLY_ERASE constexpr boxed_value(const boxed_value& other) : ptr_(other.ptr_.copy()) {} FOLLY_ERASE constexpr boxed_value& operator=(const boxed_value& other) { ptr_ = other.ptr_.copy(); return *this; } FOLLY_ERASE constexpr explicit boxed_value(std::unique_ptr uptr) noexcept : ptr_(std::move(uptr)) {} FOLLY_ERASE constexpr bool has_value() const noexcept { return ptr_ != nullptr; } FOLLY_ERASE constexpr const T& value() const noexcept { return *ptr_; } FOLLY_ERASE constexpr T& mut() & { return ensureMutable(); } FOLLY_ERASE constexpr T&& mut() && { return std::move(ensureMutable()); } FOLLY_ERASE constexpr const T& operator*() const noexcept { return value(); } FOLLY_ERASE constexpr const T* operator->() const noexcept { return &value(); } FOLLY_ERASE constexpr explicit operator bool() const noexcept { return has_value(); } FOLLY_ERASE constexpr const T& ensure() noexcept { if (!has_value()) { reset(std::make_unique()); } return value(); } FOLLY_ERASE constexpr void reset() noexcept { ptr_.reset(); } FOLLY_ERASE constexpr void reset(std::unique_ptr p) noexcept { ptr_.reset(std::move(p)); } FOLLY_ERASE constexpr T* get() const noexcept { return ptr_.get(); } private: FOLLY_ERASE explicit constexpr boxed_value(const T* p) : ptr_(boxed_ptr::fromStaticConstant(p)) {} FOLLY_ERASE constexpr void checkHasValue() { if (ptr_ == nullptr) { folly::throw_exception( "Trying to dereference a nullptr."); } } FOLLY_ERASE constexpr T& ensureMutable() { checkHasValue(); return *ptr_.mut(); } friend void swap(boxed_value& lhs, boxed_value& rhs) noexcept { using std::swap; swap(lhs.ptr_, rhs.ptr_); } // TODO(dokwon): Add std::nullopt_t comparison when we support optional intern // boxed field. friend bool operator<(const boxed_value& lhs, const T& rhs) { return lhs ? *lhs < rhs : true; } friend bool operator==(const boxed_value& lhs, const T& rhs) { return lhs ? *lhs == rhs : false; } friend bool operator>(const boxed_value& lhs, const T& rhs) { return lhs ? rhs < *lhs : false; } friend bool operator<(const boxed_value& lhs, const boxed_value& rhs) { return rhs ? lhs < *rhs : false; } friend bool operator==(const boxed_value& lhs, const boxed_value& rhs) { // Use pointer comparison for short cut. if (lhs.ptr_ == rhs.ptr_) { return true; } return rhs ? lhs == *rhs : false; } boxed_ptr ptr_; }; } // namespace detail } // namespace thrift } // namespace apache #undef THRIFT_CONSTEXPR_IF_NOT_MSVC