summaryrefslogtreecommitdiffstats
path: root/src/libutil/cxx/local_shared_ptr.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/cxx/local_shared_ptr.hxx')
-rw-r--r--src/libutil/cxx/local_shared_ptr.hxx440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/libutil/cxx/local_shared_ptr.hxx b/src/libutil/cxx/local_shared_ptr.hxx
new file mode 100644
index 0000000..78ed5ba
--- /dev/null
+++ b/src/libutil/cxx/local_shared_ptr.hxx
@@ -0,0 +1,440 @@
+/*-
+ * 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 <memory>
+#include <algorithm> // for std::swap
+#include <cstddef> // for std::size_t
+#include <functional>// 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 T>
+class obj_and_refcnt : public ref_cnt {
+private:
+ typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type storage_type;
+ storage_type storage;
+ bool initialized;
+ virtual void dispose() override
+ {
+ if (initialized) {
+ T *p = reinterpret_cast<T *>(&storage);
+ p->~T();
+ initialized = false;
+ }
+ }
+
+public:
+ template<typename... Args>
+ explicit obj_and_refcnt(Args &&...args)
+ : initialized(true)
+ {
+ new (&storage) T(std::forward<Args>(args)...);
+ }
+ auto get(void) -> T *
+ {
+ if (initialized) {
+ return reinterpret_cast<T *>(&storage);
+ }
+
+ return nullptr;
+ }
+ virtual ~obj_and_refcnt() = default;
+};
+
+template<class T, class D = typename std::default_delete<T>>
+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<T>())
+ : ptr(_ptr),
+ deleter(std::move(d))
+ {
+ }
+ virtual ~ptr_and_refcnt() = default;
+};
+
+}// namespace detail
+
+template<class T>
+class local_weak_ptr;
+
+template<class T>
+class local_shared_ptr {
+public:
+ typedef T element_type;
+ typedef local_weak_ptr<T> weak_type;
+
+ // Simplified comparing to libc++, no custom deleter and no rebind here
+ // constructors:
+ constexpr local_shared_ptr() noexcept
+ : px(nullptr), cnt(nullptr)
+ {
+ }
+
+ template<class Y, typename std::enable_if<
+ std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ explicit local_shared_ptr(Y *p)
+ : px(p), cnt(new detail::ptr_and_refcnt(p))
+ {
+ }
+
+ // custom deleter
+ template<class Y, class D, typename std::enable_if<std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ explicit local_shared_ptr(Y *p, D &&d)
+ : px(p), cnt(new detail::ptr_and_refcnt<Y, D>(p, std::forward<D>(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<class Y>
+ explicit local_shared_ptr(const local_weak_ptr<Y> &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<class Y, typename std::enable_if<
+ std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ auto operator==(const local_shared_ptr<Y> &other) const -> bool
+ {
+ return px == other.px;
+ }
+
+ template<class Y, typename std::enable_if<
+ std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ auto operator<(const local_shared_ptr<Y> &other) const -> auto
+ {
+ return *px < *other.px;
+ }
+
+private:
+ T *px;// contained pointer
+ detail::ref_cnt *cnt;
+
+ template<class _T, class... Args>
+ friend local_shared_ptr<_T> local_make_shared(Args &&...args);
+ friend class local_weak_ptr<T>;
+};
+
+template<class T, class... Args>
+local_shared_ptr<T> local_make_shared(Args &&...args)
+{
+ local_shared_ptr<T> ptr;
+ auto tmp_object = new detail::obj_and_refcnt<T>(std::forward<Args>(args)...);
+ ptr.px = tmp_object->get();
+ ptr.cnt = tmp_object;
+
+ return ptr;
+}
+
+template<class T>
+class local_weak_ptr {
+public:
+ typedef T element_type;
+
+ // constructors
+ constexpr local_weak_ptr() noexcept
+ : px(nullptr), cnt(nullptr)
+ {
+ }
+ template<class Y, typename std::enable_if<
+ std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ local_weak_ptr(local_shared_ptr<Y> 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<T> const &r) noexcept
+ {
+ local_weak_ptr(r).swap(*this);
+ return *this;
+ }
+
+ template<class Y, typename std::enable_if<
+ std::is_convertible<Y *, element_type *>::value, bool>::type = true>
+ local_weak_ptr &operator=(local_weak_ptr<Y> 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<T> lock() const noexcept
+ {
+ local_shared_ptr<T> 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<class T>
+struct hash<rspamd::local_shared_ptr<T>> {
+ inline auto operator()(const rspamd::local_shared_ptr<T> &p) const -> auto
+ {
+ if (!p) {
+ throw std::logic_error("no hash for dangling pointer");
+ }
+ return hash<T>()(*p.get());
+ }
+};
+template<class T>
+struct hash<rspamd::local_weak_ptr<T>> {
+ inline auto operator()(const rspamd::local_weak_ptr<T> &p) const -> auto
+ {
+ if (!p) {
+ throw std::logic_error("no hash for dangling pointer");
+ }
+ return hash<T>()(*p.get());
+ }
+};
+
+template<class T>
+inline void swap(rspamd::local_shared_ptr<T> &x, rspamd::local_shared_ptr<T> &y) noexcept
+{
+ x.swap(y);
+}
+
+template<class T>
+inline void swap(rspamd::local_weak_ptr<T> &x, rspamd::local_weak_ptr<T> &y) noexcept
+{
+ x.swap(y);
+}
+
+}// namespace std
+
+#endif//RSPAMD_LOCAL_SHARED_PTR_HXX