# 2021q1 Homework2 (quiz2)
###### tags: `linux2021`
contributed by < `Billy4195` >
> [Problem Set](https://hackmd.io/@sysprog/linux2021-quiz2)
## Problem 1
```cpp
static list_ele_t *get_middle(struct list_head *list)
{
struct list_head *fast = list->next, *slow;
list_for_each (slow, list) {
if (COND1 || COND2)
break;
fast = fast->next->next;
}
return list_entry(TTT, list_ele_t, list);
}
```
### Explanation
`get_middle()` uses two pointer `fast` and `slow` to traverse the linked-list try to get the middle element. The algorithm is that the `fast` pointer move two-step(two elements) in each iteration. When it reach the end of the list, the middle element is pointed by the `slow` pointer.
So the end condition for the traverse is when the `fast` pointer reach the end of the linked-list.
COND1 = ?
`(c) fast->next == list`
COND2 = ?
`(b) fast->next->next == list`
MMM = ?
`(e) &get_middle(&q->list)->list`
TTT = ?
`(a) slow`
### Extened questions
* [x] Explain the implementation
* [ ] Try to improve the implementation
* [ ] Read the [lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) in Linux kernel, and learn from [sysprog21/linux-list](https://github.com/sysprog21/linux-list).
* [ ] Extract the [lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) to be a standalone user level application.
* [ ] Design profiling experiments and explain the optimization in Linux kernel
* [ ] Improve implementation of quiz1 and compare with [lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) using benchmark
## Quiz 2
The `func` receive an 16bits unsigned int N, it will return an number which is power of 2 and less than or equal to N
### Explanation
```c=
uint16_t func(uint16_t N) {
/* change all right side bits to 1 */
N |= N >> 1;
N |= N >> X;
N |= N >> Y;
N |= N >> Z;
return (N + 1) >> 1;
}
```
At the first glance, it's hard to understand how it works. However, I guess the answers of `X`, `Y`, `Z` may have something to do with power of 2.
I write a simple program to test the behavior of this function, and fill the `X`, `Y` and `Z` with 2, 4, 8, respectively.
```c=
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
uint16_t func(uint16_t N) {
/* change all right side bits to 1 */
N |= N >> 1;
N |= N >> 2;
N |= N >> 4;
N |= N >> 8;
return (N + 1) >> 1;
}
int main() {
uint16_t ret = 0;
ret = func(7);
printf("func(7) = %u\n", ret);
ret = func(26);
printf("func(26) = %u\n", ret);
return 0;
}
```
```shell=
$ gcc -o test main.c
$ ./test
func(7) = 4
func(26) = 16
```
The program works as expected so far, and we need to understand what it did.
#### For N = 7
If the given `N` for the `func` is 7, the binary expression is `0000111`
After Line 7 executed (`N |= N >> 1`), `N` is still 7. After Line 7-10 is executed, the N remains `7`.
The finaly statement add 1 to N (N=8 `00001000`), and right shift 1 bit will be 4 (`00000100`).
#### For N = 26
The binary expression of 26 is `00011010`
`N |= N >> 1`
> N = `00011111` = 31
`N |= N >> 2`
> N = `00011111` = 31
`N |= N >> 4`
> N = `00011111` = 31
`N |= N >> 8`
> N = `00011111` = 31
` (N+1) >> 1`
> N = `00001000` = 16
---
According above two cases, I guess the `func` will fill all the bits on the right hand side of the leading `1` bit with `1`, and the add 1 to make it be an number is power of 2 and larger than `N`. Then the right shift 1 bit to make it to be an power of 2 and less than or equal to N.
#### Prove from the simplified uint8_t cases
To prove my guessing is correct, I take a simplified case for uint8.
The `func` will become as follows:
```c=
uint8_t func(uint8_t N) {
N |= N >> 1;
N |= N >> 2;
N |= N >> 4;
return (N+1) >> 1;
}
```
#### For the given unsigned 8 bits integer
For the given N is 128
```graphviz
digraph {
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
values [label="{bit 7 | 1 } | {bit 6 | 0 }| {bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
{ rank=same; values }
}
```
#### For N |= N >> 1
```graphviz
digraph {
node [shape=plaintext];
"Result" [fontcolor=red]
"N"
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
{ rank=same; "N"; values }
{ rank=same; values1 }
{ rank=same; "Result"; valuesR }
values [label="{bit 7 | 1 } | {bit 6 | <F0_1> 0 }| {bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
values1 [label="{<F1_0> bit 7 | 1 } | {bit 6 | 0 }| {bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
valuesR [label="{ bit 7 | 1 } | {bit 6 | <F2_1>1 }| {bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
valuesR:F2_1 [color=red]
values:F0_1 -> values1:F1_0 [style=invis]
values1:F1_0 -> valuesR:F2_1 [style=invis]
}
```
#### For N |= N >> 2
```graphviz
digraph {
node [shape=plaintext];
"Result" [fontcolor=red]
"N"
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
{ rank=same; "N"; values }
{ rank=same; values1 }
{ rank=same; "Result"; valuesR }
values [label="{<F0>bit 7 | 1 } | {bit 6 | 1 }| {<F2>bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
values1 [label="{<F0> bit 7 | 1 } | {bit 6 | 1 }| {bit 5 | 0}| {bit 4 | 0} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
valuesR [label="{<F0> bit 7 | 1 } | {bit 6 | 1 }| {<F2>bit 5 | 1}| {bit 4 | 1} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
valuesR:F1 [color=red]
values:F2 -> values1:F0 [style=invis]
values1:F0 -> valuesR:F2 [style=invis]
}
```
#### For N |= N >> 4
```graphviz
digraph {
node [shape=plaintext];
"Result" [fontcolor=red]
"N"
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
{ rank=same; "N"; values }
{ rank=same; values1 }
{ rank=same; "Result"; valuesR }
values [label="{<F0>bit 7 | 1 } | {bit 6 | 1 }| {<F2>bit 5 | 1}| {bit 4 | 1} | {<F4>bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
values1 [label="{<F0> bit 7 | 1 } | {bit 6 | 1 }| {bit 5 | 1}| {bit 4 | 1} | {bit 3 | 0} | {bit 2 | 0} | {bit 1 | 0} | {bit 0 | 0}"];
valuesR [label="{<F0> bit 7 | 1 } | {bit 6 | 1 }| {<F2>bit 5 | 1}| {bit 4 | 1} | {<F4>bit 3 | 1} | {bit 2 | 1} | {bit 1 | 1} | {bit 0 | 1}"];
valuesR:F1 [color=red]
values:F4 -> values1:F0 [style=invis]
values1:F0 -> valuesR:F4 [style=invis]
}
```
---
> TODO: Explain implementation
> TODO: Add more complex test cases
> Discuss the case of N = MAX_UINT16 or 0
X = ?
`(b) 2`
Y = ?
`(d) 4`
Z = ?
`(h) 8`
## Quiz 3
```c=
#include <stdint.h>
void bitcpy(void *_dest, /* Address of the buffer to write to */
size_t _write, /* Bit offset to start writing to */
const void *_src, /* Address of the buffer to read from */
size_t _read, /* Bit offset to start reading from */
size_t count)
{
size_t read_lhs = _read & 7;
size_t read_rhs = 8 - read_lhs;
const uint8_t *source = (const uint8_t *) _src + (_read / 8);
size_t write_lhs = _write & 7;
size_t write_rhs = 8 - write_lhs;
uint8_t *dest = (uint8_t *) _dest + (_write / 8);
static const uint8_t read_mask[] = {
0x00, /* == 0 00000000b */
0x80, /* == 1 10000000b */
0xC0, /* == 2 11000000b */
0xE0, /* == 3 11100000b */
0xF0, /* == 4 11110000b */
0xF8, /* == 5 11111000b */
0xFC, /* == 6 11111100b */
0xFE, /* == 7 11111110b */
0xFF /* == 8 11111111b */
};
static const uint8_t write_mask[] = {
0xFF, /* == 0 11111111b */
0x7F, /* == 1 01111111b */
0x3F, /* == 2 00111111b */
0x1F, /* == 3 00011111b */
0x0F, /* == 4 00001111b */
0x07, /* == 5 00000111b */
0x03, /* == 6 00000011b */
0x01, /* == 7 00000001b */
0x00 /* == 8 00000000b */
};
while (count > 0) {
uint8_t data = *source++;
size_t bitsize = (count > 8) ? 8 : count;
if (read_lhs > 0) {
RRR;
if (bitsize > read_rhs)
data |= (*source >> read_rhs);
}
if (bitsize < 8)
data &= read_mask[bitsize];
uint8_t original = *dest;
uint8_t mask = read_mask[write_lhs];
if (bitsize > write_rhs) {
/* Cross multiple bytes */
*dest++ = (original & mask) | (data >> write_lhs);
original = *dest & write_mask[bitsize - write_rhs];
*dest = original | (data << write_rhs);
} else {
// Since write_lhs + bitsize is never >= 8, no out-of-bound access.
DDD;
*dest++ = (original & mask) | (data >> write_lhs);
}
count -= bitsize;
}
}
```
### Explanation
RRR = ?
`(a) data <<= read_lhs`
For line 43-47, `data` will store the first 1 bytes data from `source`.
If `read_lhs == 0`, then `data` is just the same as `source`
If `read_lhs > 0`, we need to eliminate the bits of left hand side.
Therefore, `RRR` should be `(a)` which left shift `read_lhs` bits to make the extra bits of source to be deleted.
DDD = ?
`(b) mask |= write_mask[write_lhs + bitsize]`
## Quiz 4
CCC = ?
(a) strlen(str) + 1
(b) strlen(str) * 2
(c) s->hash_size
(d) s->size
(e) strlen(s->cstr) + 1