Std compatibility
New to the concepts? See Tools — PolyNode first.
PolyNode embeds std.DoublyLinkedList.Node.
- No custom node type.
- No adapter.
- The intrusive links are the standard ones.
Batch operations speak ItemList
ItemList is the toolkit's list type. It holds the same std list inside and
speaks ItemHandle on the outside.
Mbox.close()Mbox.receive_batch()Pool.put_all()Pool.Hooks.on_put,Pool.Hooks.on_close
Walk results with popFirst():
var batch: polynode.ItemList = try mbx.receive_batch();
while (batch.popFirst()) |ih| {
const ev: *Event = Event.EventPolyHelper.fromPoly(ih) orelse return error.WrongTag;
// ...
}
One step. popFirst gives back an ItemHandle, not a list node, so
@fieldParentPtr never appears in your code.
What ItemList adds
std.DoublyLinkedList checks nothing. ItemList is where it is checked.
pub fn append(self: *ItemList, ih: ItemHandle) void
pub fn prepend(self: *ItemList, ih: ItemHandle) void
pub fn appendFromSlot(self: *ItemList, slot: *Slot) void
pub fn prependFromSlot(self: *ItemList, slot: *Slot) void
pub fn insertAfter(self: *ItemList, existing: ItemHandle, ih: ItemHandle) void
pub fn insertBefore(self: *ItemList, existing: ItemHandle, ih: ItemHandle) void
pub fn popFirst(self: *ItemList) ?ItemHandle
pub fn popLast(self: *ItemList) ?ItemHandle
pub fn remove(self: *ItemList, ih: ItemHandle) void
pub fn first(self: *const ItemList) ?ItemHandle
pub fn last(self: *const ItemList) ?ItemHandle
pub fn isEmpty(self: *const ItemList) bool
pub fn len(self: *const ItemList) usize
pub fn iterator(self: *const ItemList) Iterator
pub fn concat(self: *ItemList, other: *ItemList) void
pub fn moveFromList(list: *std.DoublyLinkedList) ItemList
pub fn moveToList(self: *ItemList) std.DoublyLinkedList
-
popFirst,popLastandremoveall callpolynode.reset. The item comes back unlinked, ready for aSlotor another list. -
firstandlastlook without taking. A list of one returns the same item from both. -
lenforwards std's walk. O(n) — the mailbox and pool keep their own counters for that reason.
Taking one item out
remove takes an item from anywhere in the list — head, middle, or tail:
This list must hold the item. Use it instead of reaching through _list.
Inserting from a Slot
append takes an ItemHandle, so it has no Slot to clear. appendFromSlot
does:
prependFromSlotis the same at the front.- Both assert the Slot holds an item. An insert is not a
defertarget. - Use
append/prependfor a stack item, which has no Slot to empty.
Every other transfer in the toolkit empties its source. Now ItemList does
too, so you write no slot = null line after an insert.
The inserts check twice
Under a safety build every insert asserts twice on the item going in: the list
does not already hold it, and polynode.is_linked is false. insertAfter and
insertBefore also assert existing is in the list.
Neither check is complete, and they are blind to opposite cases:
| check | sees | blind to |
|---|---|---|
| the list walk | this list, including a list of one | any other list |
is_linked |
any list | the list holding the item alone |
- O(n) per insert under safety builds. Nothing outside them.
- An item alone in a different list still passes both.
concat asserts its two arguments are different lists, and returns early if
they are the same one. The assert is unreachable outside safety builds, and
std.DoublyLinkedList.concatByMoving would ring the items and then clear the
header they are reachable through — the list would come back empty with every
item in it lost.
moveFromList asserts the std header it is handed is consistent: first and
last both null, or both set.
popFirst clears the links
std.DoublyLinkedList.popFirst() leaves prev/next pointing at the old
neighbours. ItemList.popFirst() calls polynode.reset before it returns.
- A popped
ItemHandleis never linked. polynode.is_linkedon it is false.- It goes straight into a
Slot, or intoMbox.send, with no extra step.
polynode.reset is still public. You need it only when you take items out
through ItemList._list — see below.
Crossing to a plain std list
Both directions are a move, never a copy. Copying a list header aliases it, and
the first popFirst on either header corrupts the other.
var list = polynode.ItemList.moveFromList(&raw); // raw is left empty
var raw_again = list.moveToList(); // list is left empty
Both are O(1) — a header value copy, no walk.
The _list field
ItemList._list is the plain std list this ItemList holds. Tests that
manipulate raw links use it — the layout is what those tests check.
Use the ItemList methods instead. To take one item out, remove is the
method — it calls polynode.reset for you. Take an item out through the field
and it still points at the list it left, and polynode.reset becomes yours to
call.