###### tags: `Linux` # 2023q1 Homework3 (quiz3) contributed by < [`shhung`](https://github.com/shhung) > [quiz3](https://hackmd.io/@sysprog/linux2023-quiz3) ## 測驗`1` 觀察結構的宣告 ```c typedef struct __node { uintptr_t color; struct __node *left, *right; struct __node *next; long value; } node_t __attribute__((aligned(sizeof(long)))); ``` aligned 8 個 byte,代表定址都會是 8 的倍數,也就是最低位的 3 個位元不會被用到,可以利用這三個位元來做顏色的設定。 ```c #define rb_parent(r) ((node_t *) (((r)->color & ~7))) // AAAA #define rb_color(r) ((color_t) (r)->color & 1) ``` 可以看到 `rb_color` 是取最後一個位元當作顏色,所以我們要將顏色設定在最後一個位元 ```c // BBBB #define rb_set_red(r) \ do { \ (r)->color &= ~1; \ } while (0) // CCCC #define rb_set_black(r) \ do { \ (r)->color |= 1; \ } while (0) ``` `DDDD` 和 `EEEE` 所在的部份是在做 tree 的尋訪,根據程式碼邏輯分別為向左和向右 ```c /* Traverse the tree until we hit the end or find a side that is NULL */ for (node_t *cur = obj->head;;) { int res = obj->comparator(&node->value, &cur->value); if (!res) /* If the key matches something else, don't insert */ assert(0 && "not support repetitive value"); if (res < 0) { if (!cur->left) { cur->left = node; rb_set_parent(node, cur); cmap_fix_colors(obj, node); break; } cur = cur->left; // DDDD } else { if (!cur->right) { cur->right = node; rb_set_parent(node, cur); cmap_fix_colors(obj, node); break; } cur = cur->right; // EEEE } } ``` `FFFF` ,`GGGG`, `HHHH` 位於 `tree_sort` 之中,程式邏輯是將數列依序存如由紅黑樹實做的 `cmap` 結構中,而在插入的過程中依照大小存放,因此當資料都放進去後,循序走訪將資料都取出後即可獲得排序完的數列。 ```c void tree_sort(node_t **list) { node_t **record = list; cmap_t map = cmap_new(sizeof(long), sizeof(NULL), cmap_cmp_int); while (*list) { cmap_insert(map, *list, NULL); // list = FFFF; list = &(*list)->next; } node_t *node = cmap_first(map), *first = node; for (; node; node = cmap_next(node)) { *list = node; // list = GGGG; list = &(*list)->next; } // HHHH; *list = NULL; *record = first; free(map); } ``` ## 測驗 `4` 先思考 `log` 用 bitwise 來看的話要怎麼找, `8` 的二進位是 `b'00001000`,取 `log` 就是 `3`,再觀察其他數值發現最高位往後看第一個 `1` 出現的位置就是 `log` 的答案,類似 `quiz2` 用到的 `__builtin_ffs`,而資料型態為 `32` 位元,所以每次會比較一半的位元, $2^{n-1}, ..., 2^1$ , `r` 用來累加比較的結果, `shift` 則用來暫存每一次比較的結果,因此在 `return` 的地方也必須再做一次 `r | shift` 將 `(x > 0x3) << 1` 的結果加起來,而最後 `x` 還有 `2` 個位元沒有比到, `x` 可能是 `b'11` `b'10` `b'01` `b'00`,這邊只要將 `x` 右移 `1` 位就能得到結果不需要比較,因此 `return` 要再加上 `x >> 1`,而 `()`外的 `+1` 是因為一開始的 `x--`,使得原本整數解的部份比原先少 `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 (KKKK) + 1; return (r | shift | x >> 1) + 1; } ```