blob: 43b51996e4d7a79e5fc0af8911945ff4f665e0cd [file] [log] [blame]
// 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"
)
// listHead points to the head of an intrusive doubly-linked list.
//
// Prior to use, you must call init to store the offset of listNode fields.
//
// Every object in the list should be the same type.
type listHead struct {
obj unsafe.Pointer
initialized bool
nodeOffset uintptr
}
// init initializes the list head. off is the offset (via unsafe.Offsetof) of
// the listNode field in the objects in the list.
func (head *listHead) init(off uintptr) {
head.initialized = true
head.nodeOffset = off
}
// listNode is the linked list node for objects in a listHead list.
//
// listNode must be stored as a field in objects placed in the linked list. The
// offset of the field is registered via listHead.init.
//
// For example:
//
// type foo struct {
// val int
//
// node listNode
// }
//
// var fooHead listHead
// fooHead.init(unsafe.Offsetof(foo{}.node))
type listNode struct {
prev unsafe.Pointer
next unsafe.Pointer
}
func (head *listHead) getNode(p unsafe.Pointer) *listNode {
if !head.initialized {
throw("runtime: uninitialized listHead")
}
if p == nil {
return nil
}
return (*listNode)(unsafe.Add(p, head.nodeOffset))
}
// Returns true if the list is empty.
func (head *listHead) empty() bool {
return head.obj == nil
}
// Returns the head of the list without removing it.
func (head *listHead) head() unsafe.Pointer {
return head.obj
}
// Push p onto the front of the list.
func (head *listHead) 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 != nil {
headNode := head.getNode(head.obj)
headNode.prev = p
}
head.obj = p
}
// Pop removes the head of the list.
func (head *listHead) pop() unsafe.Pointer {
if head.obj == nil {
return nil
}
// Return the head of the list.
p := 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 = nil
// ... and the new head no longer has a prev.
if head.obj != nil {
headNode := head.getNode(head.obj)
headNode.prev = nil
}
return p
}
// Remove p from the middle of the list.
func (head *listHead) remove(p unsafe.Pointer) {
if head.obj == p {
// Use pop to ensure head is updated when removing the head.
head.pop()
return
}
pNode := head.getNode(p)
prevNode := head.getNode(pNode.prev)
nextNode := head.getNode(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 = nil
pNode.next = nil
}