aboutsummaryrefslogblamecommitdiff
path: root/include/salticidae/ref.h
blob: 7239402ed975944e1827870b1214e1ed2692d55c (plain) (tree)































                                                                                  
                    









                                                               
 






                                                               
     










                                                

           



                                              
                            

     


                          

     

                                  

     









                                                      










                                                                            











                                                                        
                                                                         

















                                                                        
                                                                       

  



                                                                              


                          












































                                                  
                                                              
                                                             

                                
                    
           
           
 

                                                  
 
           












                                                        


                     









                                                   
                                            
                                 

     
                                       
                                            


                            
                        
                                                   
 
                                                     

  
 
                                
                                              

                                      




                                                    


                                                                  



                                                      

                                      
           



                                                    


                                                                    









                                            
                              

                                                   
 
           


                                                                     


                              










                                                    


                     









                                               



                                            

                                                   
                                            
                                

     
                                   



                                            

                                              



                                            
                                                 












                                            



                                      

                                                               










                                                                               

  


                                                                
                                











                                                                                           
                                                                                


                                                                  
                                                                                



                                                                        








                                                        
                                






























                                                                                           

                             


                         




                                                                            




                         
                                
                    
                                                                          
                                        
                             










                                                              


                                                                          





                                 
/**
 * Copyright (c) 2018 Cornell University.
 *
 * Author: Ted Yin <[email protected]>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef _SALTICIDAE_REF_H
#define _SALTICIDAE_REF_H

#include <atomic>
#include <functional>

namespace salticidae {

template<typename T>
struct default_delete {
    constexpr default_delete() = default;
    void operator()(T *ptr) const {
        static_assert(!std::is_void<T>::value,
                    "can't delete pointer to incomplete type");
        static_assert(sizeof(T) > 0,
                    "can't delete pointer to incomplete type");
        delete ptr;
    }
};

template<typename T>
struct default_delete<T[]> {
    constexpr default_delete() = default;
    void operator()(T *ptr) const {
        static_assert(sizeof(T) > 0,
                    "can't delete pointer to incomplete type");
        delete [] ptr;
    }
};

template<typename T, typename D>
class BoxObj;

template<typename T, typename D>
class _BoxObj {
    protected:
    using ptr_type = T *;
    ptr_type obj;
    constexpr _BoxObj(ptr_type obj): obj(obj) {}

    public:

    constexpr _BoxObj(): obj(nullptr) {}
    _BoxObj(const _BoxObj &other) = delete;
    _BoxObj(_BoxObj &&other): obj(other.obj) {
        other.obj = nullptr;
    }


    ~_BoxObj() {
        if (obj) D()(obj);
    }

    void swap(_BoxObj &other) {
        std::swap(obj, other.obj);
    }

    _BoxObj &operator=(const _BoxObj &other) = delete;
    _BoxObj &operator=(_BoxObj &&other) {
        if (this != &other)
        {
            _BoxObj tmp(std::move(other));
            tmp.swap(*this);
        }
        return *this;
    }

    T &operator *() const { return *obj; }
    T *get() const { return obj; }
    operator bool() const { return obj != nullptr; }
    bool operator==(const _BoxObj &other) const { return obj == other.obj; }
    bool operator!=(const _BoxObj &other) const { return obj != other.obj; }
    bool operator==(std::nullptr_t) const { return obj == nullptr; }
    bool operator!=(std::nullptr_t) const { return obj != nullptr; }
    bool operator<(const _BoxObj &other) const { return obj < other.obj; }
    bool operator>(const _BoxObj &other) const { return obj > other.obj; }
    bool operator<=(const _BoxObj &other) const { return obj <= other.obj; }
    bool operator>=(const _BoxObj &other) const { return obj >= other.obj; }
};

template<typename T, typename D = default_delete<T>>
class BoxObj: public _BoxObj<T, D> {
    using base_t = _BoxObj<T, D>;
    template<typename T__, typename D__, typename T_, typename D_>
    friend BoxObj<T__, D__> static_pointer_cast(BoxObj<T_, D_> &&other);

    public:
    BoxObj() = default;
    BoxObj(T *obj): base_t(obj) {}
    template<typename T_, typename =
                typename std::enable_if<!std::is_array<T_>::value>::type>
    BoxObj(BoxObj<T_> &&other): base_t(other) {}

    T *operator->() const { return base_t::obj; }
};

template<typename T, typename D>
class BoxObj<T[], D>: public _BoxObj<T, D> {
    using base_t = _BoxObj<T, D>;
    template<typename T__, typename D__, typename T_, typename D_>
    friend BoxObj<T__, D__> static_pointer_cast(BoxObj<T_, D_> &&other);

    public:
    BoxObj() = default;
    BoxObj(T obj[]): base_t(obj) {}
    template<typename T_>
    BoxObj(BoxObj<T_[]> &&other): base_t(std::move(other)) {}

    T &operator[](size_t idx) { return base_t::obj[idx]; }
    const T &operator[] (size_t idx) const { return base_t::obj[idx]; }
};

template<typename T, typename D = default_delete<T>, typename T_, typename D_>
BoxObj<T, D> static_pointer_cast(BoxObj<T_, D_> &&other) {
    BoxObj<T, D> box{};
    box.obj = static_cast<typename BoxObj<T, D>::ptr_type>(other.obj);
    return std::move(box);
}

struct _RCCtl {
    size_t ref_cnt;
    size_t weak_cnt;
    void add_ref() { ref_cnt++; }
    void add_weak() { weak_cnt++; }
    bool release_ref() {
        if (--ref_cnt) return false;
        if (weak_cnt) return true;
        delete this;
        return true;
    }
    void release_weak() {
        if (--weak_cnt) return;
        if (ref_cnt) return;
        delete this;
    }
    size_t get_cnt() { return ref_cnt; }
    _RCCtl(): ref_cnt(1), weak_cnt(0) {}
    ~_RCCtl() {}
};

struct _ARCCtl {
    std::atomic_size_t ref_cnt;
    std::atomic_size_t weak_cnt;
    std::atomic_uint8_t dcnt;
    void add_ref() { ref_cnt++; }
    void add_weak() { weak_cnt++; }
    bool release_ref() {
        dcnt++;
        if (--ref_cnt) { dcnt--; return false; }
        if (weak_cnt) { dcnt--; return true; }
        if (!--dcnt) delete this;
        return true;
    }
    void release_weak() {
        dcnt++;
        if (--weak_cnt) { dcnt--; return; }
        if (ref_cnt) { dcnt--; return; }
        if (!--dcnt) delete this;
    }
    size_t get_cnt() { return ref_cnt.load(); }
    _ARCCtl(): ref_cnt(1), weak_cnt(0), dcnt(0) {}
    ~_ARCCtl() {}
};

template<typename T, typename R, typename D> class _RcObjBase;
template<typename T, typename R, typename D> class RcObjBase;

template<typename T, typename R>
class _WeakObjBase {
    T *obj;
    R *ctl;

    template<typename T_, typename R_, typename D>
    friend class _RcObjBase;

    public:
    _WeakObjBase(): ctl(nullptr) {}

    void swap(_WeakObjBase &other) {
        std::swap(obj, other.obj);
        std::swap(ctl, other.ctl);
    }

    _WeakObjBase &operator=(const _WeakObjBase &other) {
        if (this != &other)
        {
            _WeakObjBase tmp(other);
            tmp.swap(*this);
        }
        return *this;
    }

    _WeakObjBase &operator=(_WeakObjBase &&other) {
        if (this != &other)
        {
            _WeakObjBase tmp(std::move(other));
            tmp.swap(*this);
        }
        return *this;
    }

    _WeakObjBase(const _WeakObjBase &other):
            obj(other.obj), ctl(other.ctl) {
        if (ctl) ctl->add_weak();
    }

    _WeakObjBase(_WeakObjBase &&other):
            obj(other.obj), ctl(other.ctl) {
        other.ctl = nullptr;
    }

    template<typename D>
    _WeakObjBase(const _RcObjBase<T, R, D> &other);

    ~_WeakObjBase() { if (ctl) ctl->release_weak(); }
};


template<typename T, typename R>
class WeakObjBase: public _WeakObjBase<T, R> {
    using base_t = _WeakObjBase<T, R>;
    friend std::hash<WeakObjBase>;
    public:
    WeakObjBase() = default;
    WeakObjBase(const WeakObjBase &other) = default;
    WeakObjBase(WeakObjBase &&other) = default;
    template<typename D>
    WeakObjBase(const RcObjBase<T, R, D> &other): base_t(other) {}
    WeakObjBase &operator=(const WeakObjBase &) = default;
    WeakObjBase &operator=(WeakObjBase &&) = default;
};

template<typename T, typename R>
class WeakObjBase<T[], R>: public _WeakObjBase<T, R> {
    using base_t = _WeakObjBase<T, R>;
    friend std::hash<WeakObjBase>;
    public:
    WeakObjBase() = default;
    WeakObjBase(const WeakObjBase &other) = default;
    WeakObjBase(WeakObjBase &&other) = default;
    template<typename D>
    WeakObjBase(const RcObjBase<T[], R, D> &other): base_t(other) {}
    WeakObjBase &operator=(const WeakObjBase &) = default;
    WeakObjBase &operator=(WeakObjBase &&) = default;
};


template<typename T, typename R, typename D>
class _RcObjBase {
    protected:
    using ptr_type = T *;
    ptr_type obj;
    R *ctl;

    friend _WeakObjBase<T, R>;
    template<typename T_, typename R_, typename D_>
    friend class _RcObjBase;

    public:
    _RcObjBase(): obj(nullptr), ctl(nullptr) {}
    _RcObjBase(ptr_type obj): obj(obj), ctl(new R()) {}
    _RcObjBase(BoxObj<T> &&box_ref): obj(box_ref.obj), ctl(new R()) {
        box_ref.obj = nullptr;
    }

    void swap(_RcObjBase &other) {
        std::swap(obj, other.obj);
        std::swap(ctl, other.ctl);
    }

    _RcObjBase &operator=(const _RcObjBase &other) {
        if (this != &other)
        {
            _RcObjBase tmp(other);
            tmp.swap(*this);
        }
        return *this;
    }

    _RcObjBase &operator=(_RcObjBase &&other) {
        if (this != &other)
        {
            _RcObjBase tmp(std::move(other));
            tmp.swap(*this);
        }
        return *this;
    }

    _RcObjBase(const _RcObjBase &other):
            obj(other.obj), ctl(other.ctl) {
        if (ctl) ctl->add_ref();
    }

    template<typename T_, typename D_>
    _RcObjBase(const _RcObjBase<T_, R, D_> &other):
            obj(other.obj), ctl(other.ctl) {
        if (ctl) ctl->add_ref();
    }

    _RcObjBase(_RcObjBase &&other):
            obj(other.obj), ctl(other.ctl) {
        other.ctl = nullptr;
    }

    template<typename T_, typename D_>
    _RcObjBase(_RcObjBase<T_, R, D_> &&other):
            obj(other.obj), ctl(other.ctl) {
        other.ctl = nullptr;
    }

    _RcObjBase(const _WeakObjBase<T, R> &other) {
        if (other.ctl && other.ctl->ref_cnt)
        {
            obj = other.obj;
            ctl = other.ctl;
            ctl->add_ref();
        }
        else
        {
            obj = nullptr;
            ctl = nullptr;
        }
    }

    ~_RcObjBase() {
        if (ctl && ctl->release_ref())
            D()(obj);
    }

    size_t get_cnt() const { return ctl ? ctl->get_cnt() : 0; }
    T &operator *() const { return *obj; }
    T *get() const { return obj; }
    operator bool() const { return obj != nullptr; }
    bool operator==(const _RcObjBase &other) const { return obj == other.obj; }
    bool operator!=(const _RcObjBase &other) const { return obj != other.obj; }
    bool operator==(std::nullptr_t) const { return obj == nullptr; }
    bool operator!=(std::nullptr_t) const { return obj != nullptr; }
    bool operator<(const _RcObjBase &other) const { return obj < other.obj; }
    bool operator>(const _RcObjBase &other) const { return obj > other.obj; }
    bool operator<=(const _RcObjBase &other) const { return obj <= other.obj; }
    bool operator>=(const _RcObjBase &other) const { return obj >= other.obj; }
};

template<typename T, typename R, typename D = default_delete<T>>
class RcObjBase: public _RcObjBase<T, R, D> {
    using base_t = _RcObjBase<T, R, D>;
    friend std::hash<RcObjBase>;
    template<typename T__, typename D__, typename T_, typename R_, typename D_>
    friend RcObjBase<T__, R_, D__> static_pointer_cast(const RcObjBase<T_, R_, D_> &other);
    template<typename T__, typename D__, typename T_, typename R_, typename D_>
    friend RcObjBase<T__, R_, D__> static_pointer_cast(RcObjBase<T_, R_, D_> &&other);

    public:
    T *operator->() const { return base_t::obj; }
    RcObjBase() = default;
    RcObjBase(T *obj): base_t(obj) {}
    RcObjBase(BoxObj<T> &&box_ref): base_t(std::move(box_ref)) {}

    template<typename T_, typename D_,
            typename = typename std::enable_if<!std::is_array<T_>::value>::type>
    RcObjBase(const RcObjBase<T_, R, D_> &other): base_t(other) {}

    template<typename T_, typename D_,
            typename = typename std::enable_if<!std::is_array<T_>::value>::type>
    RcObjBase(RcObjBase<T_, R, D_> &&other): base_t(std::move(other)) {}

    RcObjBase(const WeakObjBase<T, R> &other): base_t(other) {}

    RcObjBase(const RcObjBase &other) = default;
    RcObjBase(RcObjBase &&other) = default;
    RcObjBase &operator=(const RcObjBase &) = default;
    RcObjBase &operator=(RcObjBase &&) = default;
};

template<typename T, typename R, typename D>
class RcObjBase<T[], R, D>: public _RcObjBase<T, R, D> {
    using base_t = _RcObjBase<T, R, D>;
    friend std::hash<RcObjBase>;
    template<typename T__, typename D__, typename T_, typename R_, typename D_>
    friend RcObjBase<T__, R_, D__> static_pointer_cast(const RcObjBase<T_, R_, D_> &other);
    template<typename T__, typename D__, typename T_, typename R_, typename D_>
    friend RcObjBase<T__, R_, D__> static_pointer_cast(RcObjBase<T_, R_, D_> &&other);

    public:
    T &operator[](size_t idx) { return base_t::obj; }
    RcObjBase() = default;
    RcObjBase(T obj[]): base_t(obj) {}
    RcObjBase(BoxObj<T[]> &&box_ref): base_t(std::move(box_ref)) {}

    template<typename T_, typename D_>
    RcObjBase(const RcObjBase<T_[], R, D_> &other): base_t(other) {}

    template<typename T_, typename D_>
    RcObjBase(RcObjBase<T_[], R, D_> &&other): base_t(std::move(other)) {}

    RcObjBase(const WeakObjBase<T[], R> &other): base_t(other) {}

    RcObjBase(const RcObjBase &other) = default;
    RcObjBase(RcObjBase &&other) = default;
    RcObjBase &operator=(const RcObjBase &) = default;
    RcObjBase &operator=(RcObjBase &&) = default;
};


template<typename T, typename D = default_delete<T>,
        typename T_, typename R_, typename D_>
RcObjBase<T, R_, D> static_pointer_cast(const RcObjBase<T_, R_, D_> &other) {
    RcObjBase<T, R_, D> rc{};
    rc.obj = static_cast<typename RcObjBase<T, R_, D>::ptr_type>(other.obj);
    if ((rc.ctl = other.ctl))
        rc.ctl->add_ref();
    return std::move(rc);
}

template<typename T, typename D = default_delete<T>,
        typename T_, typename R_, typename D_>
RcObjBase<T, R_, D> static_pointer_cast(RcObjBase<T_, R_, D_> &&other) {
    RcObjBase<T, R_, D> rc{};
    rc.obj = static_cast<typename RcObjBase<T, R_, D>::ptr_type>(other.obj);
    rc.ctl = other.ctl;
    other.ctl = nullptr;
    return std::move(rc);
}

template<typename T, typename R>
template<typename D>
inline _WeakObjBase<T, R>::_WeakObjBase(const _RcObjBase<T, R, D> &other):
        obj(other.obj), ctl(other.ctl) {
    if (ctl) ctl->add_weak();
}

template<typename T> using RcObj = RcObjBase<T, _RCCtl>;
template<typename T> using WeakObj = WeakObjBase<T, _RCCtl>;

template<typename T> using ArcObj = RcObjBase<T, _ARCCtl>;
template<typename T> using AweakObj = WeakObjBase<T, _ARCCtl>;

}

namespace std {
    template<typename T, typename R, typename D>
    struct hash<salticidae::RcObjBase<T, R, D>> {
        size_t operator()(const salticidae::RcObjBase<T, R, D> &k) const {
            return (size_t)k.obj;
        }
    };
}

#endif