# 2024q1 Homework1 (lab0)
[github](https://github.com/backink/lab0-c)
## 實作 queue.c 中未完成的函式
本次作業第一個目標是通過 `make test` 中所有測試資料,觀察 Makefile 內容可以發現測試是由 `script/driver.py` 執行,而 `script/driver.py` 的工作是將 `trace/` 內的檔案當作輸入資料傳入主程式 `qtest`中。
實作`queue.c`過程中需要盡可能使用 Linux 鏈結串列 API (即 `list.h`中所提供之 API)完成函式指定之操作。
### q_new()
```c
/* Create an empty queue */
struct list_head *q_new()
{
struct list_head *head = malloc(sizeof(struct list_head));
if (!head)
return NULL;
INIT_LIST_HEAD(head);
return head;
}
```
### q_free()
```c
/* Free all storage used by queue */
void q_free(struct list_head *head)
{
if (!head)
return;
element_t *entry, *safe;
list_for_each_entry_safe (entry, safe, head, list) {
q_release_element(entry);
}
free(head);
}
```
### q_insert_head()
```c
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
ele->value = strdup(s);
if (!ele->value) {
free(ele);
return false;
}
list_add(&ele->list, head);
return true;
}
```
### q_insert_tail()
```c
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
ele->value = strdup(s);
if (!ele->value) {
free(ele);
return false;
}
list_add_tail(&ele->list, head);
return true;
}
```
### q_remove_head()
```c
/* Remove an element from head of queue */
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *ele = list_first_entry(head, element_t, list);
if (sp && ele->value) {
strncpy(sp, ele->value, bufsize);
sp[bufsize - 1] = '\0';
}
list_del(head->next);
return ele;
}
```
### q_remove_tail()
```c
/* Remove an element from tail of queue */
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *ele = list_last_entry(head, element_t, list);
if (sp && ele->value) {
strncpy(sp, ele->value, bufsize);
sp[bufsize - 1] = '\0';
}
list_del(head->prev);
return ele;
}
```
### q_size()
```c
/* Return number of elements in queue */
int q_size(struct list_head *head)
{
if (!head || list_empty(head))
return 0;
int ret = 0;
struct list_head *node;
list_for_each (node, head) {
ret++;
}
return ret;
}
```
### q_delete_mid()
```c
/* Delete the middle node in queue */
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
struct list_head *first = head->next, *last = head->prev;
while ((first != last) && (first->next != last)) {
last = last->prev;
first = first->next;
}
element_t *ele = list_entry(first, element_t, list);
list_del(first);
q_release_element(ele);
return true;
}
```
在寫 leetcode 時因為題目限制都是單向鏈結串列,通常會用快慢指標來找到中間節點。
這裡的實作是雙向鏈結串列,因此使用兩個指標分別從頭尾往中間移動,一旦交會就可以得知中間節點的位置。
### q_delete_dup()
```c
/* Delete all nodes that have duplicate string */
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
element_t *entry, *safe;
bool dup = false;
list_for_each_entry_safe (entry, safe, head, list) {
if (&safe->list != head && !strcmp(entry->value, safe->value)) {
dup = true;
list_del(&entry->list);
q_release_element(entry);
} else if (dup) {
dup = false;
list_del(&entry->list);
q_release_element(entry);
}
}
return true;
}
```
### q_swap()
```c
/* Swap every two adjacent nodes */
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head || list_empty(head))
return;
struct list_head *node = head->next;
for (; node->next != head && node != head; node = node->next) {
list_move(node, node->next);
}
}
```
### q_reverse()
```c
/* Reverse elements in queue */
void q_reverse(struct list_head *head)
{
if (!head || list_empty(head))
return;
struct list_head *last = head->next;
struct list_head *node = head;
for (; head->prev != last; node = node->next) {
list_move(head->prev, node);
}
}
```
### q_reverseK()
```c
/* Reverse the nodes of the list k at a time */
void q_reverseK(struct list_head *head, int k)
{
// https://leetcode.com/problems/reverse-nodes-in-k-group/
if (!head || list_empty(head) || list_is_singular(head) || k <= 1)
return;
struct list_head *start = head->next;
struct list_head *node;
while (start != head) {
int n = k - 1;
for (node = start; n > 0 && node->next != head; node = node->next) {
n--;
}
if (n == 0) {
reverse_between(start, node);
start = start->next;
} else {
break;
}
}
}
void reverse_between(struct list_head *start, struct list_head *node)
{
struct list_head *head = start->prev;
struct list_head *tmp;
while (node != start) {
tmp = node->prev;
list_move(node, head);
head = head->next;
node = tmp;
}
}
```
新增一個函式`reverse_between` 給定兩個節點,將包含兩個節點之間的所有節點反向,以滿足`reverseK` 中反向K個節點的需求。
### q_sort()
```c
/* Sort elements of queue in ascending/descending order */
void q_sort(struct list_head *head, bool descend)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
struct list_head *first = head->next, *last = head->prev;
while (first != last) {
last = last->prev;
if (first == last)
break;
first = first->next;
}
LIST_HEAD(head_to);
list_cut_position(&head_to, head, first);
q_sort(head, descend);
q_sort(&head_to, descend);
merge(head, &head_to, descend);
}
void merge(struct list_head *head_main, struct list_head *head, bool descend)
{
struct list_head *node_main = head_main->next, *node = head->next;
struct list_head *tmp;
int flag = descend ? 1 : -1;
while (node_main != head_main && node != head) {
element_t *entry_main = list_entry(node_main, element_t, list);
element_t *entry = list_entry(node, element_t, list);
while (node_main != head_main &&
(flag * strcmp(entry_main->value, entry->value) > 0)) {
node_main = node_main->next;
entry_main = list_entry(node_main, element_t, list);
}
tmp = node->next;
list_move(node, node_main->prev);
node = tmp;
}
if (node != head)
list_splice_tail(head, head_main);
}
```
實作遞迴版本的 Merge Sort。
:::info
TODO
1. 比較不同排序方法的執行效能
:::
### q_ascend()
```c
/* Remove every node which has a node with a strictly less value anywhere to
* the right side of it */
int q_ascend(struct list_head *head)
{
// https://leetcode.com/problems/remove-nodes-from-linked-list/
if (!head || list_empty(head))
return 0;
struct list_head *min = head->prev;
int ret = 1;
while (min->prev != head) {
element_t *ele_min = list_entry(min, element_t, list);
element_t *ele_pre = list_entry(min->prev, element_t, list);
if (strcmp(ele_min->value, ele_pre->value) < 0) {
list_del(min->prev);
q_release_element(ele_pre);
} else {
min = min->prev;
ret++;
}
}
return ret;
}
```
函式說明使用`Remove`而非`Delete` ,所以我一開始沒有將移除節點的記憶體釋放,即使使用 Valgrind 也沒有找到錯誤位置,最後觀摩同學實作才發現需要釋放目標節點的記憶體。
```
Valgrind結果
```
:::success
送出PR,新增函式說明 [Link](https://github.com/sysprog21/lab0-c/pull/170)
TODO
1. 使用 sha1sum 更新 queue.h 的 checksum值
2. 追蹤 git hook 的流程,了解 git hook 如何運作及實作
:::
### q_descend()
```c
/* Remove every node which has a node with a strictly greater value anywhere to
* the right side of it */
int q_descend(struct list_head *head)
{
// https://leetcode.com/problems/remove-nodes-from-linked-list/
if (!head || list_empty(head))
return 0;
struct list_head *max = head->prev;
int ret = 1;
while (max->prev != head) {
element_t *ele_max = list_entry(max, element_t, list);
element_t *ele_pre = list_entry(max->prev, element_t, list);
if (strcmp(ele_max->value, ele_pre->value) > 0) {
list_del(max->prev);
q_release_element(ele_pre);
} else {
max = max->prev;
ret++;
}
}
return ret;
}
```
### q_merge()
```c
/* Merge all the queues into one sorted queue, which is in ascending/descending
* order */
int q_merge(struct list_head *head, bool descend)
{
// https://leetcode.com/problems/merge-k-sorted-lists/
if (!head || list_empty(head))
return 0;
struct list_head *first = head->next, *last = head->prev;
int ret = 0;
bool count_size = true;
while (last != head->next) {
while (first != last && first->prev != last) {
queue_contex_t *q_first = list_entry(first, queue_contex_t, chain);
queue_contex_t *q_last = list_entry(last, queue_contex_t, chain);
if (count_size) {
ret += q_first->size + q_last->size;
}
merge(q_first->q, q_last->q, descend);
first = first->next;
last = last->prev;
}
if (count_size && first == last) {
queue_contex_t *q = list_entry(first, queue_contex_t, chain);
ret += q->size;
}
count_size = false;
first = head->next;
}
return ret;
}
```
:::success
閱讀說明中有發現錯字,發送PR修正 [Link](https://github.com/sysprog21/lab0-c/pull/171)
:::
## 整合網頁伺服器
流程
select、fdf_set的說明及使用
## 論文〈[Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf)〉
本次作業有導入部分論文內容實作的程式碼 [dudect](https://github.com/oreparaz/dudect),用意是檢測函式實作是否屬於常數時間。
簡介、摘要:
雖然知道作業內原始程式碼和dudect的差異是後者有使用percetile對資料做篩選,藉由將離群值移除得到較佳的統計結果,但是我還沒有完全理解 percentile 實作的原理。
:::success
閱讀 dudect 程式碼的時候發現問題,原本的程式碼將64位元轉換成32位元數值,有可能造成非預期結果
發送 PR 修正 [Link](https://github.com/oreparaz/dudect/commit/dc269651fb2567e46755cfb2a13d3875592968b5)
```diff
- static int cmp(const int64_t *a, const int64_t *b) { return (int)(*a - *b); }
+ static int cmp(const int64_t *a, const int64_t *b) {
+ if (*a == *b)
+ return 0;
+ return (*a > *b) ? 1 : -1;
+}
```
:::
percentile 的計算方式?
## 研讀 Linux 核心原始程式碼的 lib/list_sort.c