// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package weak

import (
	"internal/abi"
	"runtime"
	"unsafe"
)

// Pointer is a weak pointer to a value of type T.
//
// Just like regular pointers, Pointer may reference any part of an
// object, such as a field of a struct or an element of an array.
// Objects that are only pointed to by weak pointers are not considered
// reachable, and once the object becomes unreachable, [Pointer.Value]
// may return nil.
//
// The primary use-cases for weak pointers are for implementing caches,
// canonicalization maps (like the unique package), and for tying together
// the lifetimes of separate values (for example, through a map with weak
// keys).
//
// Two Pointer values compare equal if and only if the pointers from which they
// were created compare equal.
// This property is maintained even after the object referenced by the pointer
// used to create a weak reference is reclaimed.
// If multiple weak pointers are made to different offsets within the same object
// (for example, pointers to different fields of the same struct), those pointers
// will not compare equal.
// In other words, weak pointers map to objects and offsets within those
// objects, not plain addresses.
// If a weak pointer is created from an object that becomes unreachable, but is
// then resurrected due to a finalizer, that weak pointer will not compare equal
// with weak pointers created after the resurrection.
//
// Calling [Make] with a nil pointer returns a weak pointer whose [Pointer.Value]
// always returns nil. The zero value of a Pointer behaves as if it were created
// by passing nil to [Make] and compares equal with such pointers.
//
// [Pointer.Value] is not guaranteed to eventually return nil.
// [Pointer.Value] may return nil as soon as the object becomes
// unreachable.
// Values stored in global variables, or that can be found by tracing
// pointers from a global variable, are reachable. A function argument or
// receiver may become unreachable at the last point where the function
// mentions it. To ensure [Pointer.Value] does not return nil,
// pass a pointer to the object to the [runtime.KeepAlive] function after
// the last point where the object must remain reachable.
//
// Note that because [Pointer.Value] is not guaranteed to eventually return
// nil, even after an object is no longer referenced, the runtime is allowed to
// perform a space-saving optimization that batches objects together in a single
// allocation slot. The weak pointer for an unreferenced object in such an
// allocation may never become nil if it always exists in the same batch as a
// referenced object. Typically, this batching only happens for tiny
// (on the order of 16 bytes or less) and pointer-free objects.
type Pointer[T any] struct {
	// Mention T in the type definition to prevent conversions
	// between Pointer types, like we do for sync/atomic.Pointer.
	_ [0]*T
	u unsafe.Pointer
}

// Make creates a weak pointer from a pointer to some value of type T.
func Make[T any](ptr *T) Pointer[T] {
	// Explicitly force ptr to escape to the heap.
	ptr = abi.Escape(ptr)

	var u unsafe.Pointer
	if ptr != nil {
		u = runtime_registerWeakPointer(unsafe.Pointer(ptr))
	}
	runtime.KeepAlive(ptr)
	return Pointer[T]{u: u}
}

// Value returns the original pointer used to create the weak pointer.
// It returns nil if the value pointed to by the original pointer was reclaimed by
// the garbage collector.
// If a weak pointer points to an object with a finalizer, then Value will
// return nil as soon as the object's finalizer is queued for execution.
func (p Pointer[T]) Value() *T {
	if p.u == nil {
		return nil
	}
	return (*T)(runtime_makeStrongFromWeak(p.u))
}

// Implemented in runtime.

//go:linkname runtime_registerWeakPointer
func runtime_registerWeakPointer(unsafe.Pointer) unsafe.Pointer

//go:linkname runtime_makeStrongFromWeak
func runtime_makeStrongFromWeak(unsafe.Pointer) unsafe.Pointer
