#ifndef SASS_MEMORY_SHARED_PTR_H
#define SASS_MEMORY_SHARED_PTR_H
#include "sass/base.h"
#include "../sass.hpp"
#include "allocator.hpp"
#include <cstddef>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
// https://lokiastari.com/blog/2014/12/30/c-plus-plus-by-example-smart-pointer/index.html
// https://lokiastari.com/blog/2015/01/15/c-plus-plus-by-example-smart-pointer-part-ii/index.html
// https://lokiastari.com/blog/2015/01/23/c-plus-plus-by-example-smart-pointer-part-iii/index.html
namespace Sass {
// Forward declaration
class SharedPtr;
///////////////////////////////////////////////////////////////////////////////
// Use macros for the allocation task, since overloading operator `new`
// has been proven to be flaky under certain compilers (see comment below).
///////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_SHARED_PTR
#define SASS_MEMORY_NEW(Class, ...) \
((Class*)(new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \
#define SASS_MEMORY_COPY(obj) \
((obj)->copy(__FILE__, __LINE__)) \
#define SASS_MEMORY_CLONE(obj) \
((obj)->clone(__FILE__, __LINE__)) \
#else
#define SASS_MEMORY_NEW(Class, ...) \
new Class(__VA_ARGS__) \
#define SASS_MEMORY_COPY(obj) \
((obj)->copy()) \
#define SASS_MEMORY_CLONE(obj) \
((obj)->clone()) \
#endif
// SharedObj is the base class for all objects that can be stored as a shared object
// It adds the reference counter and other values directly to the objects
// This gives a slight overhead when directly used as a stack object, but has some
// advantages for our code. It is safe to create two shared pointers from the same
// objects, as the "control block" is directly attached to it. This would lead
// to undefined behavior with std::shared_ptr. This also avoids the need to
// allocate additional control blocks and/or the need to dereference two
// pointers on each operation. This can be optimized in `std::shared_ptr`
// too by using `std::make_shared` (where the control block and the actual
// object are allocated in one continuous memory block via one single call).
class SharedObj {
public:
SharedObj() : refcount(0), detached(false) {
#ifdef DEBUG_SHARED_PTR
if (taint) all.push_back(this);
#endif
}
virtual ~SharedObj() {
#ifdef DEBUG_SHARED_PTR
for (size_t i = 0; i < all.size(); i++) {
if (all[i] == this) {
all.erase(all.begin() + i);
break;
}
}
#endif
}
#ifdef DEBUG_SHARED_PTR
static void dumpMemLeaks();
SharedObj* trace(sass::string file, size_t line) {
this->file = file;
this->line = line;
return this;
}
sass::string getDbgFile() { return file; }
size_t getDbgLine() { return line; }
void setDbg(bool dbg) { this->dbg = dbg; }
size_t getRefCount() const { return refcount; }
#endif
static void setTaint(bool val) { taint = val; }
#ifdef SASS_CUSTOM_ALLOCATOR
inline void* operator new(size_t nbytes) {
return allocateMem(nbytes);
}
inline void operator delete(void* ptr) {
return deallocateMem(ptr);
}
#endif
virtual sass::string to_string() const = 0;
protected:
friend class SharedPtr;
friend class Memory_Manager;
size_t refcount;
bool detached;
static bool taint;
#ifdef DEBUG_SHARED_PTR
sass::string file;
size_t line;
bool dbg = false;
static sass::vector<SharedObj*> all;
#endif
};
// SharedPtr is a intermediate (template-less) base class for SharedImpl.
// ToDo: there should be a way to include this in SharedImpl and to get
// ToDo: rid of all the static_cast that are now needed in SharedImpl.
class SharedPtr {
public:
SharedPtr() : node(nullptr) {}
SharedPtr(SharedObj* ptr) : node(ptr) {
incRefCount();
}
SharedPtr(const SharedPtr& obj) : SharedPtr(obj.node) {}
~SharedPtr() {
decRefCount();
}
SharedPtr& operator=(SharedObj* other_node) {
if (node != other_node) {
decRefCount();
node = other_node;
incRefCount();
} else if (node != nullptr) {
node->detached = false;
}
return *this;
}
SharedPtr& operator=(const SharedPtr& obj) {
return *this = obj.node;
}
// Prevents all SharedPtrs from freeing this node until it is assigned to another SharedPtr.
SharedObj* detach() {
if (node != nullptr) node->detached = true;
#ifdef DEBUG_SHARED_PTR
if (node->dbg) {
std::cerr << "DETACHING NODE\n";
}
#endif
return node;
}
SharedObj* obj() const { return node; }
SharedObj* operator->() const { return node; }
bool isNull() const { return node == nullptr; }
operator bool() const { return node != nullptr; }
protected:
SharedObj* node;
void decRefCount() {
if (node == nullptr) return;
--node->refcount;
#ifdef DEBUG_SHARED_PTR
if (node->dbg) std::cerr << "- " << node << " X " << node->refcount << " (" << this << ") " << "\n";
#endif
if (node->refcount == 0 && !node->detached) {
#ifdef DEBUG_SHARED_PTR
if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";
#endif
delete node;
}
else if (node->refcount == 0) {
#ifdef DEBUG_SHARED_PTR
if (node->dbg) std::cerr << "NODE EVAEDED DELETE " << node << "\n";
#endif
}
}
void incRefCount() {
if (node == nullptr) return;
node->detached = false;
++node->refcount;
#ifdef DEBUG_SHARED_PTR
if (node->dbg) std::cerr << "+ " << node << " X " << node->refcount << " (" << this << ") " << "\n";
#endif
}
};
template <class T>
class SharedImpl : private SharedPtr {
public:
SharedImpl() : SharedPtr(nullptr) {}
template <class U>
SharedImpl(U* node) :
SharedPtr(static_cast<T*>(node)) {}
template <class U>
SharedImpl(const SharedImpl<U>& impl) :
SharedImpl(impl.ptr()) {}
template <class U>
SharedImpl<T>& operator=(U *rhs) {
return static_cast<SharedImpl<T>&>(
SharedPtr::operator=(static_cast<T*>(rhs)));
}
template <class U>
SharedImpl<T>& operator=(const SharedImpl<U>& rhs) {
return static_cast<SharedImpl<T>&>(
SharedPtr::operator=(static_cast<const SharedImpl<T>&>(rhs)));
}
operator sass::string() const {
if (node) return node->to_string();
return "null";
}
using SharedPtr::isNull;
using SharedPtr::operator bool;
operator T*() const { return static_cast<T*>(this->obj()); }
operator T&() const { return *static_cast<T*>(this->obj()); }
T& operator* () const { return *static_cast<T*>(this->obj()); };
T* operator-> () const { return static_cast<T*>(this->obj()); };
T* ptr () const { return static_cast<T*>(this->obj()); };
T* detach() { return static_cast<T*>(SharedPtr::detach()); }
};
// Comparison operators, based on:
// https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp
template<class T1, class T2>
bool operator==(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
return x.ptr() == y.ptr();
}
template<class T1, class T2>
bool operator!=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
return x.ptr() != y.ptr();
}
template<class T1, class T2>
bool operator<(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
using CT = typename std::common_type<T1*, T2*>::type;
return std::less<CT>()(x.get(), y.get());
}
template<class T1, class T2>
bool operator<=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
return !(y < x);
}
template<class T1, class T2>
bool operator>(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
return y < x;
}
template<class T1, class T2>
bool operator>=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {
return !(x < y);
}
template <class T>
bool operator==(const SharedImpl<T>& x, std::nullptr_t) noexcept {
return x.isNull();
}
template <class T>
bool operator==(std::nullptr_t, const SharedImpl<T>& x) noexcept {
return x.isNull();
}
template <class T>
bool operator!=(const SharedImpl<T>& x, std::nullptr_t) noexcept {
return !x.isNull();
}
template <class T>
bool operator!=(std::nullptr_t, const SharedImpl<T>& x) noexcept {
return !x.isNull();
}
template <class T>
bool operator<(const SharedImpl<T>& x, std::nullptr_t) {
return std::less<T*>()(x.get(), nullptr);
}
template <class T>
bool operator<(std::nullptr_t, const SharedImpl<T>& y) {
return std::less<T*>()(nullptr, y.get());
}
template <class T>
bool operator<=(const SharedImpl<T>& x, std::nullptr_t) {
return !(nullptr < x);
}
template <class T>
bool operator<=(std::nullptr_t, const SharedImpl<T>& y) {
return !(y < nullptr);
}
template <class T>
bool operator>(const SharedImpl<T>& x, std::nullptr_t) {
return nullptr < x;
}
template <class T>
bool operator>(std::nullptr_t, const SharedImpl<T>& y) {
return y < nullptr;
}
template <class T>
bool operator>=(const SharedImpl<T>& x, std::nullptr_t) {
return !(x < nullptr);
}
template <class T>
bool operator>=(std::nullptr_t, const SharedImpl<T>& y) {
return !(nullptr < y);
}
} // namespace Sass
#endif