# 進階電腦系統理論與實作 lab0
[I01: lab0 宅色夫](https://hackmd.io/@sysprog/2020-lab0#I01-lab0)
[My github](https://github.com/a5932016)
# 作業要求
* 在 GitHub 上 fork [lab0-c](https://github.com/sysprog21/lab0-c)
* 參閱 [Git 教學和 GitHub 設定指引](https://hackmd.io/@sysprog/git-with-github)
* 詳細閱讀 C Programming Lab ,依據指示著手修改 queue.[ch] 和連帶的檔案,測試後用 Git 管理各項修改,記得也該實作 q_sort 函式。
* 在提交程式變更前,務必詳閱 [如何寫好 Git Commit Message](https://blog.louie.lu/2017/03/21/%E5%A6%82%E4%BD%95%E5%AF%AB%E4%B8%80%E5%80%8B-git-commit-message/)
* 不用理會 [Autolab](https://autolabproject.com/) 和檔案下載的描述,這兩者都是 CMU 專屬
* 除了修改程式,也要編輯「作業區」,增添開發紀錄和 GitHub 連結,除了提及你如何逐步達到自動評分程式的要求外,共筆也要涵蓋以下:
* 運用 Valgrind 排除 qtest 實作的記憶體錯誤,並透過 Massif 視覺化 “simulation” 過程中的記憶體使用量,需要設計對應的實驗
* 研讀論文 [Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf),解釋本程式的 “simulation” 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 [Student’s t-distribution](https://en.wikipedia.org/wiki/Student%27s_t-distribution) 及程式實作的原理。注意:現有實作存在若干致命缺陷,請討論並提出解決方案;
* 挑戰題
* 參照 [antirez/linenoise](https://github.com/antirez/linenoise),強化 qtest 命令列功能,使其支援單行或多行編輯、歷史命令處理、命令自動補完 (例如輸入 h 之後按下 Tab 按鍵,自動接續補上 elp,成為完整的 help 命令)。除了整合程式碼外,應當說明 antirez/linenoise 的運作原理,注意到 [termios](https://man7.org/linux/man-pages/man3/termios.3.html) 的運用
* 思考如何預設開啟 AddressSanitizer,卻又能滿足效能測試所需 (可能需要兩種以上的 build target)
* 指出現有程式的缺陷 (提示: 和 [RIO 套件](http://csapp.cs.cmu.edu/2e/ch10-preview.pdf) 有關),嘗試強化並提交 pull request
# 環境
```
$ uname -a
Linux zihyan 5.7.0-kali1-amd64 #1 SMP Debian 5.7.6-1kali2 (2020-07-01) x86_64 GNU/Linux
$ gcc --version
gcc (Debian 10.2.0-9) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```
# 在 GitHub 上 fork lab0-c
照教學步驟完成即可
[我的lab0-c](https://github.com/a5932016/lab0-c)
# 開始撰寫queue.[ch]
## 如何測試
在目標資料夾後執行
方法1
```
make check
```
方法2
```
make test
```
方法3
```
make
./qtest
help
#依自行需求執行測試
```
結束後皆要執行`make clean`做清除
## q_size
### queue.h
`queue_t`結構新增`int size`
```
typedef struct {
list_ele_t *head; /* Linked list of elements */
int size; /* Size of queue */
} queue_t;
```
### queue.c
修改function`q_size`,確認參數是否為NULL,如果為TRUE則傳回`q->size`
```
int q_size(queue_t *q)
{
if (!q || !q->size)
return 0;
return q->size;
}
```
## q_new
### queue.h
`queue_t`結構新增`list_ele_t *tail;`
```
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size; /* Size of queue */
} queue_t;
```
### queue.c
`queue_t`因新增`size`和`tail`故需要做初始化
要確認是否成功分配記憶體,避免操作到空指標
```
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q) {
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
return q;
}
```
## q_free
傳入參數`queue_t`
先確認參數是否為NULL
依參數`q`使用`while loop`依序對`head`和`value`執行`free`
最後結束不要忘記對`q`執行`free`
```
void q_free(queue_t *q)
{
if (!q)
return;
while (q->head) {
list_ele_t *tmp = q->head;
q->head = q->head->next;
free(tmp->value);
free(tmp);
}
free(q);
}
```
## q_insert_head
加入新值在`head`前
指派一個新的`list_ele_t`,確認是否有建立成功
指派一個`char`,長度為`sizeof(char) * (strlen(s) + 1)`,額外留一個空間存`\0`
`tmp`指派到`newh`並指派為`q->head`,若`!q->tail`則指派到`q->tail`
`q->size`加1
```
bool q_insert_head(queue_t *q, char *s)
{
if (!q || !s)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (!newh)
return false;
int len = sizeof(char) * (strlen(s) + 1);
char *tmp = malloc(len);
if (!tmp) {
free(newh);
return false;
}
memcpy(tmp, s, len);
newh->value = tmp;
newh->next = q->head;
q->head = newh;
if (!q->tail) {
q->tail = q->head;
}
q->size += 1;
return true;
}
```
## q_insert_tail
作法大致上與`q_insert_head`一樣
```
bool q_insert_tail(queue_t *q, char *s)
{
if (!q || !s)
return false;
list_ele_t *newt = malloc(sizeof(list_ele_t));
if (!newt)
return false;
int len = sizeof(char) * (strlen(s) + 1);
char *tmp = malloc(len);
if (!tmp) {
free(newt);
return false;
}
memcpy(tmp, s, len);
newt->value = tmp;
newt->next = NULL;
if (!q->head) {
q->head = newt;
}
if (q->tail) {
q->tail->next = newt;
}
q->tail = newt;
q->size += 1;
return true;
}
```
## q_remove_head
確認q是否為空
暫存`q->head`
將`q->head`指派`q->head->next`
`q->size -= -1` 如果`size`為`0`,則將`q->tail`指派為`NULL`
如果`sp`不為空,則把欲`free`的值指派到`sp`
最後執行對`tmp`和`tmp->value`執行`free`
```
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !q->head)
return false;
list_ele_t *tmp = q->head;
q->head = q->head->next;
q->size -= 1;
if (!q->size) {
q->tail = NULL;
}
if (sp) {
size_t len =
bufsize > strlen(tmp->value) ? strlen(tmp->value) : bufsize - 1;
memset(sp, '\0', len + 1);
strncpy(sp, tmp->value, len);
}
free(tmp->value);
free(tmp);
return true;
}
```
## q_reverse
反轉鏈結串列
參考 [206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)
`while loop`完成後要記得`q->head->next = NULL`和指派新的`head` `tail`
```
void q_reverse(queue_t *q)
{
if (!q || q->size <= 1)
return;
list_ele_t *tmp;
list_ele_t *prev = NULL;
list_ele_t *curr = q->head;
while (curr) {
tmp = curr->next;
curr->next = prev;
prev = curr;
curr = tmp;
}
q->head->next = NULL;
tmp = q->head;
q->head = q->tail;
q->tail = tmp;
}
```
## q_sort
使用合併排序法
```
void mergeSort(list_ele_t **head)
{
if (!*head || !(*head)->next)
return;
list_ele_t *l1 = (*head)->next;
list_ele_t *l2 = *head;
while (l1 && l1->next) {
l2 = l2->next;
l1 = l1->next->next;
}
l1 = l2->next;
l2->next = NULL;
l2 = *head;
mergeSort(&l2);
mergeSort(&l1);
*head = NULL;
list_ele_t **tmp = head;
while (l1 && l2) {
if (strcmp(l1->value, l2->value) < 0) {
*tmp = l1;
l1 = l1->next;
} else {
*tmp = l2;
l2 = l2->next;
}
tmp = &((*tmp)->next);
}
*tmp = l1 ? l1 : l2;
}
void q_sort(queue_t *q)
{
if (!q || !q->head || q->size <= 1)
return;
mergeSort(&q->head);
while (q->tail->next) {
q->tail = q->tail->next;
}
}
```
# Valgrind 實驗
## 運用 Valgrind 排除 qtest 實作的記憶體錯誤
## 透過 Massif 視覺化 simulation 過程中的記憶體使用量
## 設計對應的實驗
//TODO
# 研讀論文 [Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf)
//TODO