mirror of
https://github.com/ziglang/zig.git
synced 2025-12-31 18:43:18 +00:00
Merge pull request #2424 from daurnimator/single-linked-list
Add SinglyLinkedList
This commit is contained in:
commit
12b2950bf2
@ -160,7 +160,7 @@ pub const Compilation = struct {
|
||||
/// it uses an optional pointer so that tombstone removals are possible
|
||||
fn_link_set: event.Locked(FnLinkSet),
|
||||
|
||||
pub const FnLinkSet = std.LinkedList(?*Value.Fn);
|
||||
pub const FnLinkSet = std.TailQueue(?*Value.Fn);
|
||||
|
||||
windows_subsystem_windows: bool,
|
||||
windows_subsystem_console: bool,
|
||||
|
||||
@ -186,7 +186,7 @@ pub const Value = struct {
|
||||
/// Path to the object file that contains this function
|
||||
containing_object: Buffer,
|
||||
|
||||
link_set_node: *std.LinkedList(?*Value.Fn).Node,
|
||||
link_set_node: *std.TailQueue(?*Value.Fn).Node,
|
||||
|
||||
/// Creates a Fn value with 1 ref
|
||||
/// Takes ownership of symbol_name
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn Queue(comptime T: type) type {
|
||||
mutex: std.Mutex,
|
||||
|
||||
pub const Self = @This();
|
||||
pub const Node = std.LinkedList(T).Node;
|
||||
pub const Node = std.TailQueue(T).Node;
|
||||
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
|
||||
@ -13,7 +13,7 @@ const BufMap = std.BufMap;
|
||||
const Buffer = std.Buffer;
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const LinkedList = std.LinkedList;
|
||||
const TailQueue = std.TailQueue;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
pub const ChildProcess = struct {
|
||||
@ -48,7 +48,7 @@ pub const ChildProcess = struct {
|
||||
pub cwd: ?[]const u8,
|
||||
|
||||
err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,
|
||||
llnode: if (os.windows.is_the_target) void else LinkedList(*ChildProcess).Node,
|
||||
llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node,
|
||||
|
||||
pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||
|
||||
os.ChangeCurDirError || windows.CreateProcessError;
|
||||
@ -388,7 +388,7 @@ pub const ChildProcess = struct {
|
||||
|
||||
self.pid = pid;
|
||||
self.err_pipe = err_pipe;
|
||||
self.llnode = LinkedList(*ChildProcess).Node.init(self);
|
||||
self.llnode = TailQueue(*ChildProcess).Node.init(self);
|
||||
self.term = null;
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) {
|
||||
|
||||
@ -19,7 +19,7 @@ pub const Server = struct {
|
||||
waiting_for_emfile_node: PromiseNode,
|
||||
listen_resume_node: event.Loop.ResumeNode,
|
||||
|
||||
const PromiseNode = std.LinkedList(promise).Node;
|
||||
const PromiseNode = std.TailQueue(promise).Node;
|
||||
|
||||
pub fn init(loop: *Loop) Server {
|
||||
// TODO can't initialize handler coroutine here because we need well defined copy elision
|
||||
|
||||
11
std/heap.zig
11
std/heap.zig
@ -347,10 +347,10 @@ pub const ArenaAllocator = struct {
|
||||
pub allocator: Allocator,
|
||||
|
||||
child_allocator: *Allocator,
|
||||
buffer_list: std.LinkedList([]u8),
|
||||
buffer_list: std.SinglyLinkedList([]u8),
|
||||
end_index: usize,
|
||||
|
||||
const BufNode = std.LinkedList([]u8).Node;
|
||||
const BufNode = std.SinglyLinkedList([]u8).Node;
|
||||
|
||||
pub fn init(child_allocator: *Allocator) ArenaAllocator {
|
||||
return ArenaAllocator{
|
||||
@ -359,7 +359,7 @@ pub const ArenaAllocator = struct {
|
||||
.shrinkFn = shrink,
|
||||
},
|
||||
.child_allocator = child_allocator,
|
||||
.buffer_list = std.LinkedList([]u8).init(),
|
||||
.buffer_list = std.SinglyLinkedList([]u8).init(),
|
||||
.end_index = 0,
|
||||
};
|
||||
}
|
||||
@ -387,10 +387,9 @@ pub const ArenaAllocator = struct {
|
||||
const buf_node = &buf_node_slice[0];
|
||||
buf_node.* = BufNode{
|
||||
.data = buf,
|
||||
.prev = null,
|
||||
.next = null,
|
||||
};
|
||||
self.buffer_list.append(buf_node);
|
||||
self.buffer_list.prepend(buf_node);
|
||||
self.end_index = 0;
|
||||
return buf_node;
|
||||
}
|
||||
@ -398,7 +397,7 @@ pub const ArenaAllocator = struct {
|
||||
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
|
||||
|
||||
var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment);
|
||||
var cur_node = if (self.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
|
||||
while (true) {
|
||||
const cur_buf = cur_node.data[@sizeOf(BufNode)..];
|
||||
const addr = @ptrToInt(cur_buf.ptr) + self.end_index;
|
||||
|
||||
@ -5,8 +5,192 @@ const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
/// Generic doubly linked list.
|
||||
pub fn LinkedList(comptime T: type) type {
|
||||
/// A singly-linked list is headed by a single forward pointer. The elements
|
||||
/// are singly linked for minimum space and pointer manipulation overhead at
|
||||
/// the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
/// added to the list after an existing element or at the head of the list.
|
||||
/// A singly-linked list may only be traversed in the forward direction.
|
||||
/// Singly-linked lists are ideal for applications with large datasets and
|
||||
/// few or no removals or for implementing a LIFO queue.
|
||||
pub fn SinglyLinkedList(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,
|
||||
|
||||
pub fn init(data: T) Node {
|
||||
return Node{
|
||||
.next = null,
|
||||
.data = data,
|
||||
};
|
||||
}
|
||||
|
||||
/// Insert a new node after the current one.
|
||||
///
|
||||
/// Arguments:
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn insertAfter(node: *Node, new_node: *Node) void {
|
||||
new_node.next = node.next;
|
||||
node.next = new_node;
|
||||
}
|
||||
|
||||
/// Remove a node from the list.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to the node to be removed.
|
||||
/// Returns:
|
||||
/// node removed
|
||||
pub fn removeNext(node: *Node) ?*Node {
|
||||
const next_node = node.next orelse return null;
|
||||
node.next = next_node.next;
|
||||
return next_node;
|
||||
}
|
||||
};
|
||||
|
||||
first: ?*Node,
|
||||
|
||||
/// Initialize a linked list.
|
||||
///
|
||||
/// Returns:
|
||||
/// An empty linked list.
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
.first = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Insert a new node after an existing one.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to a node in the list.
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void {
|
||||
node.insertAfter(new_node);
|
||||
}
|
||||
|
||||
/// Insert a new node at the head.
|
||||
///
|
||||
/// Arguments:
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn prepend(list: *Self, new_node: *Node) void {
|
||||
new_node.next = list.first;
|
||||
list.first = new_node;
|
||||
}
|
||||
|
||||
/// Remove a node from the list.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to the node to be removed.
|
||||
pub fn remove(list: *Self, node: *Node) void {
|
||||
if (list.first == node) {
|
||||
list.first = node.next;
|
||||
} else {
|
||||
var current_elm = list.first.?;
|
||||
while (current_elm.next != node) {
|
||||
current_elm = current_elm.next.?;
|
||||
}
|
||||
current_elm.next = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove and return the first node in the list.
|
||||
///
|
||||
/// Returns:
|
||||
/// A pointer to the first node in the list.
|
||||
pub fn popFirst(list: *Self) ?*Node {
|
||||
const first = list.first orelse return null;
|
||||
list.first = first.next;
|
||||
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, allocator: *Allocator) !*Node {
|
||||
var node = try list.allocateNode(allocator);
|
||||
node.* = Node.init(data);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "basic SinglyLinkedList test" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list = SinglyLinkedList(u32).init();
|
||||
|
||||
var one = try list.createNode(1, allocator);
|
||||
var two = try list.createNode(2, allocator);
|
||||
var three = try list.createNode(3, allocator);
|
||||
var four = try list.createNode(4, allocator);
|
||||
var five = try list.createNode(5, allocator);
|
||||
defer {
|
||||
list.destroyNode(one, allocator);
|
||||
list.destroyNode(two, allocator);
|
||||
list.destroyNode(three, allocator);
|
||||
list.destroyNode(four, allocator);
|
||||
list.destroyNode(five, allocator);
|
||||
}
|
||||
|
||||
list.prepend(two); // {2}
|
||||
list.insertAfter(two, five); // {2, 5}
|
||||
list.prepend(one); // {1, 2, 5}
|
||||
list.insertAfter(two, three); // {1, 2, 3, 5}
|
||||
list.insertAfter(three, four); // {1, 2, 3, 4, 5}
|
||||
|
||||
// Traverse forwards.
|
||||
{
|
||||
var it = list.first;
|
||||
var index: u32 = 1;
|
||||
while (it) |node| : (it = node.next) {
|
||||
testing.expect(node.data == index);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
_ = list.popFirst(); // {2, 3, 4, 5}
|
||||
_ = list.remove(five); // {2, 3, 4}
|
||||
_ = two.removeNext(); // {2, 4}
|
||||
|
||||
testing.expect(list.first.?.data == 2);
|
||||
testing.expect(list.first.?.next.?.data == 4);
|
||||
testing.expect(list.first.?.next.?.next == null);
|
||||
}
|
||||
|
||||
/// A tail queue is headed by a pair of pointers, one to the head of the
|
||||
/// list and the other to the tail of the list. The elements are doubly
|
||||
/// linked so that an arbitrary element can be removed without a need to
|
||||
/// traverse the list. New elements can be added to the list before or
|
||||
/// after an existing element, at the head of the list, or at the end of
|
||||
/// the list. A tail queue may be traversed in either direction.
|
||||
pub fn TailQueue(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
@ -219,9 +403,9 @@ pub fn LinkedList(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
test "basic linked list test" {
|
||||
test "basic TailQueue test" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list = LinkedList(u32).init();
|
||||
var list = TailQueue(u32).init();
|
||||
|
||||
var one = try list.createNode(1, allocator);
|
||||
var two = try list.createNode(2, allocator);
|
||||
@ -271,10 +455,10 @@ test "basic linked list test" {
|
||||
testing.expect(list.len == 2);
|
||||
}
|
||||
|
||||
test "linked list concatenation" {
|
||||
test "TailQueue concatenation" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list1 = LinkedList(u32).init();
|
||||
var list2 = LinkedList(u32).init();
|
||||
var list1 = TailQueue(u32).init();
|
||||
var list2 = TailQueue(u32).init();
|
||||
|
||||
var one = try list1.createNode(1, allocator);
|
||||
defer list1.destroyNode(one, allocator);
|
||||
|
||||
@ -7,17 +7,18 @@ pub const Buffer = @import("buffer.zig").Buffer;
|
||||
pub const BufferOutStream = @import("io.zig").BufferOutStream;
|
||||
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
pub const Mutex = @import("mutex.zig").Mutex;
|
||||
pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
|
||||
pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
|
||||
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
|
||||
pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
|
||||
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
|
||||
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
|
||||
pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
pub const SpinLock = @import("spinlock.zig").SpinLock;
|
||||
pub const ChildProcess = @import("child_process.zig").ChildProcess;
|
||||
pub const TailQueue = @import("linked_list.zig").TailQueue;
|
||||
pub const Thread = @import("thread.zig").Thread;
|
||||
|
||||
pub const atomic = @import("atomic.zig");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user