# 2022q1 Homework1 (lab0)
contributed by < [Nomad1230](https://github.com/Nomad1230) >
## 實驗環境
```
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 1
On-line CPU(s) list: 0
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i5-8300H CPU @ 2.30GHz
CPU family: 6
Model: 158
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 1
Stepping: 10
BogoMIPS: 4608.00
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mc
a cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall n
x rdtscp lm constant_tsc rep_good nopl xtopology nonsto
p_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3
cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave a
vx rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_
single pti fsgsbase avx2 invpcid rdseed clflushopt md_c
lear flush_l1d
Virtualization features:
Hypervisor 供應商: KVM
虛擬型態: 全部
Caches (sum of all):
L1d: 32 KiB (1 instance)
L1i: 32 KiB (1 instance)
L2: 256 KiB (1 instance)
L3: 8 MiB (1 instance)
NUMA:
NUMA 節點: 1
NUMA node0 CPU(s): 0
Vulnerabilities:
Itlb multihit: KVM: Mitigation: VMX unsupported
L1tf: Mitigation; PTE Inversion
Mds: Mitigation; Clear CPU buffers; SMT Host state unknown
Meltdown: Mitigation; PTI
Spec store bypass: Vulnerable
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer
sanitization
Spectre v2: Mitigation; Full generic retpoline, STIBP disabled, RSB
filling
Srbds: Unknown: Dependent on hypervisor status
Tsx async abort: Not affected
```
## 作業要求
> [lab0](https://hackmd.io/@sysprog/linux2022-lab0)
[queue.c](https://github.com/sysprog21/lab0-c/blob/master/queue.c) 僅提供介面 ([interface](https://en.wikipedia.org/wiki/Interface-based_programming)) 但尚未有完整程式實作,需要由學員補完並逐步精進,包含以下:
* q_new: 建立新的「空」佇列;
* q_free: 釋放佇列所佔用的記憶體;
* q_insert_head: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則);
* q_insert_tail: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則);
* q_remove_head: 在佇列開頭 (head) 移去 (remove) 給定的節點;
* q_release_element: 釋放特定節點的記憶體空間;
* q_size: 計算目前佇列的節點總量;
* q_delete_mid: 移走佇列中間的節點,詳見 [LeetCode 2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/)
* q_delete_dup: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 [LeetCode 82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)
* q_swap: 交換佇列中鄰近的節點,詳見 [LeetCode 24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/)
* q_reverse: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點;
* q_sort: 以遞增順序來排序鏈結串列的節點,可參閱 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 得知實作手法;
## 開發過程
### q_new
:::spoiler 程式碼
```c
struct list_head *q_new()
{
struct list_head *new = malloc(sizeof(struct list_head));
// check if malloc success
if (!new)
return NULL;
else {
INIT_LIST_HEAD(new);
return new;
}
}
```
:::
---
### q_free
:::spoiler 程式碼
```c
void q_free(struct list_head *l)
{
if (!l)
return;
struct list_head *node, *safe;
list_for_each_safe (node, safe, l) {
list_del_init(node);
q_release_element(list_entry(node, element_t, list));
}
free(l);
}
```
:::
---
### q_insert_head
:::spoiler 程式碼
```c
bool q_insert_head(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *new = malloc(sizeof(element_t));
if (!new)
return false;
else {
new->value = strdup(s);
if (!new->value) {
free(new);
return false;
} else {
list_add(&new->list, head);
return true;
}
}
}
```
:::
---
### q_insert_tail
:::spoiler 程式碼
```c
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *new = malloc(sizeof(element_t));
if (!new)
return false;
else {
new->value = strdup(s);
if (!new->value) {
free(new);
return false;
} else {
list_add_tail(&new->list, head);
return true;
}
}
}
```
:::
---
### q_remove_head
:::spoiler 程式碼
```c
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || head->next == head)
return NULL;
else {
struct list_head *del = head->next;
list_del_init(head->next);
element_t *del_ele = list_entry(del, element_t, list);
if (sp) {
strncpy(sp, del_ele->value, bufsize - 1);
*(sp + bufsize - 1) = '\0';
}
return del_ele;
}
}
```
:::
---
### q_remove_tail
:::spoiler 程式碼
```c
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || head->next == head)
return NULL;
else {
struct list_head *del = head->prev;
list_del_init(head->prev);
element_t *del_ele = list_entry(del, element_t, list);
if (sp) {
strncpy(sp, del_ele->value, bufsize - 1);
*(sp + bufsize - 1) = '\0';
}
return del_ele;
}
}
```
:::
---
### q_size
:::spoiler 程式碼
```c
int q_size(struct list_head *head)
{
if (!head || head->next == head)
return 0;
int count = 0;
struct list_head *node;
list_for_each (node, head) {
count++;
}
return count;
}
```
:::
---
### q_delete_mid
:::spoiler 程式碼
```c
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || head->next == head)
return false;
element_t *del;
struct list_head **indir = &head;
struct list_head *temp = head->prev;
temp->next = NULL;
for (struct list_head *fast = head; fast && fast->next;
fast = fast->next->next)
indir = &(*indir)->next;
temp->next = head;
del = list_entry(*indir, element_t, list);
list_del_init(*indir);
q_release_element(del);
return true;
}
```
:::
---
### q_delete_dup
:::spoiler 程式碼
```c
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head)
return false;
element_t *node, *safe;
list_for_each_entry_safe (node, safe, head, list) {
// if &safe->list == head, safe->value will dereference a null pointer
if (&safe->list != head) {
if (node->value && safe->value) {
if (!strcmp(node->value, safe->value)) {
list_del(&node->list);
q_release_element(node);
}
}
}
}
return true;
}
```
:::
---
### q_swap
:::spoiler 程式碼
```c
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head || head->next == head || head->next->next == head)
return;
for (struct list_head *node1 = head->next, *node2 = head->next->next;
node1 != head && node2 != head;
node1 = node1->next, node2 = node1->next) {
node1->prev->next = node2;
node2->prev = node1->prev;
node1->prev = node2;
node1->next = node2->next;
node2->next->prev = node1;
node2->next = node1;
}
}
```
:::
---
### q_reverse
原理是將每一個 list_head 中的 prev 跟 next 互相交換,當每一個 node 的 prev 跟 next 都交換完成,即可達成整個 list 的 reverse 。
先把 Linked list 中首尾相接的兩條指標設成NULL,讓 while 迴圈操作時可以順利中止並避免出錯,當最後一個 node 交換完成之後跳出迴圈,並把剛才移除的指標重新連結上。
:::spoiler
```c
void q_reverse(struct list_head *head)
{
if (!head || head->next == head)
return;
struct list_head *node = head, *temp;
head->prev->next = NULL;
head->prev = NULL;
while (1) {
temp = node->next;
node->next = node->prev;
node->prev = temp;
if (!node->prev)
break;
else
node = node->prev;
}
node->prev = head;
head->next = node;
}
```
:::
---
### q_sort
:::spoiler 程式碼
```c
void q_sort(struct list_head *head)
{
if (!head || head->next == head || head->next->next == head)
return;
int top = 0;
int listsSize = 0;
struct list_head *stack[10000] = {NULL};
struct list_head *lists[100000] = {NULL};
stack[top] = head->next;
head->prev->next = NULL;
INIT_LIST_HEAD(head);
// divide to single node
while (top >= 0) {
struct list_head *left = stack[top--];
if (left) {
struct list_head *slow = left;
struct list_head *fast;
for (fast = left->next; fast && fast->next; fast = fast->next->next)
slow = slow->next;
struct list_head *right = slow->next;
slow->next = NULL;
stack[++top] = left;
stack[++top] = right;
} else {
stack[top]->prev = stack[top];
stack[top]->next = NULL;
lists[listsSize++] = stack[top--];
}
}
// merge K sorted lists
while (listsSize > 1) {
for (int i = 0, j = listsSize - 1; i < j; i++, j--)
lists[i] = mergeTwoLists(lists[i], lists[j]);
listsSize = (listsSize + 1) / 2;
}
struct list_head *node = lists[0];
while (node->next) {
node->next->prev = node;
node = node->next;
}
node->next = head;
head->next = lists[0];
lists[0]->prev = head;
head->prev = node;
}
struct list_head *mergeTwoLists(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head, **node;
for (node = NULL; L1 && L2; *node = (*node)->next) {
node = (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) < 0) ? &L1: &L2;
*ptr = *node;
ptr = &(*ptr)->next;
}
if (!L1) {
*ptr = L2;
return head;
}
if (!L2) {
*ptr = L1;
return head;
}
return head;
}
```
:::
:::spoiler
```c
struct list_head *mergeTwoLists(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head, **node;
for (node = NULL; L1 && L2; *node = (*node)->next) {
node = (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) < 0)
? &L1
: &L2;
*ptr = *node;
ptr = &(*ptr)->next;
}
*ptr = (struct list_head *) ((u_int64_t) L1 | (u_int64_t) L2);
return head;
}
static struct list_head *mergesort(struct list_head *head)
{
if (!head || !head->next)
return head;
struct list_head *slow = head, *fast = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
slow->prev->next = NULL;
struct list_head *left = mergesort(head);
struct list_head *right = mergesort(slow);
return mergeTwoLists(left, right);
}
/*
* Sort elements of queue in ascending order
* No effect if q is NULL or empty. In addition, if q has only one
* element, do nothing.
*/
void q_sort(struct list_head *head)
{
if (!head || head->next == head || head->next->next == head)
return;
head->prev->next = NULL;
head->next = mergesort(head->next);
struct list_head *node = head->next;
node->prev = head;
while (node->next) {
node->next->prev = node;
node = node->next;
}
node->next = head;
head->prev = node;
}
```
:::