/*- * Copyright 2021 Vsevolod Stakhov * * 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. */ #ifndef RSPAMD_LOCAL_SHARED_PTR_HXX #define RSPAMD_LOCAL_SHARED_PTR_HXX #pragma once #include #include // for std::swap #include // for std::size_t #include // for std::less /* * Smart pointers with no atomic refcounts to speed up Rspamd which is * apparently single threaded */ namespace rspamd { namespace detail { class ref_cnt { public: using refcount_t = int; constexpr auto add_shared() -> refcount_t { return ++ref_shared; } constexpr auto add_weak() -> refcount_t { return ++ref_weak; } constexpr auto release_shared() -> refcount_t { return --ref_shared; } constexpr auto release_weak() -> refcount_t { return --ref_weak; } constexpr auto shared_count() const -> refcount_t { return ref_shared; } constexpr auto weak_count() const -> refcount_t { return ref_weak; } virtual ~ref_cnt() { } virtual void dispose() = 0; private: refcount_t ref_weak = 0; refcount_t ref_shared = 1; }; template class obj_and_refcnt : public ref_cnt { private: typedef typename std::aligned_storage::value>::type storage_type; storage_type storage; bool initialized; virtual void dispose() override { if (initialized) { T *p = reinterpret_cast(&storage); p->~T(); initialized = false; } } public: template explicit obj_and_refcnt(Args &&...args) : initialized(true) { new (&storage) T(std::forward(args)...); } auto get(void) -> T * { if (initialized) { return reinterpret_cast(&storage); } return nullptr; } virtual ~obj_and_refcnt() = default; }; template> class ptr_and_refcnt : public ref_cnt { private: T *ptr; D deleter; virtual void dispose() override { deleter(ptr); ptr = nullptr; } public: explicit ptr_and_refcnt(T *_ptr, D &&d = std::default_delete()) : ptr(_ptr), deleter(std::move(d)) { } virtual ~ptr_and_refcnt() = default; }; }// namespace detail template class local_weak_ptr; template class local_shared_ptr { public: typedef T element_type; typedef local_weak_ptr weak_type; // Simplified comparing to libc++, no custom deleter and no rebind here // constructors: constexpr local_shared_ptr() noexcept : px(nullptr), cnt(nullptr) { } template::value, bool>::type = true> explicit local_shared_ptr(Y *p) : px(p), cnt(new detail::ptr_and_refcnt(p)) { } // custom deleter template::value, bool>::type = true> explicit local_shared_ptr(Y *p, D &&d) : px(p), cnt(new detail::ptr_and_refcnt(p, std::forward(d))) { } local_shared_ptr(const local_shared_ptr &r) noexcept : px(r.px), cnt(r.cnt) { if (cnt) { cnt->add_shared(); } } local_shared_ptr(local_shared_ptr &&r) noexcept : px(r.px), cnt(r.cnt) { r.px = nullptr; r.cnt = nullptr; } template explicit local_shared_ptr(const local_weak_ptr &r) : px(r.px), cnt(r.cnt) { if (cnt) { cnt->add_shared(); } } local_shared_ptr(std::nullptr_t) : local_shared_ptr() { } ~local_shared_ptr() { if (cnt) { if (cnt->release_shared() <= 0) { cnt->dispose(); if (cnt->weak_count() == 0) { delete cnt; } } } } // assignment: local_shared_ptr &operator=(const local_shared_ptr &r) noexcept { local_shared_ptr(r).swap(*this); return *this; } local_shared_ptr &operator=(local_shared_ptr &&r) noexcept { local_shared_ptr(std::move(r)).swap(*this); return *this; } // Mutators void swap(local_shared_ptr &r) noexcept { std::swap(this->cnt, r.cnt); std::swap(this->px, r.px); } void reset() noexcept { local_shared_ptr().swap(*this); } // Observers: T *get() const noexcept { return px; } T &operator*() const noexcept { return *px; } T *operator->() const noexcept { return px; } long use_count() const noexcept { if (cnt) { return cnt->shared_count(); } return 0; } bool unique() const noexcept { return use_count() == 1; } explicit operator bool() const noexcept { return px != nullptr; } template::value, bool>::type = true> auto operator==(const local_shared_ptr &other) const -> bool { return px == other.px; } template::value, bool>::type = true> auto operator<(const local_shared_ptr &other) const -> auto { return *px < *other.px; } private: T *px;// contained pointer detail::ref_cnt *cnt; template friend local_shared_ptr<_T> local_make_shared(Args &&...args); friend class local_weak_ptr; }; template local_shared_ptr local_make_shared(Args &&...args) { local_shared_ptr ptr; auto tmp_object = new detail::obj_and_refcnt(std::forward(args)...); ptr.px = tmp_object->get(); ptr.cnt = tmp_object; return ptr; } template class local_weak_ptr { public: typedef T element_type; // constructors constexpr local_weak_ptr() noexcept : px(nullptr), cnt(nullptr) { } template::value, bool>::type = true> local_weak_ptr(local_shared_ptr const &r) noexcept : px(r.px), cnt(r.cnt) { if (cnt) { cnt->add_weak(); } } local_weak_ptr(local_weak_ptr const &r) noexcept : px(r.px), cnt(r.cnt) { if (cnt) { cnt->add_weak(); } } local_weak_ptr(local_weak_ptr &&r) noexcept : px(r.px), cnt(r.cnt) { r.px = nullptr; r.cnt = nullptr; } ~local_weak_ptr() { if (cnt) { if (cnt->release_weak() <= 0 && cnt->shared_count() == 0) { delete cnt; } } } // assignment local_weak_ptr &operator=(local_weak_ptr const &r) noexcept { local_weak_ptr(r).swap(*this); return *this; } local_weak_ptr &operator=(local_shared_ptr const &r) noexcept { local_weak_ptr(r).swap(*this); return *this; } template::value, bool>::type = true> local_weak_ptr &operator=(local_weak_ptr const &r) noexcept { local_weak_ptr(r).swap(*this); return *this; } local_weak_ptr &operator=(local_weak_ptr &&r) noexcept { local_weak_ptr(std::move(r)).swap(*this); return *this; } // modifiers void swap(local_weak_ptr &r) noexcept { std::swap(this->cnt, r.cnt); std::swap(this->px, r.px); } void reset() noexcept { local_weak_ptr().swap(*this); } // observers long use_count() const noexcept { if (cnt) { return cnt->shared_count(); } return 0; } bool expired() const noexcept { if (cnt) { return cnt->shared_count() == 0; } return true; } local_shared_ptr lock() const noexcept { local_shared_ptr tmp; tmp.cnt = cnt; if (cnt) { cnt->add_shared(); tmp.px = px; } return tmp; } private: element_type *px; detail::ref_cnt *cnt; }; }// namespace rspamd /* Hashing stuff */ namespace std { template struct hash> { inline auto operator()(const rspamd::local_shared_ptr &p) const -> auto { if (!p) { throw std::logic_error("no hash for dangling pointer"); } return hash()(*p.get()); } }; template struct hash> { inline auto operator()(const rspamd::local_weak_ptr &p) const -> auto { if (!p) { throw std::logic_error("no hash for dangling pointer"); } return hash()(*p.get()); } }; template inline void swap(rspamd::local_shared_ptr &x, rspamd::local_shared_ptr &y) noexcept { x.swap(y); } template inline void swap(rspamd::local_weak_ptr &x, rspamd::local_weak_ptr &y) noexcept { x.swap(y); } }// namespace std #endif//RSPAMD_LOCAL_SHARED_PTR_HXX