


//
// SharedPointer
//

record RefCounted[T] (
    refCount:Int,
    value:T,
);

record SharedPointer[T] (
    ptr: Pointer[RefCounted[T]]
);

[T]
overload RegularRecord?(static SharedPointer[T]) = false;



//
// new
//

[T]
alias new(x:T) {
    var ptr = allocateRawMemory(RefCounted[T], SizeT(1));
    ptr^.refCount = 1;
    try {
        ptr^.value <-- x;
    }
    catch (e) {
        freeRawMemory(ptr);
        throw e;
    }
    return SharedPointer(ptr);
}

alias newShared = new;


//
// constructors, moveUnsafe, resetUnsafe, assign, destroy
//

[T]
inline overload SharedPointer[T]() {
    return SharedPointer(null(RefCounted[T]));
}

[T]
inline overload SharedPointer[T](src:SharedPointer[T]) {
    if (not null?(src))
        src.ptr^.refCount += 1;
    return SharedPointer(src.ptr);
}

[T]
overload BitwiseMovedType?(static SharedPointer[T]) = true;

[T]
inline overload resetUnsafe(p : SharedPointer[T]) {
    p <-- SharedPointer[T]();
}

[T]
inline overload assign(ref dest:SharedPointer[T], ref src:SharedPointer[T]) {
    if (dest.ptr == src.ptr)
        return;
    dest = SharedPointer[T](src);
}

[T]
inline overload destroy(p : SharedPointer[T]) {
    if (not null?(p)) {
        p.ptr^.refCount -= 1;
        if (p.ptr^.refCount == 0) {
            destroy(p.ptr^.value);
            freeRawMemory(p.ptr);
        }
    }
}



//
// dereference, null?, nullShared
//

[T]
inline overload dereference(p : SharedPointer[T]) = ref p.ptr^.value;

[T]
inline overload null?(p : SharedPointer[T]) = null?(p.ptr);

[T]
inline nullShared(static T) = SharedPointer[T]();



//
// equality
//
[T]
inline overload equals?(p: SharedPointer[T], q: SharedPointer[T])
    = p.ptr == q.ptr;
