Skip to content

Commit

Permalink
add linked list exercise (#156)
Browse files Browse the repository at this point in the history
* exercises: add linked-list

* exercises: linked-list: fix missing reference

* exercises: linked-list: fix build with pie

* exercises: linked-list: don't use malloc+free and use struc

* exercises: linked-list: remove stuct defs in tests

* exercises: linked-list: rename struc and move to .bss

* exercises: linked-list: no pointers between c and asm

* exercises: linked-list: better doc

* exercises: linked-list: chore: rebase

* exercises: linked-list: update generator
  • Loading branch information
sudhackar authored Feb 16, 2024
1 parent eadbe59 commit 892b4b9
Show file tree
Hide file tree
Showing 12 changed files with 4,043 additions and 1 deletion.
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

0 comments on commit 892b4b9

Please sign in to comment.