# 2021q1 Homework2 (quiz2)
contributed by < `tigger12613` >
> [2021q1 第 2 週測驗題](https://hackmd.io/@sysprog/linux2021-quiz2)
## 測驗 `1`
### 解釋程式碼運作原理
#### List add tail
新增一個節點在鏈的尾端
```cpp
static inline void list_add_tail(struct list_head *node, struct list_head *head)
{
struct list_head *prev = head->prev;
prev->next = node;
node->next = head;
node->prev = prev;
head->prev = node;
}
```
before add node
```graphviz
digraph a {
rankdir=LR;
edge [dir = both]
node[shape=box]
head [label = "head"]
tail [label = "tail"]
middle[label = "..."]
first [label = "first"]
head->first
first->middle
middle ->tail
tail-> head
}
```
after add node ,紅色是新增的,灰色是移除的
``` graphviz
digraph b {
rankdir=LR;
edge [dir = both]
node[shape=box]
new_node [ label = "new node" fontcolor = red color = red]
head [label = "head"]
tail [label = "tail"]
middle[label = "..."]
first [label = "first"]
head->first
first->middle
middle ->tail
head->tail [ color = gray]
tail->new_node [color = red]
new_node->head [color = red]
}
```
#### List delete
移除一個指定的節點
```cpp
static inline void list_del(struct list_head *node)
{
struct list_head *next = node->next, *prev = node->prev;
next->prev = prev; prev->next = next;
}
```
```graphviz
digraph a {
rankdir=LR;
edge [dir = both]
node[shape=box]
none1 [label = "..." shape=none]
prev [label = "prev node"]
next [label = "next node"]
removed [label = "removed node" fontcolor = gray color=gray ]
none2 [label = "..." shape=none]
prev->removed [color=gray]
none1->prev
removed->next [color=gray]
prev->next [color=red]
next->none2
}
```
#### Get middle
找到鏈的中點
用一個快指標跟慢指標,快指標一次走兩個節點,慢指標一次走一個節點,最後慢指標會停在鏈的中點。
```cpp
static list_ele_t *get_middle(struct list_head *list)
{
struct list_head *fast = list->next, *slow;
list_for_each (slow, list) {
if (COND1 || COND2)
break;
fast = fast->next->next;
}
return list_entry(TTT, list_ele_t, list);
}
```
COND1 = `fast->next == list`
COND2 = `fast->next->next == list`
TTT = `slow`
#### Merge sort
```cpp
void list_merge_sort(queue_t *q)
{
if (list_is_singular(&q->list))
return;
queue_t left;
struct list_head sorted;
INIT_LIST_HEAD(&left.list);
list_cut_position(&left.list, &q->list, MMM);
list_merge_sort(&left);
list_merge_sort(q);
list_merge(&left.list, &q->list, &sorted);
INIT_LIST_HEAD(&q->list);
list_splice_tail(&sorted, &q->list);
}
```
MMM = `&get_middle(&q->list)->list`
`q` is `queue_t`
`q->list` is `struct list_head`
`&q->list` is `struct list_head*` is type is `get_middle`needed.
So, chose `&q->list` be the varable of `get_middle`
MMM need `struct list_head *node`
`get_middle` return `list_ele_t`
`list_ele_t->list` is `struct list_head`
`&list_ele_t->list` is we want.
By checking the type we will know MMM = `&get_middle(&q->list)->list`
```c
static list_ele_t get_middle(struct list_head *list)
```
---
## 測驗 `2`
### 解釋程式碼運作原理
考慮函式 func 接受一個 16 位元無號整數 N,並回傳小於或等於 N 的 power-of-2 (漢字可寫作為 2 的冪)
```cpp
uint16_t func(uint16_t N) {
/* change all right side bits to 1 */
N |= N >> 1;
N |= N >> X;
N |= N >> Y;
N |= N >> Z;
return (N + 1) >> 1;
}
```
X = 2
Y = 4
Z = 8
#### 解法
小於或等於 N 的 power-of-2 就是 N 保留二進位第一個 1 剩下的都變成 0 就是答案。
舉例來說
```c
if N = 01??????????????
after N |= N >> 1, N = 011?????????????b
after N |= N >> 2, N = 01111???????????b
after N |= N >> 4, N = 011111111???????b
after N |= N >> 8, N = 0111111111111111b
after (N + 1) >> 1,N = 0100000000000000b
```
:::warning
你可用漢語或英語書寫共筆,但全頁面應該用單一語文
:notes: jserv
:::
for every `0 < N < 2^16` the function will return the correct answer.
if `N = 0`, we will get 1. Actually, there is no any power-of-2 that is smaller or equal to `0`.
if `2^15 <= N < (2^16)-1`, the return value should be `0`, because after line `N |= N >> 8` N = `1111111111111111b`, N+1 = `0000000000000000b`, `(N + 1) >> 1` = `0000000000000000b`, because it will overflow the result should be 0.
But on my computer `Ubuntu 20.04` running on `Parallels VM` on `macOS Catalina` on a `intel 64bit` processer, it return right answer `100000000000000b`.
```cpp=
uint16_t func(uint16_t N)
{
N = N | N >> 1;
N = N | N >> 2;
N = N | N >> 4;
N = N | N >> 8;
return (N+1) >> 1;
}
int main(){
unsigned int u32 = 0xffffffff;
printf("sizeof uint: %ld\n",sizeof(unsigned int));
printf("u32=0x%x u32+1=0x%x\n",u32,u32+1);
uint16_t u16 = 0xffff;
printf("sizeof u_int16: %ld\n",sizeof(u_int16_t));
printf("u16=0x%x u16+1=0x%x\n",u16,u16+1);
printf("intput:0x%x\n",(uint16_t) pow(2,15)+1);
printf("pow-of-2 = 0x%x\n",func((uint16_t) pow(2,15)+1));
return 0;
}
```
```shell
sizeof uint: 4
u32=0xffffffff u32+1=0x0
sizeof u_int16: 2
u16=0xffff u16+1=0x10000
intput:0x8001
pow-of-2 = 0x8000
```
考慮到可能是因為 N+1 的時候 1 被視為 int 讓 N+1 轉型成 int ,因此設計以下實驗,可以發現如果印出暫時數值的時候超出 16bit ,相加後給予實際變數之後卻會溢出。
```cpp=
uint16_t u16 = 0xffff;
uint16_t tmp = 0x0001;
uint16_t result = u16 + tmp;
printf("sizeof u_int16: %ld\n",sizeof(u_int16_t));
printf("u16=0x%x u16+1=0x%x result=0x%x\n",u16,u16+tmp, result);
```
```shell
sizeof u_int16: 2
u16=0xffff u16+1=0x10000 result=0x0
```
---
## 測驗 `3`
### 解釋程式碼運作原理
```cpp=
void bitcpy(void *_dest, /* Address of the buffer to write to */
size_t _write, /* Bit offset to start writing to */
const void *_src, /* Address of the buffer to read from */
size_t _read, /* Bit offset to start reading from */
size_t count) /* Numbers of bit to copy */
{
size_t read_lhs = _read & 7;
size_t read_rhs = 8 - read_lhs;
const uint8_t *source = (const uint8_t *) _src + (_read / 8);
size_t write_lhs = _write & 7;
size_t write_rhs = 8 - write_lhs;
uint8_t *dest = (uint8_t *) _dest + (_write / 8);
static const uint8_t read_mask[] = {
0x00, /* == 0 00000000b */
0x80, /* == 1 10000000b */
0xC0, /* == 2 11000000b */
0xE0, /* == 3 11100000b */
0xF0, /* == 4 11110000b */
0xF8, /* == 5 11111000b */
0xFC, /* == 6 11111100b */
0xFE, /* == 7 11111110b */
0xFF /* == 8 11111111b */
};
static const uint8_t write_mask[] = {
0xFF, /* == 0 11111111b */
0x7F, /* == 1 01111111b */
0x3F, /* == 2 00111111b */
0x1F, /* == 3 00011111b */
0x0F, /* == 4 00001111b */
0x07, /* == 5 00000111b */
0x03, /* == 6 00000011b */
0x01, /* == 7 00000001b */
0x00 /* == 8 00000000b */
};
while (count > 0) {
uint8_t data = *source++;
size_t bitsize = (count > 8) ? 8 : count;
if (read_lhs > 0) {
RRR;
if (bitsize > read_rhs)
data |= (*source >> read_rhs);
}
if (bitsize < 8)
data &= read_mask[bitsize];
uint8_t original = *dest;
uint8_t mask = read_mask[write_lhs];
if (bitsize > write_rhs) {
/* Cross multiple bytes */
*dest++ = (original & mask) | (data >> write_lhs);
original = *dest & write_mask[bitsize - write_rhs];
*dest = original | (data << write_rhs);
} else {
// Since write_lhs + bitsize is never >= 8, no out-of-bound access.
mask |= write_mask[write_lhs + bitsize];
*dest++ = (original & mask) | (data >> write_lhs);
}
count -= bitsize;
}
}
```
`read_lhs` 在一個 byte 中有幾個左邊的 bit 不要被複製
`read_rhs` 在一個 byte 中有幾個右邊的 bit 要被複製
`source` 指向第一個需要被複製的 byte
`write_lhs` 在一個 byte 中有幾個左邊的 bit 不要被覆蓋
`write_rhs` 在一個 byte 中有幾個右邊的 bit 要被覆蓋
`dest` 指向第一個需要被覆蓋的 byte
```cpp=7
size_t read_lhs = _read & 7;
size_t read_rhs = 8 - read_lhs;
const uint8_t *source = (const uint8_t *) _src + (_read / 8);
size_t write_lhs = _write & 7;
size_t write_rhs = 8 - write_lhs;
uint8_t *dest = (uint8_t *) _dest + (_write / 8);
```
`data` 要複製的 bits
`bitsize` 本次要複製的 bits count ,如果大於 8 則複製 8 個
```cpp=39
uint8_t data = *source++;
size_t bitsize = (count > 8) ? 8 : count;
if (read_lhs > 0) {
/*讓 data 的最左邊是要複製的 bits*/
data <<= read_lhs;
/* 如果成立,代表要被複製的數量大於現在在 data 裡的數量
* 因此要把下一個位置的 bits 也放到 data 中*/
if (bitsize > read_rhs)
data |= (*source >> read_rhs);
}
/*不需要的位置設為0*/
if (bitsize < 8)
data &= read_mask[bitsize];
uint8_t original = *dest;
uint8_t mask = read_mask[write_lhs];
if (bitsize > write_rhs) {
/* Cross multiple bytes */
/* (original & mask)把要被覆蓋的位子設為0
* (data >> write_lhs)對齊位置 */
*dest++ = (original & mask) | (data >> write_lhs);
/* original 左邊要被覆蓋的位置設為0*/
original = *dest & write_mask[bitsize - write_rhs];
/* 把 data 右邊還沒複製的 bits 複製上去*/
*dest = original | (data << write_rhs);
} else {
// Since write_lhs + bitsize is never >= 8, no out-of-bound access.
/* 因為bitsize <= write_rhs
* 且 write_lhs + write_rhs = 8
* 因此 write_lhs + bitsize <= 8
* mask 把左邊與右邊不需要覆蓋的範圍設為1*/
mask |= write_mask[write_lhs + bitsize];
/* (original & mask)把需要被覆蓋的位置設為0
* (data >> write_lhs)對齊位置*/
*dest++ = (original & mask) | (data >> write_lhs);
}
```
---
## 測驗 `4`
### 解釋程式碼運作原理