Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add linked list exercise #156

Merged
merged 11 commits into from
Feb 16, 2024
15 changes: 14 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,19 @@
"loops",
"strings"
]
},
{
"slug": "linked-list",
"name": "Linked List",
"uuid": "b79e66ed-54b3-43da-a4ce-884d81447ce5",
"practices": [],
"prerequisites": [],
"difficulty": 4,
"topics": [
"functions",
"pointers",
"structs"
]
}
]
},
Expand Down Expand Up @@ -423,4 +436,4 @@
"runtime/standalone_executable",
"used_for/embedded_systems"
]
}
}
28 changes: 28 additions & 0 deletions exercises/practice/linked-list/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Instructions

Implement a doubly linked list.

Like an array, a linked list is a simple linear data structure. Several
common data types can be implemented using linked lists, like queues,
stacks, and associative arrays.

A linked list is a collection of data elements called *nodes*. In a
*singly linked list* each node holds a value and a link to the next node.
In a *doubly linked list* each node also holds a link to the previous
node.

You will write an implementation of a doubly linked list. Implement a
Node to hold a value and pointers to the next and previous nodes. Then
implement a List which holds references to the first and last node and
offers an array-like interface for adding and removing items:

* `push` (*insert value at back*);
* `pop` (*remove value at back*);
* `shift` (*remove value at front*).
* `unshift` (*insert value at front*);

To keep your implementation simple, the tests will not cover error
conditions. Specifically: `pop` or `shift` will never be called on an
empty list.

If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/Linked_list).
19 changes: 19 additions & 0 deletions exercises/practice/linked-list/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"blurb": "Implement a doubly linked list",
"authors": [
"sudhackar"
],
"contributors": [],
"files": {
"solution": [
"linked_list.asm"
],
"test": [
"linked_list_test.c"
],
"example": [
".meta/example.asm"
]
},
"source": "Classic computer science topic"
}
221 changes: 221 additions & 0 deletions exercises/practice/linked-list/.meta/example.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
default rel
; a Node in the doubly linked-list
struc Node
; pointer to previous node
.Prev: resq 1
; pointer to next node
.Next: resq 1
; int data
.Data: resd 1
endstruc

; references to start and end of a list
struc List
; pointer to First node in the List
.First resq 1
; pointer to Last node in the list
.Last resq 1
endstruc

section .bss
; store the single instance of List to store pointers to First and Last nodes
list:
resb List_size
; space for 16 nodes which will be "allocated" sequentially
nodes:
resb 0x10*Node_size
; an integer that has the next available node from nodes to "allocate"
currnodeidx:
resq 1

section .text

global list_create
global list_count
global list_push
global list_pop
global list_unshift
global list_shift
global list_delete
global list_destroy

get_list:
; reset current node index to 0
mov dword [currnodeidx], 0
; return pointer to list
lea rax, [list]
ret
get_node:
movsx rax, dword [currnodeidx]
lea edx, [rax+1]
imul rax, rax, Node_size
mov dword [currnodeidx], edx
lea rdx, [nodes]
add rax, rdx
ret

