152 lines
4.8 KiB
Zig
152 lines
4.8 KiB
Zig
const std = @import("std");
|
|
|
|
/// DeltaQueue is a singly-linked list where each
|
|
/// node has a counter. Each counter is relative
|
|
/// to the previous.
|
|
///
|
|
/// Inspired by https://wiki.osdev.org/Blocking_Process
|
|
/// Based on std.SinglyLinkedList
|
|
pub fn DeltaQueue(comptime T: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
/// Node inside the linked list wrapping the actual data.
|
|
pub const Node = struct {
|
|
next: ?*Node,
|
|
data: T,
|
|
counter: u64,
|
|
|
|
pub fn init(data: T, counter: u64) Node {
|
|
return Node{
|
|
.next = null,
|
|
.data = data,
|
|
.counter = counter,
|
|
};
|
|
}
|
|
|
|
/// Insert a new node after the current one.
|
|
///
|
|
/// Arguments:
|
|
/// new_node: Pointer to the new node to insert.
|
|
fn insertAfter(node: *Node, new_node: *Node) void {
|
|
if (node.next) |after| {
|
|
std.debug.assert(new_node.counter <= after.counter); //sanity check
|
|
after.counter -= new_node.counter;
|
|
}
|
|
new_node.next = node.next;
|
|
node.next = new_node;
|
|
}
|
|
};
|
|
|
|
len: usize = 0,
|
|
first: ?*Node,
|
|
|
|
/// Initialize a delta queue.
|
|
///
|
|
/// Returns:
|
|
/// An empty linked list.
|
|
pub fn init() Self {
|
|
return Self{
|
|
.first = null,
|
|
};
|
|
}
|
|
|
|
/// Insert a node in the list
|
|
///
|
|
/// Arguments:
|
|
/// node: Pointer to a node in the list.
|
|
/// new_node: Pointer to the new node to insert.
|
|
pub fn insert(list: *Self, node: *Node) void {
|
|
list.len += 1;
|
|
|
|
var target: ?*Node = null;
|
|
var next: ?*Node = list.first;
|
|
while (true) {
|
|
if (next == null or node.counter <= next.?.counter) {
|
|
if (target) |tg| return tg.insertAfter(node);
|
|
return list.prepend(node);
|
|
}
|
|
if (target) |tg| node.counter -= tg.counter;
|
|
target = next;
|
|
next = target.?.next;
|
|
}
|
|
}
|
|
|
|
/// worst case is O(n)
|
|
/// Could be better with a different data structure
|
|
/// Example: case of large list with all counters at 0,
|
|
/// we need to traverse the whole list.
|
|
pub fn decrement(list: *Self, count: u64) void {
|
|
var it = list.first;
|
|
var i = count;
|
|
while (it) |node| : (it = node.next) {
|
|
if (node.counter >= i) {
|
|
node.counter -= i;
|
|
return;
|
|
}
|
|
i -= node.counter;
|
|
node.counter = 0;
|
|
}
|
|
}
|
|
|
|
/// Insert a new node at the head.
|
|
///
|
|
/// Arguments:
|
|
/// new_node: Pointer to the new node to insert.
|
|
fn prepend(list: *Self, new_node: *Node) void {
|
|
if (list.first) |after| {
|
|
std.debug.assert(new_node.counter <= after.counter); //sanity check
|
|
after.counter -= new_node.counter;
|
|
}
|
|
new_node.next = list.first;
|
|
list.first = new_node;
|
|
}
|
|
|
|
/// Remove and return the first node in the list
|
|
/// if its counter is 0.
|
|
///
|
|
/// Returns:
|
|
/// A pointer to the first node in the list.
|
|
pub fn popZero(list: *Self) ?*Node {
|
|
const first = list.first orelse return null;
|
|
if (first.counter != 0) return null;
|
|
list.first = first.next;
|
|
list.len -= 1;
|
|
return first;
|
|
}
|
|
|
|
/// Allocate a new node.
|
|
///
|
|
/// Arguments:
|
|
/// allocator: Dynamic memory allocator.
|
|
///
|
|
/// Returns:
|
|
/// A pointer to the new node.
|
|
pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
|
|
return allocator.create(Node);
|
|
}
|
|
|
|
/// Deallocate a node.
|
|
///
|
|
/// Arguments:
|
|
/// node: Pointer to the node to deallocate.
|
|
/// allocator: Dynamic memory allocator.
|
|
pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
|
|
allocator.destroy(node);
|
|
}
|
|
|
|
/// Allocate and initialize a node and its data.
|
|
///
|
|
/// Arguments:
|
|
/// data: The data to put inside the node.
|
|
/// allocator: Dynamic memory allocator.
|
|
///
|
|
/// Returns:
|
|
/// A pointer to the new node.
|
|
pub fn createNode(list: *Self, data: T, counter: u64, allocator: *Allocator) !*Node {
|
|
var node = try list.allocateNode(allocator);
|
|
node.* = Node.init(data, counter);
|
|
return node;
|
|
}
|
|
};
|
|
}
|