---
title: 2020q1 Homework1 (lab0)
tags: Linux
---
# 2020q1 Homework1 (lab0)
contributed by < `LJP-TW` >
- [Linux 核心設計課程 —— 第 1 次作業](https://hackmd.io/K0Ym8IkjR3iWaL6m8-DNZw)
# 實作
## queue_t
```c
/* Queue structure */
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t **tail;
unsigned int size;
} queue_t;
```
- 為了使得 `q_insert_tail` 和 `q_size` 時間複雜度為 $O(1)$,於是增加了 tail 和 size 兩個欄位
## q_new
```c
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q == NULL) {
return NULL;
}
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
- `malloc` 執行失敗會回傳 NULL [1]
## q_free
```c
void q_free(queue_t *q)
{
list_ele_t *ele;
if (q == NULL)
return;
/* Free queue structure */
ele = q->head;
while (ele != NULL) {
q->head = ele->next;
free(ele->value);
free(ele);
ele = q->head;
}
free(q);
}
```
- 將整個 linked list 釋放掉
## q_insert_head
```c
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
char *news;
unsigned long len, tmp;
if (q == NULL)
goto ERROR;
newh = malloc(sizeof(list_ele_t));
if (newh == NULL)
goto ERROR;
len = 0;
while (*(s + len))
++len;
/* Plus 1 for NULL byte */
news = malloc(sizeof(char) * (len + 1));
if (news == NULL) {
free(newh);
goto ERROR;
}
/* Copy string, including the NULL byte */
for (tmp = 0; tmp <= len; ++tmp) {
news[tmp] = s[tmp];
}
newh->value = news;
newh->next = q->head;
q->head = newh;
if (q->tail == NULL) {
q->tail = &(newh->next);
}
++q->size;
return true;
ERROR:
return false;
}
```
- 在計算字串多長以及複製字串這兩方面,使用簡單的迴圈即可處理,不 call lib function
- 預想上是會比 call lib function 還要快,需再做實驗
- 將遇到錯誤的部分 goto 到一個區域
- 若日後要統一改變遇到錯誤後的行為,能直接在此區域變更,較為方便
## q_insert_tail
```c
bool q_insert_tail(queue_t *q, char *s)
{
list_ele_t *newh;
char *news;
unsigned long len, tmp;
if (q == NULL)
goto _ERROR;
newh = malloc(sizeof(list_ele_t));
if (newh == NULL)
goto _ERROR;
len = 0;
while (*(s + len))
++len;
news = malloc(sizeof(char) * (len + 1));
if (news == NULL) {
free(newh);
goto _ERROR;
}
for (tmp = 0; tmp <= len; ++tmp) {
news[tmp] = s[tmp];
}
newh->value = news;
newh->next = NULL;
if (q->head == NULL) {
q->head = newh;
} else {
*(q->tail) = newh;
}
q->tail = &(newh->next);
++q->size;
return true;
_ERROR:
return false;
}
```
- 與 `q_insert_head` 只差在 linked list 鏈結的部分
- 反思 `q->tail` 的型態訂成什麼比較好
- 若為 `list_ele_t *`
則尾段的 code 要改成
```c
if (q->head == NULL) {
q->head = newh;
} else {
q->tail->next = newh;
}
q->tail = newh;
```
似乎也沒有什麼不好
- 總之,有了 `q->tail` 後,此 function 就能在時間複雜度 $O(1)$ 以內處理完了
## q_remove_head
```c
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
list_ele_t *tmp;
if (q == NULL || q->head == NULL)
return false;
if (sp != NULL) {
char *v;
unsigned long i;
v = q->head->value;
for (i = 0; i < bufsize - 1; ++i) {
sp[i] = v[i];
}
sp[i] = '\0';
}
free(q->head->value);
if (q->size == 1) {
/* There is only one element in list */
free(q->head);
q->head = NULL;
q->tail = NULL;
} else {
tmp = q->head->next;
free(q->head);
q->head = tmp;
}
--q->size;
return true;
}
```
- 若 `sp` 有值,表示使用者想將字串存取出來,先處理完這一部分後再執行釋放的部分
## q_size
```c
int q_size(queue_t *q)
{
if (q == NULL || q->head == NULL)
return 0;
return q->size;
}
```
- 判斷一些條件後回傳 size 即可,有了 `q->size` 後就能在時間複雜度 $O(1)$ 以內處理完
## q_reverse
```c
void q_reverse(queue_t *q)
{
list_ele_t *curr, *prev, *tmp;
if (q == NULL || q->head == NULL)
return;
q->tail = &(q->head->next);
prev = NULL;
curr = q->head;
while (curr != NULL) {
tmp = curr->next;
curr->next = prev;
prev = curr;
curr = tmp;
}
q->head = prev;
}
```
- 透過 `prev` 和 `curr` 走完整張 linked list 並改變鏈結方向
## q_sort
```c
void q_sort(queue_t *q)
{
if (q == NULL || q->head == NULL || q->head->next == NULL)
return;
// bubble_sort(q, strnatcmp);
merge_sort(q, strnatcmp);
}
```
- 寫了 bubble sort 來驗證的確過不了 trace-16-perf.cmd
> sorting algorithms with O(n^2) time complexity are expected failed
> sorting algorithms with O(nlogn) time complexity are expected pass
>
- 原先擔心是否會用爆 stack,所以先寫了 iterative 版的 merge sort,但速度太慢,過不了 trace-15-perf.cmd,所以又寫了 recursive 版的
- Sorting algorithm 相關程式碼寫在 `sort.[ch]`,並也下載 [natural sort](https://github.com/sourcefrog/natsort) 的 `strnatcmp.[ch]`,最後修改 Makefile
## Makefile
```
OBJS := qtest.o report.o console.o harness.o queue.o strnatcmp.o sort.o\
random.o dudect/constant.o dudect/fixture.o dudect/ttest.o
```
- `OBJS` 新增了 `strnatcmp.o` 和 `sort.o`
## merge_sort
### sort.h
```c
#ifndef LAB0_SORT_H
#define LAB0_SORT_H
/*
* This program implements kinds of sorting algorithm for singly linked list
* You must make sure that queue_t *q, q->head, q->head->next isn't NULL
*/
#include "queue.h"
typedef int (*strcmpfp)(char const *, char const *);
void merge_sort(queue_t *q, strcmpfp cmp);
void bubble_sort(queue_t *q, strcmpfp cmp);
#endif /* LAB0_SORT_H */
```
- 配合 `strnatcmp`,定義相對應的 function pointer type
- 可自訂比較方式
### sort.c
```c
static strcmpfp cmp;
static list_ele_t *merge(list_ele_t *l1, list_ele_t *l2)
{
list_ele_t buf;
list_ele_t *tmp = &buf;
while (l1 && l2) {
if (cmp(l1->value, l2->value) <= 0) {
tmp->next = l1;
tmp = tmp->next;
l1 = l1->next;
} else {
tmp->next = l2;
tmp = tmp->next;
l2 = l2->next;
}
}
tmp->next = l1 ? l1 : l2;
return buf.next;
}
static list_ele_t *_merge_sort(list_ele_t *head)
{
if (!head || !head->next)
return head;
list_ele_t *fast = head->next;
list_ele_t *slow = head;
// Split list
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
fast = slow->next;
slow->next = NULL;
list_ele_t *l1 = _merge_sort(head);
list_ele_t *l2 = _merge_sort(fast);
return merge(l1, l2);
}
void merge_sort(queue_t *q, strcmpfp _cmp)
{
list_ele_t *head, *curr;
cmp = _cmp;
// Sort
head = _merge_sort(q->head);
// Set queue_t
q->head = head;
curr = head;
while (curr->next)
curr = curr->next;
q->tail = &(curr->next);
}
```
- 參考了 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 並實作出來
- `static strcmpfp cmp` 設為 global 目的是避免每次遞迴還要多傳這個變數一次
# Reference
[1]: man malloc
> The malloc() function allocates size bytes and returns a pointer to the allocated
memory. The memory is not initialized. If size is 0, then malloc() returns either
NULL, or a unique pointer value that can later be successfully passed to free().