# 2020q1 Homework3 (quiz4) contributed by < `kylekylehaha` > > [第四週測驗](https://hackmd.io/@sysprog/linux2020-quiz4) > [作業要求](https://hackmd.io/@sysprog/ByJyk6HrL) ###### tags: `linux2020` ## 測驗一 ### 題目解析 在解題之前,必須先瞭解各個變數的用意: * `*_dest`: 寫入的目標起始位置 * `_write`: 寫入的位移量 * `*_src`: 讀取的目標起始位置 * `_read`: 讀取的位移量 * `_count`: 欲複製的 bits 個數 **KK1** * 由於 bitcpy 不一定是從第一項開始,因此必須計算偏移量。其中 `read_lhs` 為 `data` 須向左偏移的量,故我們用 bitwise 操作: `_read & 7` 來做到 `_read % 8` 的效果。當我們需要 mod $2^n$ 時,我們只需和 $2^n - 1$ 做 AND 運算即可。 * `white_lhs` 也是同樣道理,故本題答案為: > `KK1` = 7 **KK2** * 由於我們已知 `_count` 為欲複製的 bits 個數,其中在 while 內每次最多 `bitsize` 做複製,故可以得知每當 while 結束時,欲複製的 bits 數會減少 `bitsize` 個。故本題答案為: > `KK2` = bitsize ### 程式碼運作原理 ```cpp ... data = *source++; bitsize = (_count > 8) ? 8 : _count; if (read_lhs > 0) { data <<= read_lhs; if (bitsize > read_rhs) data |= (*source >> read_rhs); } ... ``` * 由於我們欲複製的位元,並未與位元組對齊,因此我們必須先將 `data` 做調整。由上面 `KK1` 的解析可得知,`read_lhs` 為需向左移的位移量,故必須```data <<= read_lhs ``` * 假如進入條件式 `if (bitsize > read_rhs)`,代表我們要複製的位元在下一項,然而因為第一行的 `data = *source++`,現在 `*source` 已為下一項,故直接將下一項資料向右移 `read_rhs`後,與 `data` 做 OR 運算。 ```cpp if (bitsize < 8) data &= read_mask[bitsize]; ``` * 如果 `bitsize` 小於 8 時,須將多餘的部分,用 mask 做消除。 * 做到此時,已確定 `data` 為這次 while 內要複製的位元。 --- 寫入分為兩種 case: 1. `white_lhs` > 0: 需要調整寫入的位置。 2. `white_lhs` == 0: 直接將 `data` 寫入 `*dest` 即可。 我們先討論 case2 ```cpp ... else { if (bitsize < 8) data = data | (original & write_mask[bitsize]); dest++ = data; } ... ``` * 如果寫入的 `data` 長度小於 8,則必須確保不能污染到原本的資料 `original`,故必須修正 `data`,使 `data` 保留原本的部分以及欲寫入的部分。 * e.g. `bitsize` = 4,代表欲寫入的 `data` 只有前 4 個 bit 是正確的,而 `original & write_mask[bitsize] ` = `original & 00001111b`,即保留後面四個原本的 bit,前面四個為欲修改的 bit。 接著,我們討論 case1,其中又分為兩種情況: ```cpp if (write_lhs > 0) { mask = read_mask[write_lhs]; if (bitsize > write_rhs) { *dest++ = (original & mask) | (data >> write_lhs); original = *dest & write_mask[bitsize - write_rhs]; *dest = original | (data << write_rhs); } else { if ((bitsize - write_lhs) > 0) mask = mask | write_mask[8 - (bitsize - write_lhs)]; *dest++ = (original & mask) | (data >> write_lhs); } ... ``` `mask` 為用來保留原來資料的部分。 **bitsize > write_rhs**(代表欲寫入的資料跨過一個位元組。) ```cpp *dest++ = (original & mask) | (data >> write_lhs) ``` * `original & mask`: 用來保留原本的部分,避免被污染。 * `data >> write_lhs`: 將 `data` 右移到正確的位置,來避免污染原本的資料。 * 兩者做 OR 運算後為正確寫入該 `*dest` 的資料。 ```cpp original = *dest & write_mask[bitsize - write_rhs]; ``` * 由於上一行 `*dest++`,故此時 `*dest` 為下一項的值。 * `write_mask[bitsize - write_rhs` 為欲保留的部分 * 故兩者做 AND 運算即可得正確欲保留的部分。 ```cpp *dest = original | (data << write_rhs) ``` * `data << write_rhs`: 將欲寫入的部分左移到正確位置。 * 與 `original` 做 OR 運算,即為正確寫入的資料。 **bitsize <= write_rhs** * 看是否需要更新 `mask`,如果不需更新則直接寫入 `*dest`。 * 如果須更新,則更新 `mask` 來保留正確的 `original` 部分。 --- ```cpp _count -= bitsize; ``` * 更新還剩多少個位元需要被複製。 ### 延伸問題 #### 改寫程式碼 ## 測驗二 ### 題目解析 **VV1** * 由名字可以知道,此 marco 用來找到給予 `s` ,大於 `s`的最小 2 的次方。 * if `s` 屬於 2 的次方,則 `s` & `s-1` 會等於 `0`,代表 `s` 為 2 的次方。 e.g. s(01000),s-1(00111) 則`s & (s-1) == 0` ,故回傳 `s` * if `s` 不屬於 2 的次方,則 `s` & `s-1` 會等於 1,故回傳 `1 << (64 - _builtin_clzl(s))`,其值為:` 2^(64 - _builtin_clz(s))` $\geq$ s > `vv1` = s & (s-1) == 0 **VV2** * 由名字可以判斷,此為 pop stack 內的 element,故必須將 vector 內的個數 - 1。 * 其中, `size` 為 vector 內的個數, `capacity` 為 vector 內的最大容量,故我們可以知道答案為: > `vv2` = v.size -= 1 > ### 程式碼運作原理 ```cpp #define STRUCT_BODY(type) \ struct { \ size_t size : 54, on_heap : 1, capacity : 6, flag1 : 1, flag2 : 1,\ flag3 : 1; \ type *ptr; \ } ``` * 在這次的題目的 macro ,出現了[quiz2](https://hackmd.io/@sysprog/linux2020-quiz2) 提到的 [folly:fbstring](https://github.com/facebook/folly/blob/master/folly/FBString.h): 當字串長度太大時,用 heap,反之則用 stack 來儲存。 ```cpp #define v(t, s, name, ...) \ union { \ STRUCT_BODY(t); \ struct { \ size_t filler; \ t buf[NEXT_POWER_OF_2(s)]; \ }; \ } name __attribute__((cleanup(vec_free))) = {.buf = {__VA_ARGS__}}; \ name.size = sizeof((__typeof__(name.buf[0])[]){0, __VA_ARGS__}) / \ sizeof(name.buf[0]) - 1; \ name.capacity = sizeof(name.buf) / sizeof(name.buf[0]) ``` * `__VA_ARGS__` 為 `v(t, s, name, ...)` 中, `...` 的內容,也就是第四項以後的參數。這邊是我第一次看到,故查了一下資料[21世紀C語言之17](https://ithelp.ithome.com.tw/articles/10160393)。 * `__attribute__((cleanup(vec_free))) = {.buf = {__VA_ARGS__}};` * 將 `__VA_ARGS__` 的內容存入 buf。 * 當被形容的變數離開時,會呼叫 `vec_free`來釋放記憶體。 ### 延伸問題 #### 效率分析 #### 實作 vector 經典操作 ## 參考資料 * [21世紀C語言之17](https://ithelp.ithome.com.tw/articles/10160393) * [homework2 (quiz2)](https://hackmd.io/@kylekylehaha/SkTYL62BU)