// 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 }