// Copyright 2025 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 runtime

import (
	"unsafe"
)

// The types in this file are exact copies of the types in list.go, but with
// unsafe.Pointer replaced with uintptr for use where write barriers must be
// avoided, such as uses of muintptr, puintptr, guintptr.
//
// Objects in these lists must be kept alive via another real reference.

// listHeadManual points to the head of an intrusive doubly-linked list of
// objects.
//
// Prior to use, you must call init to store the offset of listNodeManual fields.
//
// Every object in the list should be the same type.
type listHeadManual struct {
	obj uintptr

	initialized bool
	nodeOffset  uintptr
}

// init initializes the list head. off is the offset (via unsafe.Offsetof) of
// the listNodeManual field in the objects in the list.
func (head *listHeadManual) init(off uintptr) {
	head.initialized = true
	head.nodeOffset = off
}

// listNodeManual is the linked list node for objects in a listHeadManual list.
//
// listNodeManual must be stored as a field in objects placed in the linked list.
// The offset of the field is registered via listHeadManual.init.
//
// For example:
//
//	type foo struct {
//		val int
//
//		node listNodeManual
//	}
//
// var fooHead listHeadManual
// fooHead.init(unsafe.Offsetof(foo{}.node))
type listNodeManual struct {
	prev uintptr
	next uintptr
}

func (head *listHeadManual) getNode(p unsafe.Pointer) *listNodeManual {
	if !head.initialized {
		throw("runtime: uninitialized listHead")
	}

	if p == nil {
		return nil
	}
	return (*listNodeManual)(unsafe.Add(p, head.nodeOffset))
}

// Returns true if the list is empty.
func (head *listHeadManual) empty() bool {
	return head.obj == 0
}

// Returns the head of the list without removing it.
func (head *listHeadManual) head() unsafe.Pointer {
	return unsafe.Pointer(head.obj)
}

// Push p onto the front of the list.
func (head *listHeadManual) push(p unsafe.Pointer) {
	// p becomes the head of the list.

	// ... so p's next is the current head.
	pNode := head.getNode(p)
	pNode.next = head.obj

	// ... and the current head's prev is p.
	if head.obj != 0 {
		headNode := head.getNode(unsafe.Pointer(head.obj))
		headNode.prev = uintptr(p)
	}

	head.obj = uintptr(p)
}

// Pop removes the head of the list.
func (head *listHeadManual) pop() unsafe.Pointer {
	if head.obj == 0 {
		return nil
	}

	// Return the head of the list.
	p := unsafe.Pointer(head.obj)

	// ... so the new head is p's next.
	pNode := head.getNode(p)
	head.obj = pNode.next
	// p is no longer on the list. Clear next to remove unused references.
	// N.B. as the head, prev must already be nil.
	pNode.next = 0

	// ... and the new head no longer has a prev.
	if head.obj != 0 {
		headNode := head.getNode(unsafe.Pointer(head.obj))
		headNode.prev = 0
	}

	return p
}

// Remove p from the middle of the list.
func (head *listHeadManual) remove(p unsafe.Pointer) {
	if unsafe.Pointer(head.obj) == p {
		// Use pop to ensure head is updated when removing the head.
		head.pop()
		return
	}

	pNode := head.getNode(p)
	prevNode := head.getNode(unsafe.Pointer(pNode.prev))
	nextNode := head.getNode(unsafe.Pointer(pNode.next))

	// Link prev to next.
	if prevNode != nil {
		prevNode.next = pNode.next
	}
	// Link next to prev.
	if nextNode != nil {
		nextNode.prev = pNode.prev
	}

	pNode.prev = 0
	pNode.next = 0
}
