Skip to content

Commit 1d83966

Browse files
authored
Add #unshift, #pop and #pop? to Crystal::PointerLinkedList (#16287)
Adds methods to **add and remove from any end of the list**, while the current implementation only allows a FIFO behavior (add to tail, remove from head). For example in the [fdlock pull request](https://github.com/crystal-lang/crystal/pull/16209/files/904dd952f788160c49d48b5f3aca040069f669fe#r2429767257) I'd like to add new waiters to tail but add woken waiters to head (to give them some edge).
1 parent 4da79d8 commit 1d83966

File tree

2 files changed

+79
-2
lines changed

2 files changed

+79
-2
lines changed

spec/std/crystal/pointer_linked_list_spec.cr

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ describe Crystal::PointerLinkedList do
5555
end
5656
end
5757

58+
describe "unshift" do
59+
it "prepends the node into the list" do
60+
list = Crystal::PointerLinkedList(TestedObject).new
61+
62+
x = TestedObject.new 0
63+
y = TestedObject.new 1
64+
z = TestedObject.new 2
65+
66+
list.unshift pointerof(x)
67+
list.unshift pointerof(y)
68+
list.unshift pointerof(z)
69+
70+
ExpectOrderHelper.by_next(x, z, y, x)
71+
ExpectOrderHelper.by_previous(x, y, z, x)
72+
end
73+
end
74+
5875
describe "delete" do
5976
it "remove a node from list" do
6077
list = Crystal::PointerLinkedList(TestedObject).new
@@ -110,6 +127,40 @@ describe Crystal::PointerLinkedList do
110127
end
111128
end
112129

130+
describe "pop?" do
131+
it "remove and return the last element" do
132+
list = Crystal::PointerLinkedList(TestedObject).new
133+
134+
x = TestedObject.new 0
135+
y = TestedObject.new 1
136+
z = TestedObject.new 2
137+
w = TestedObject.new 3
138+
139+
list.push pointerof(x)
140+
list.push pointerof(y)
141+
list.push pointerof(z)
142+
list.push pointerof(w)
143+
144+
obj = list.pop?
145+
146+
typeof(obj).should eq(Pointer(TestedObject)?)
147+
148+
obj.should_not be_nil
149+
obj.should eq(pointerof(w))
150+
151+
ExpectOrderHelper.by_next(x, y, z, x)
152+
ExpectOrderHelper.by_previous(x, z, y, x)
153+
end
154+
155+
it "return nil if list is empty" do
156+
list = Crystal::PointerLinkedList(TestedObject).new
157+
158+
obj = list.pop?
159+
160+
obj.should be_nil
161+
end
162+
end
163+
113164
it "does each" do
114165
list = Crystal::PointerLinkedList(TestedObject).new
115166

src/crystal/pointer_linked_list.cr

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,18 @@ struct Crystal::PointerLinkedList(T)
3131
@head.null?
3232
end
3333

34-
# Appends a node to the tail of the list.
34+
# Prepends *node* to the head of the list.
35+
def unshift(node : Pointer(T)) : Nil
36+
if !empty?
37+
typeof(self).insert_impl node, @head.value.previous, @head
38+
else
39+
node.value.previous = node
40+
node.value.next = node
41+
end
42+
@head = node
43+
end
44+
45+
# Appends *node* to the tail of the list.
3546
def push(node : Pointer(T)) : Nil
3647
if empty?
3748
node.value.previous = node
@@ -42,7 +53,7 @@ struct Crystal::PointerLinkedList(T)
4253
end
4354
end
4455

45-
# Removes a node from the list.
56+
# Removes *node* from the list.
4657
def delete(node : Pointer(T)) : Nil
4758
_next = node.value.next
4859

@@ -68,6 +79,21 @@ struct Crystal::PointerLinkedList(T)
6879
shift { nil }
6980
end
7081

82+
# Removes and returns tail from the list, yields if empty.
83+
def pop(&)
84+
if !empty?
85+
h = @head
86+
t = (h.value.previous || h).tap { |t| delete(t) }
87+
else
88+
yield
89+
end
90+
end
91+
92+
# Removes and returns tail from the list, `nil` if empty.
93+
def pop?
94+
pop { nil }
95+
end
96+
7197
# Iterates the list.
7298
def each(&) : Nil
7399
return if empty?

0 commit comments

Comments
 (0)