import paged.*;


//
// SharedPointer
//

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

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

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


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

[T]
alias new(x:T) {
    var ptr = allocatePaged(RefCounted[T], SizeT(1));
    ptr^.refCount = 1;
    ptr^.value <-- x;
    return SharedPointer(ptr);
}

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

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

[T]
overload moveUnsafe(src:SharedPointer[T]) --> returned:SharedPointer[T] {
    returned.ptr = src.ptr;
}

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

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

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



//
// dereference
//

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



//
// null?, nullShared
//

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

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