---
tags: linux kernel class
---
# 2023q1 Homework2 (quiz2)
contributed by < `willwillhi1` >
### 測驗一
```c
uint64_t next_pow2(uint64_t x)
{
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> AAAA;
x |= x >> 16;
x |= x >> BBBB;
return CCCC;
}
```
此實作的原理是將最高位的一 `bit` 依照 `1`, `2`, `4`, `8`, `16`, `32` 的順序依序向右移,且每次都要用 `or` 把 1 寫到原本的 `x`,最後再對 `x` 加一找到大於且最接近 `x` 的冪的數。
舉例來說如果 `x` 為 (0010 0100)~2~,做到最後 `x` 會變成 (0011 1111)~2~,所以再對它加一就可以得到 (0100 0000)~2~。
* 用 `__builtin_clzl` 改寫,先去看其定義
> Similar to __builtin_clz, except the argument type is unsigned long.
* `__builtin_clz` 的定義為
> Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
`__builtin_clzl` 簡單來說就是回傳 `leading zero` 的個數
因此可以把上面的程式改寫為:
```c
uint64_t next_pow2(uint64_t x)
{
int count = __builtin_clzl(x);
int num = 64-count;
int ans = 1;
while (num) {
ans <<= 1;
num--;
}
return ans;
}
```
---
### 測驗二
```c
int concatenatedBinary(int n)
{
const int M = 1e9 + 7;
int len = 0; /* the bit length to be shifted */
/* use long here as it potentially could overflow for int */
long ans = 0;
for (int i = 1; i <= n; i++) {
// if it is power of 2, we increase the bit length
if (!(DDDD))
len++;
ans = (i | (EEEE)) % M;
}
return ans;
}
```
* `if it is power of 2, we increase the bit length` 所以 `DDDD` 要做的事情就是判斷 `i` 是否為 2 的冪,是就把 `len` 加一。
* 由上述可知 `DDDD` 為 `i & (i-1)`,因為如果 `i` 為 2 的冪, `i & (i-1)` 的結果就會是 `0`。
* 要把 `len` 加一的情況是因為位移需要,舉例來說如果 `i = 1`, `len = 1`,則當 `i = 2` 時因為 `2 = 0b10`,由最低位數到最高位的 `1` 的總長度為 `2` 個 bits,所以位移共需要 `2` 個 `bit`,`len` 要加一。
* 由上可知 `EEEE` 就是 `ans << len`。
---
### 測驗三
參考 [Ahsen-lab](https://hackmd.io/@Ahsen-lab/linux2023_quiz2#%E6%B8%AC%E9%A9%97-3) 同學的講解
此題為運算出 `utf-8` 字元數量,原理為只要判斷出首位元組和後續位元組就可以得出 `utf-8` 字元數量。而後續位元組都有一個共通點:最高的兩個位元為 `10`。
```c=
size_t swar_count_utf8(const char *buf, size_t len)
{
const uint64_t *qword = (const uint64_t *) buf;
const uint64_t *end = qword + (len >> 3);
size_t count = 0;
for (; qword != end; qword++) {
const uint64_t t0 = *qword;
const uint64_t t1 = ~t0;
const uint64_t t2 = t1 & 0x04040404040404040llu;
const uint64_t t3 = t2 + t2;
const uint64_t t4 = t0 & t3;
count += __builtin_popcountll(t4);
}
count = (1 << 3) * (len / 8) - count;
count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0;
return count;
}
```
首先,以每 `64 bits` 為單位,所以先把 `buf` 轉換成 `uint64_t`,之後 `end` 表示為 `buf` 的最後一個滿 `64 bits` 的位址。
```c=
const uint64_t *qword = (const uint64_t *) buf;
const uint64_t *end = qword + (len >> 3);
```
然後,對於每個 `64 bits` 無號整數,使用以下步驟操作來計算其中後續位元組 ( continuation byte(s) ) 的數量:
```c=
size_t count = 0;
for (; qword != end; qword++) {
const uint64_t t0 = *qword;
const uint64_t t1 = ~t0;
const uint64_t t2 = t1 & 0x04040404040404040llu;
const uint64_t t3 = t2 + t2;
const uint64_t t4 = t0 & t3;
count += __builtin_popcountll(t4);
}
```
1. 輸入的位元組
```c=
t0 = [00xx.xxxx|01xx.xxxx|10xx.xxxx|11xx.xxxx]
^^^^^^^^^
continuation byte
```
2. 反向運算 (not bit6)
```c=
t1 = ~t0
t1 = [11xx.xxxx|10xx.xxxx|01xx.xxxx|00xx.xxxx]
```
3. 隔離反向的第 6 個位元
```c=
t2 = t1 & 0x40404040
t2 = [0100.0000|0000.0000|0100.0000|0000.0000]
```
4. 對第 6 個位元,向左位移 1 個位元 (移至第 7 個位元的位置)
```c=
t3 = t2 + t2 = t2 << 1
t3 = [1000.0000|0000.0000|1000.0000|0000.0000]
```
5. 進行 not bit6 and bit7
```c=
t4 = t0 & t3
t4 = [00xx.xxxx|01xx.xxxx|10xx.xxxx|11xx.xxxx] &
[1000.0000|0000.0000|1000.0000|0000.0000]
= [0000.0000|0000.0000|1000.0000|0000.0000]
^^^^^^^^^
the only non-zero byte
```
6. 以 population count 指令計算,即 popcount(t4)
7. 最後用總長(以 `64 bits` 為單位)扣掉 `count` 得到 `UTF-8` 字元的數量後,再用 `count_utf8` 處理剩下不足 `64 bits` 的部分(也就是長度為 `len & 7 = len % 8` 的部分)
```c=
count = (1 << 3) * (len / 8) - count;
count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0;
return count;
```
---
### 測驗四
要判斷 `x` 是否符合以下 `pattern`:
```
pattern: 0x8000 = 0b1000 0000 0000 0000
pattern: 0xc000 = 0b1100 0000 0000 0000
pattern: 0xe000 = 0b1110 0000 0000 0000
pattern: 0xf000 = 0b1111 0000 0000 0000
pattern: 0xf800 = 0b1111 1000 0000 0000
pattern: 0xfc00 = 0b1111 1100 0000 0000
pattern: 0xfe00 = 0b1111 1110 0000 0000
pattern: 0xff00 = 0b1111 1111 0000 0000
pattern: 0xff80 = 0b1111 1111 1000 0000
pattern: 0xffc0 = 0b1111 1111 1100 0000
pattern: 0xffe0 = 0b1111 1111 1110 0000
pattern: 0xfff0 = 0b1111 1111 1111 0000
pattern: 0xfff8 = 0b1111 1111 1111 1000
pattern: 0xfffc = 0b1111 1111 1111 1100
pattern: 0xfffe = 0b1111 1111 1111 1110
pattern: 0xffff = 0b1111 1111 1111 1111
```
```c
bool is_pattern(uint16_t x)
{
const uint16_t n = ~x+1;
return (n ^ x) < x;
}
```
* `EEEE = ~x+1`,這樣做的效果可以讓 `n^x` 的結果保證小於 `x`。
> `-x` 亦可 :notes: jserv
* 以 (1111 1100)~2~ 和 (1111 0100)~2~ 為例
* (1111 1**1**00)~2~ 的 `2` 的補數是 (0000 0100)~2~,與原本的數做 `XOR` 為 (1111 1**0**00)~2~,效果是把原本的數的最低位的 `1` 改成 `0`,所以最後的結果一定會小於原本的數。
* 但是 (1111 **0**100)~2~ 的 `2` 的補數是 (0000 1100)~2~,與原本的數做 `XOR` 為 (1111 **1**000)~2~,可以發現除了把最低位的 `1` 改成 `0` 之外,還會把原本的數的 `1` 中間的 `0` 的位元會變成 `1` (粗體的部分),導致結果大於原本的數。
:::warning
尊重視障朋友 (含色弱者) 閱讀的權益,解說程式碼時,避免談及特別的顏色。
:notes: jserv
:::