# 2023q1 Homework3 (quiz3) contributed by < `csm1735` > ## 測驗 `1` --- ## 測驗 `2` --- ## 測驗 `3` ### LFSR 的運作原理及案例 Linear feedback shift register (LFSR) 是指給定前一狀態,將該輸出的線性函數作為輸入的移位暫存器 ```c static void lfsr(uint64_t *up) { uint64_t new_bit = ((*up) ^ ((*up) >> 3) ^ ((*up) >> 30) ^ ((*up) >> 55)) & 1u; /* don't have to map '+1' in polynomial */ *up = ((*up) >> 1) | (new_bit << 63); /* shift *up by 1 to RIGHT and insert new_bit at "empty" position */ } ``` 計算出 `new_bit` 後將 `up` 右移 1 位,再將 `new_bit`補到最高位。 ```shell $ git log --grep lfsr ``` 透過該指令可以去搜尋包含關鍵字 `lfsr` 的 commit message 可以發現在此次 [commit](https://github.com/torvalds/linux/commit/dec0fb3946c478e7bb6165a1b7a03092ceefd419) 提及到了 `lfsr` ,而其所提到的 `jent_lfsr_time` 函式可以在 [linux/crypto/jitterentropy.c](https://github.com/torvalds/linux/blob/master/crypto/jitterentropy.c) 中發現 ### 解釋程式碼原理 `log2_64` 功能為計算以 2 為底的 `x` 的對數,且下取至整數 (即 `floor` ),從程式碼可看到不斷的透過右移與 `if` 來判斷 `x` 的對數值會落在哪個區間,再以該區間的最小對數值為基底,去加上該部份透過 `log_table_256` 所換算出來的對數值。 ```c static const char log_table_256[256] = { #define _(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, _(4), _(5), _(5), _(6), _(6), _(6), _(6), _(7), _(7), _(7), _(7), _(7), _(7), _(7), _(7), #undef _ }; ``` 這邊解釋一下 `log_table_256` 的作法,一開始看到時並沒有看懂,仔細看看後發現有跡可循,才理解是透過 macro 將 `_(n)` 定義成以逗號隔開的 16 個 n 值,因為像是對數值為 4 的為 16 ~ 31 共 16 個值,對數值為 5 的為 32 ~ 63 共 32 個值,而透過這種作法,我們只需要寫 `_(4), _(5), _(5)` 即可,能夠達到程式碼更精簡的效果。 ```c unsigned int bucket_number(uint64_t x) { uint64_t mask111 = (1 << (N_BITS + 1)) - 1; uint64_t mask011 = (1 << (N_BITS)) - 1; /* one 1 less */ unsigned char leq = ((x & mask111) < N_BUCKETS); /* leq (less or equal) is 0 or 1. */ return (leq * (x & mask111)) + ((1 - leq) * ((x >> (N_BITS + 1)) & mask011)); /* 'x >> (N_BITS + 1)' : take different set of bits -> better uniformity. * '... & mask011' guarantees that the result is less or equal N_BUCKETS. */ } ``` 而此處透過 mask 的作法,可以保證結果可以小於 `N_BUCKETS` ,如果與 `mask111` 做 & 後的結果不小於 `N_BUCKETS` ,則改使用 `mask011` 。 --- ## 測驗 `4` ### 解釋程式碼原理 `ceil_log2` 功能為計算以 2 為底的 `x` 的對數,且上取至整數 (即 `ceil` ) ```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; } ``` 原理是每次去檢查 `x` 是否大於特定的 2 的冪次,如果有的話則透過左移去對特定的 bit 做 set ,並將 `x` 做右移去繼續檢查,而我們可以看出最後沒有繼續去對 `x > 1` 的狀況去檢查,因此推斷剩餘的程式碼為: ```c r |= shift; shift = (x > 0x1) << 0; x >>= shift; r |= shift; ``` 考量到 `KKKK` 的格式,可以將此段改寫為 `r | shift | x >> 1`