# 2020q3 Homework1 (lab0)
contributed by < s1041562 >
## Outline
* [Environment](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?both#Environment)
* [開發紀錄](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?both#%E9%96%8B%E7%99%BC%E7%B4%80%E9%8C%84)
* [queue_t](https://hackmd.io/@s1041562/2020q3-lab0?view#queue_t)
* [q_new](https://hackmd.io/@s1041562/2020q3-lab0?view#q_new)
* [q_free](https://hackmd.io/@s1041562/2020q3-lab0?view#q_free)
* [q_insert_head](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_insert_head)
* [q_insert_tail](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_insert_tail)
* [q_remove_head](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_remove_head)
* [q_size](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_size)
* [q_reverse](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_reverse)
* [q_sort](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?view#q_sort)
* [測試](https://hackmd.io/JXwu90ReQ0eb_WnhCpbyTQ?both#%E9%99%A4%E9%8C%AF%E9%81%8E%E7%A8%8B66100)
## Environment
```
$ uname -r
Linux oslab 5.4.0-42-generic #46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 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.
```
## 開發紀錄
### queue_t
* 因為要在 O(1) 時間內執行完成,所以除原本新增的 size 以外,需要新增一個 list_ele_t *tail 來讓 q_insert_tail 得以在 O(1) 時間內完成
```C=
typedef struct {
list_ele_t *head;
list_ele_t *tail;
int size;
} queue_t;
```
### q_new
* 宣告一個新的空佇列,如果宣告失敗的話則返回 NULL ,成功的話則初始化
```C=
/*
* Create empty queue.
* Return NULL if could not allocate space.
*/
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (!q) {
return NULL;
}
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### q_free
* 要注意釋放記憶體的時候要記得 value 也必須釋放
```C=
void q_free(queue_t *q)
{
if (!q) {
return;
}
while (q->head) {
list_ele_t *temp = q->head;
q->head = q->head->next;
free(temp->value);
free(temp);
}
free(q);
}
```
### q_insert_head
* 因為作業要求避免使用 strcpy ,參考 CERN 建議使用 strncpy ,搭配 memset 來解決 strncpy 不會自動填入 '\0' 的問題
* 要注意假如 malloc 分配失敗的情況
* 考慮第一次插入 newh 時要另外設定 q->tail
* 要注意 memory leak (line 12)
```C=
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (!q || !newh) {
return false;
}
int length = strlen(s) + 1;
newh->value = malloc(length * sizeof(char));
if (!newh->value) {
/*Avoid memory leak.*/
free(newh);
return false;
}
memset(newh->value, '\0', length);
strncpy(newh->value, s, strlen(s));
newh->next = q->head;
q->head = newh;
/* Check newh is the only element*/
if (!q->tail) {
q->tail = newh;
}
return true;
}
```
### q_insert_tail
* 作法跟 q_insert_head 類似,只是要記得把 newh->next 指向 NULL
```C=
bool q_insert_tail(queue_t *q, char *s)
{
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if(!q || !newh) {
return false;
}
int length = strlen(s) + 1;
newh->value =malloc(length * sizeof(char));
if(!newh->value) {
free(newh);
return false;
}
memset(newh->value, '\0', length);
strncpy(newh->value, s, strlen(s));
q->tail->next = newh;
q->tail = newh;
if(!q->head) {
q->head = newh;
}
newh->next = NULL;
q->size++;
return true;
}
```
### q_remove_head
```C=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !q->head) {
return false;
}
list_ele_t *temp;
temp = q->head;
if (sp) {
memset(sp, '\0', bufsize);
strncpy(sp, temp->value, bufsize - 1);
}
q->head = q->head->next;
free(temp->value);
free(temp);
return true;
}
```
### q_size
* 這裡只要增加邊界的判斷
```C=
int q_size(queue_t *q)
{
if (!q) {
return 0;
}
return q->size;
}
```
### q_reverse
* 先檢查元素數量
* 使用 temp 來幫助反轉
* 最後 q->tail->next 要記得指向NULL
```C=
void q_reverse(queue_t *q)
{
if (!q || q->size <= 1) {
return;
}
list_ele_t *temp;
q->tail->next = q->head;
temp = q->head;
q->head = q->head->next;
while (q->head != q->tail) {
/* 拿住接下來要接上去的元素*/
q->tail->next->next = q->head;
/* head 往下個元素移動避免下一個節點丟掉*/
q->head = q->head->next;
/* 把元素接上去*/
q->tail->next->next->next = temp;
/* 把 temp 移到佇列的最前面*/
temp = q->tail->next->next;
}
q->tail = q->tail->next;
q->head->next = temp;
q->tail->next = NULL;
return;
}
```
### q_sort
* 參考 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 的merge sort 方法,修改成可以排序字串
q_sort 主要檢查邊界以及要記得把 tail 移動到最後面
```C=
void q_sort(queue_t *q)
{
if (!q || q->size <= 1) {
return;
}
q->head = merge_sort_list(q->head);
while(q->tail->next){
q->tail = q->tail->next;
}
return;
}
```
merge_sort_list 用來分割 list
```C=
list_ele_t *merge_sort_list(list_ele_t *head)
{
if (!head || !head->next) {
return head;
}
list_ele_t *fast = head->next;
list_ele_t *slow = head;
/* spilt list */
while(fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
fast = slow->next;
slow->next = NULL;
list_ele_t *l1 = merge_sort_list(head);
list_ele_t *l2 = merge_sort_list(fast);
return merge(l1, l2);
}
```
merge 判斷大小
```C=
list_ele_t *merge(list_ele_t *l1, list_ele_t *l2)
{
list_ele_t q;
list_ele_t *temp = &q;
size_t len = (strlen(l1->value) < strlen(l2->value)) ? strlen(l1->value) : strlen(l2->value);
while (l1 && l2) {
if (strncmp(l1->value, l2->value, len) < 0) {
temp->next = l1;
temp = temp->next;
l1 = l1->next;
} else {
temp->next = l2;
temp = temp->next;
l2 = l2->next;
}
}
if (l1) {
temp->next = l1;
}
if (l2) {
temp->next = l2;
}
return q.next;
}
```
### 測試
先來看看分數
```
$make test
```
結果
```
scripts/driver.py -c
--- Trace Points
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
ERROR: Computed queue size as 6, but correct value is 2
--- trace-04-ops 0/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
ERROR: Computed queue size as 6, but correct value is 5
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-05-ops 0/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-06-string 0/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
ERROR: Freed queue, but 2 blocks are still allocated
--- trace-07-robust 0/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
ERROR: Not sorted in ascending order
ERROR: Not sorted in ascending order
ERROR: Not sorted in ascending order
ERROR: Not sorted in ascending order
--- trace-16-perf 0/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Testing insert_tail...(0/10)
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-17-complexity 0/5
--- TOTAL 66/100
```
只有66分
* 先看 trace-04-ops 錯誤
ERROR: Computed queue size as 6, but correct value is 2
看起來是 size 算錯,回頭查看發現 q_remove_sort 移除後沒有做 q->size--
修正之後
trace-01-ops 6/6
trace-02-ops 6/6
trace-03-ops 6/6
trace-04-ops 6/6
trace-05-ops 5/5
trace-06-string 6/6
trace-07-robust <font color="#f00">0/6</font>
trace-08-robust 6/6
trace-09-robust 6/6
trace-10-malloc 6/6
trace-11-malloc 6/6
trace-12-malloc 6/6
trace-13-perf 6/6
trace-14-perf 6/6
trace-15-perf 6/6
trace-16-perf <font color="#f00">0/6</font>
trace-17-complexity <font color="#f00">0/5</font>
TOTAL <font color="#f00">83/100</font>
剩下三個測試沒有過了
* 觀察錯誤訊息
```=
ERROR: Freed queue, but 2 blocks are still allocated
--- trace-07-robust 0/6
```
發現下方 code
```=
if (!q || !newh) {
return false;
}
```
沒有在 return 以前把宣告的 newh 釋放,於是改成
```=
if (!q || !newh) {
free(newh);
return false;
}
```
再次測試
```=
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
--- trace-07-robust 6/6
```
過了
接下來解決下一個測資
```=
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
ERROR: Not sorted in ascending order
ERROR: Not sorted in ascending order
--- trace-16-perf 0/6
```
```=
==411606== 1 bytes in 1 blocks are still reachable in loss record 1 of 258
==411606== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==411606== by 0x490350E: strdup (strdup.c:42)
==411606== by 0x124A98: xstrdup (in /usr/bin/make)
==411606== by 0x1308DD: define_variable_in_set (in /usr/bin/make)
==411606== by 0x111B70: main (in /usr/bin/make)
==411606==
==411606== 1 bytes in 1 blocks are still reachable in loss record 2 of 258
==411606== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==411606== by 0x490350E: strdup (strdup.c:42)
==411606== by 0x124A98: xstrdup (in /usr/bin/make)
==411606== by 0x1308DD: define_variable_in_set (in /usr/bin/make)
==411606== by 0x111BA4: main (in /usr/bin/make)
```
雖然 qtest 給出的錯誤是 Not sorted in ascending order 但是 Valgrind 卻偵測到
still reachable
由於不清楚 trace-16-perf 的錯誤與 Valgrind 的錯誤有沒有關聯
因此手動分別對第 16 17 測資做測試
發現 Valgrind 在第 17 測資給出下面錯誤
```=
==3584== Invalid write of size 8
==3584== at 0x10DF7A: q_insert_tail (queue.c:97)
==3584== by 0x10E473: measure (constant.c:69)
==3584== by 0x10E678: doit (fixture.c:136)
==3584== by 0x10E8D0: is_insert_tail_const (fixture.c:168)
==3584== by 0x10B592: do_insert_tail (qtest.c:259)
==3584== by 0x10CB87: interpret_cmda (console.c:220)
==3584== by 0x10D10F: interpret_cmd (console.c:243)
==3584== by 0x10D808: cmd_select (console.c:607)
==3584== by 0x10D923: run_console (console.c:628)
==3584== by 0x10BFE1: main (qtest.c:772)
==3584== Address 0x8 is not stack'd, malloc'd or (recently) free'd
==3584==
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
==3584== 3 bytes in 1 blocks are still reachable in loss record 1 of 36
==3584== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==3584== by 0x10C780: strsave_or_fail (report.c:230)
==3584== by 0x10D0D1: parse_args (console.c:190)
==3584== by 0x10D0D1: interpret_cmd (console.c:242)
==3584== by 0x10D808: cmd_select (console.c:607)
==3584== by 0x10D923: run_console (console.c:628)
==3584== by 0x10BFE1: main (qtest.c:772)
```
回頭看 q_insert_tail 發現在執行 q->tail->next = newh 時,沒有先確定 q->tail 是否存在導致程式有可能崩潰,把修改成下面在測試
```=C=
if (!q->tail) {
q->tail = q->head = newh;
} else {
q->tail->next = newh;
q->tail = newh;
}
newh->next = NULL;
```
```=
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 94/100
```
這樣就剩下第 16 測資了