list_node_create:
mov ecx, edx
call get_node
test rax, rax
je .err
mov qword [rax+Node.Prev], rdi
mov qword [rax+Node.Next], rsi
mov dword [rax+Node.Data], ecx
.err:
ret
list_create:
call get_list
test rax, rax
je .err
mov qword [rax+List.First], 0
mov qword [rax+List.Last], 0
.err:
ret
list_count:
lea rax, [list]
test rax, rax
je .fin_loop_count
mov rdi, rax
mov rdx, qword [rdi+List.First]
xor eax, eax
.loop_count:
test rdx, rdx
je .fin_loop_count
mov rdx, qword [rdx+Node.Next]
inc rax
jmp .loop_count
.fin_loop_count:
ret
list_push:
lea rax, [list]
test rax, rax
je .err
mov r8, rax
mov edx, edi
mov rdi, qword [r8+List.Last]
xor esi, esi
call list_node_create
test rax, rax
je .err
cmp qword [r8+List.First], 0
mov qword [r8+List.Last], rax
jne .first_set
mov qword [r8+List.First], rax
ret
.first_set:
mov rdx, qword [rax+Node.Prev]
mov qword [rdx+Node.Next], rax
.err:
ret
list_pop:
lea rdi, [list]
mov rax, qword [rdi+List.Last]
mov rdx, qword [rax+Node.Prev]
mov r8d, dword [rax+Node.Data]
mov qword [rdi+List.Last], rdx
cmp qword [rdi+List.First], rax
jne .set_prev_next
mov qword [rdi+List.First], 0
jmp .empty
.set_prev_next:
mov qword [rdx+Node.Next], 0
.empty:
mov qword [rax+Node.Prev], 0
mov qword [rax+Node.Next], 0
mov dword [rax+Node.Data], 0
mov eax, r8d
ret
list_unshift:
mov edx, edi
lea rdi, [list]
mov rsi, qword [rdi+List.First]
mov r8, rdi
xor edi, edi
call list_node_create
test rax, rax
je .err
cmp qword [r8+List.Last], 0
mov qword [r8+List.First], rax
jne .set_next_prev
mov qword [r8+List.Last], rax
ret
.set_next_prev:
mov rdx, qword [rax+Node.Next]
mov qword [rdx+Node.Prev], rax
.err:
ret
list_shift:
lea rdi, [list]
mov rax, qword [rdi+List.First]
mov rdx, qword [rax+Node.Next]
mov r8d, dword [rax+Node.Data]
mov qword [rdi+List.First], rdx
cmp qword [rdi+List.Last], rax
jne .set_next_prev
mov qword [rdi+List.Last], 0
jmp .zero
.set_next_prev:
mov qword [rdx+Node.Prev], 0
.zero:
mov qword [rax+Node.Prev], 0
mov qword [rax+Node.Next], 0
mov dword [rax+Node.Data], 0
mov eax, r8d
ret
list_delete:
mov esi, edi
lea rdi, [list]
mov r8, qword [rdi+List.First]
mov rax, r8
.search_loop:
test rax, rax
je .err
mov rdx, qword [rax+Node.Next]
cmp dword [rax+Node.Data], esi
jne .Next
mov rcx, qword [rax+Node.Prev]
cmp rax, r8
jne .set_prev_next
mov qword [rdi+List.First], rdx
jmp .last_compare
.set_prev_next:
mov qword [rcx+Node.Next], rdx
.last_compare:
cmp qword [rdi+List.Last], rax
jne .set_next_prev
mov qword [rdi+List.Last], rcx
jmp .zero
.set_next_prev:
mov rdx, qword [rax+Node.Next]
mov qword [rdx+Node.Prev], rcx
.zero:
mov qword [rax+Node.Prev], 0
mov qword [rax+Node.Next], 0
mov dword [rax+Node.Data], 0
ret
.Next:
mov rax, rdx
jmp .search_loop
.err:
ret
list_destroy:
call get_list
test rax, rax
je .err
mov rdi, rax
mov rax, qword [rdi+List.First]
.loop:
test rax, rax
je .zero
mov rdx, qword [rax+Node.Next]
mov qword [rax+Node.Prev], 0
mov qword [rax+Node.Next], 0
mov dword [rax+Node.Data], 0
mov rax, rdx
jmp .loop
.zero:
mov qword [rdi+List.First], 0
mov qword [rdi+List.Last], 0
.err:
ret
60 changes: 60 additions & 0 deletions exercises/practice/linked-list/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.

[7f7e3987-b954-41b8-8084-99beca08752c]
description = "pop gets element from the list"

[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885]
description = "push/pop respectively add/remove at the end of the list"

[00ea24ce-4f5c-4432-abb4-cc6e85462657]
description = "shift gets an element from the list"

[37962ee0-3324-4a29-b588-5a4c861e6564]
description = "shift gets first element from the list"

[30a3586b-e9dc-43fb-9a73-2770cec2c718]
description = "unshift adds element at start of the list"

[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d]
description = "pop, push, shift, and unshift can be used in any order"

[88f65c0c-4532-4093-8295-2384fb2f37df]
description = "count an empty list"

[fc055689-5cbe-4cd9-b994-02e2abbb40a5]
description = "count a list with items"

[8272cef5-130d-40ea-b7f6-5ffd0790d650]
description = "count is correct after mutation"

[229b8f7a-bd8a-4798-b64f-0dc0bb356d95]
description = "popping to empty doesn't break the list"

[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad]
description = "shifting to empty doesn't break the list"

[e8f7c600-d597-4f79-949d-8ad8bae895a6]
description = "deletes the only element"

[fd65e422-51f3-45c0-9fd0-c33da638f89b]
description = "deletes the element with the specified value from the list"

[59db191a-b17f-4ab7-9c5c-60711ec1d013]
description = "deletes the element with the specified value from the list, re-assigns tail"

[58242222-5d39-415b-951d-8128247f8993]
description = "deletes the element with the specified value from the list, re-assigns head"

[ee3729ee-3405-4bd2-9bad-de0d4aa5d647]
description = "deletes the first of two elements"

[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb]
description = "deletes the second of two elements"

[7b420958-f285-4922-b8f9-10d9dcab5179]
description = "delete does not modify the list if the element is not found"

[7e04828f-6082-44e3-a059-201c63252a76]
description = "deletes only the first occurrence"
Loading