---
# System prepended metadata

title: 2020q1 Homework1 (lab0)
tags: [Linux]

---

---
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().












