# 2022q1 Homework4 (quiz4)
contributed by < `cyrong` >
## 測驗`1`
```c
int ceil_log2(uint32_t x)
{
uint32_t r, shift;
x--;
r = (x > 0xFFFF) << 4;
x >>= r;
shift = (x > 0xFF) << 3;
x >>= shift;
r |= shift;
shift = (x > 0xF) << 2;
x >>= shift;
r |= shift;
shift = (x > 0x3) << 1;
x >>= shift;
return (r | shift | x >> 1) + 1;
}
```
在此程式中使用了 4 個數字作為類似 bitmask 的作用
分別是 `0xFFFF` , `0xFF`, `0xF`, `0x3`
將他們以 2 進位在 32 位元表示的話
`0xFFFF` : 0000 0000 0000 0000 1111 1111 1111 1111
`0x00FF` : 0000 0000 0000 0000 0000 0000 1111 1111
`0x000F` : 0000 0000 0000 0000 0000 0000 0000 1111
`0x0003` : 0000 0000 0000 0000 0000 0000 0000 0011
以這些數字每次將位元數分成兩半,讓變數 `r` 紀錄 `ceil_log2` 值中 2 的冪的部份。
而 `shift` 最終會有兩種結果,分別是 0, 2,是在程式碼第 14 行處決定最後的值, `shift` 會從 0 開始分別在 `x` 的值在 2 的偶數次方之後交替,也就是
`x` = 2($2^0 + 1$) ~ 4($2^2$), `shift` = 0
`x` = 5($2^2 + 1$) ~ 16($2^4$), `shift` = 2
`x` = 17($2^4 + 1$) ~ 64($2^6$), `shift` = 0
`x` = 65($2^6 + 1$) ~ 256($2^8$), `shift` = 2
以此類推,`shift` 用意在於紀錄 `r` 所紀錄不到的 `ceil_log2` 中 2 的倍數的部份。
最後是 `x` , `x` 最終會有三種結果,分別是 1, 2, 3,原因是經過程式碼中 7, 9, 12, 15行,若是符合條件 `x` 將分別被向右移 16, 8, 4, 2位元,因此最終只會存在 `x` 的前兩位元,又因為 `x` > 1,因此只會有 1, 2, 3 三種可能,觀察這三個值
$1_{10}$ = $01_{2}$
$2_{10}$ = $10_{2}$
$3_{10}$ = $11_{2}$
當 `x` = 2, 3 時,代表 `ceil_log2` 的值會比 `x` = 1 時多 1 ,因此在回傳時將 `x` 向右位移以得知 `x` 是否為 2 或 3 。
而在 return 的最後的地方 +1 其用意在於取 ceiling 的部份,上述程式碼可以計算出的值為 `x` 中包含的 2 的指數,也就是說取得的是 $\lfloor log_2(x) \rfloor$, 但為了避免 2 的指數($2^n$)被計算錯誤為 ($2^{n+1}$),因此在程式碼最開始有 `x--`
---
## 測驗 2
```c=
#include <stddef.h>
#define BITS_PER_BYTE 8
#define BITS_PER_LONG (sizeof(unsigned long) * BITS_PER_BYTE)
static inline size_t ffs(unsigned long x)
{
if (x == 0)
return 0;
size_t o = 1;
unsigned long t = ~0UL;
size_t shift = BITS_PER_LONG;
shift >>= 1;
t >>= shift;
while (shift) {
if ((x & t) == 0) {
x >>= shift;
o += shift;
}
shift >>= 1;
t >>= shift;
}
return o;
}
```
第 16 行先將 `shift` 往右位移 1 位,因此 `shift` 的值會是 `BITS_PER_LONG` >> 1 ,也就是 64 >> 1 = 32 , `t` 中的值會是前 32 位元為 0 後 32 位元為 1 。
而在後面的 while 迴圈中,每次將 `shift` 向右位移 1 位、 `t` 向右位移 `shift` 位,這樣的操作下會把 `t` 的位元中 1 的個數每次減半
while 迴圈中對於 `x`, `o` 的操作則是用 `t` 判斷 `x` 中值為 1 最低位元位置,
若是 `x & t` == 0 代表 `x` 中值為 1 的最低位元在 `t` 的位元中的左半邊 0 的區域,這時就將 `x` 向右位移 `shift` 位並且將 `o` += `shift` ,也就是將 `x` 中在最低位元為 1 右側的 `shift` 個 0 給消除,並用 `o` 將消除掉的個數進行紀錄。
最終會紀錄下消除掉的 0 的個數,但因為 `ffs` 要尋找的是第一個 1 的位置,因此 `o` 的初始值為 1。
---
## 測驗 3
```c=
struct foo_consumer {
int (*handler)(struct foo_consumer *self, void *);
struct foo_consumer *next;
};
struct foo {
struct foo_consumer *consumers;
unsigned long flags;
};
#include <stdbool.h>
/*
* For foo @foo, delete the consumer @fc.
* Return true if the @fc is deleted successfully
* or retrun false.
*/
static bool consumer_del(struct foo *foo, struct foo_consumer *fc)
{
struct foo_consumer **con;
bool ret = false;
for (con = &foo->consumers; *con, con = &(*con)->next) {
if (*con == fc) {
*con = fc->next;
ret = true;
break;
}
}
return ret;
}
```
---
## 測驗 5
```c
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define __READ_ONCE_SIZE \
({ \
switch (size) { \
case 1: \
*(uint8_t *) res = *(volatile uint8_t *) p; \
break; \
case 2: \
*(uint16_t *) res = *(volatile uint16_t *) p; \
break; \
case 4: \
*(uint32_t *) res = *(volatile uint32_t *) p; \
break; \
case 8: \
*(uint64_t *) res = *(volatile uint64_t *) p; \
break; \
default: \
memcpy((void *) res, (const void *) p, size); \
} \
})
static inline void __read_once_size(const volatile void *p, void *res, int size)
{
__READ_ONCE_SIZE;
}
#define READ_ONCE(x) \
({ \
union { \
typeof(x) __val; \
char __c[1]; \
} __u; \
__read_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
```