# 2021q1 Homework5 (quiz5)
contributed by < `secminhr` >
# Quiz Problems
## VV1 = ?
```c
#define NEXT_POWER_OF_2(s) \
VV1 ? s : (size_t) 1 << (64 - __builtin_clzl(s))
```
We can see that if `VV1` is true then `s` will be returned, which indicates that `s` is a power of 2.
Since a power of 2 has only a bit set, we can easily see that `s & (s-1)` must be 0, so `VV1` is `!(s & (s-1))`.
The answer is `(e)`.
## VV2 = ?
```c
#define vec_pop_back(v) (void) (VV2)
```
When we pop an item from a vector, the size of the vector should decrease by 1.`VV2` is therefore `v.size -= 1`.
The anwser is `(a)`.
## VV3 = ?
```c
static NON_NULL void __vec_push_back(void *restrict vec,
void *restrict e,
size_t elemsize,
size_t capacity)
{
...
if (v->on_heap) {
if (v->size == capacity)
v->ptr = realloc(v->ptr, VV3);
memcpy(&v->ptr[v->size++ * elemsize], e, elemsize);
} else {
...
}
}
```
We have to know first that the representation of `capacity` depends on wherther it's on heap or on stack. [See this section first](https://hackmd.io/1eFVSD3VQ2iwFB4Bly4Lcw#vec_capacity).
After figuring out the representation, it's clear now that `VV3` should be
```
(v->capacity)++;
elemsize * (size_t) 1 << v->capacity
```
Put it into one line we have:
`elemsize * (size_t) 1 << ++v->capacity`
The answer is `(b)`.
## WWW = ?
```c
static void socket_write_loop(struct cr *o, int fd, byte_queue_t *in)
{
static uint8_t *b;
cr_begin(o);
for (;;) {
cr_wait(o, WWW);
b = cr_queue_pop(in);
cr_sys(o, send(fd, b, 1, 0));
}
cr_end(o);
}
```
`WWW` is used with `cr_wait`, let's check that:
```c
#define cr_wait(o, cond) \
do { \
cr_label(o, CR_BLOCKED); \
if (!(cond)) \
return; \
} while (0)
```
When `cond` isn't satisfied, `return` will be called to exit current function. We can view this behaviour as a protection for the following lines.
Since next line is `cr_queue_pop`, we want to make sure that the queue isn't empty, `WWW` is therefore `!cr_queue_empty(in)`.
The answer is `(b)`.
## LLL = ?
```c
static void stdin_loop(struct cr *o, byte_queue_t *out)
{
static uint8_t b;
static int r;
cr_begin(o);
for (;;) {
cr_sys(o, r = read(STDIN_FILENO, &b, 1));
if (r == 0) {
cr_wait(o, cr_queue_empty(out));
cr_exit(o, 1);
}
cr_wait(o, LLL);
cr_queue_push(out, b);
}
cr_end(o);
}
```
Same idea as `WWW`, except that this time we want to make sure the queue isn't full before push.
So `LLL` should be `!cr_queue_full(out)`.
The answer is `(d)`.
# Extended Problems
## Test 1
### Principle
### Core Structure
The structure of our vector is in one of the following form:
- on-stack
```graphviz
graph {
struct[label="{metadata|{size|on_heap=0|capacity|unused}}|buf[0]|buf[1]|...", shape=record]
}
```
- on-heap
```graphviz
graph {
struct[label="{metadata|{size|on_heap=1|capacity|unused}}|ptr", shape=record]
}
```
Note that the representation of `capacity` [is different in 2 forms](https://hackmd.io/1eFVSD3VQ2iwFB4Bly4Lcw#vec_capacity).
### Macros
### Supportive Macros
These macros are used by other macros.
#### STRUCT_BODY
```c
#define STRUCT_BODY(type) \
struct { \
size_t size : 54, on_heap : 1, capacity : 6, flag1 : 1, flag2 : 1, \
flag3 : 1; \
type *ptr; \
}
```
This macro declare a struct corresponds to the on-heap form.
#### NEXT_POWER_OF_2
```c
#define NEXT_POWER_OF_2(s) \
!(s & (s - 1)) ? s : (size_t) 1 << (64 - __builtin_clzl(s))
```
Find a power of 2 that is greater or equal to `s` .
#### vec_data
```c
#define vec_data(v) (v.on_heap ? v.ptr : v.buf) /* always contiguous buffer */
```
Provide a way to access stored data without concerning vector's structure.
#### vec_elemsize
```c
#define vec_elemsize(v) sizeof(v.buf[0])
```
Get size of element.
Note that this macro can be used even if the vector stores its data on heap.
### Vector Macros
These macros provide ways to manipulate a vector without exposing its actual structure.
#### v
```c
#define v(t, s, name, ...) \
(void) ((struct { \
_Static_assert(s <= 8, "it is too big"); \
int dummy; \
}){1}); \
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])
```
Create a vector called `name` with on-stack form.
if `s` is greater than 8, compile will not pass due to `_Static_assert`.
#### vec_size
```c
#define vec_size(v) v.size
```
The size of `v`.
#### vec_capacity
```c
#define vec_capacity(v) \
(v.on_heap ? (size_t) 1 << v.capacity : sizeof(v.buf) / sizeof(v.buf[0]))
```
`vec_capacity` gives the capacity of `v`.
The ternary operator `?:` indicates that the representation of `v.capacity` is different, and it depends on whether it's on stack or on heap.
If the vector stores it's elements on stack, then the capacity is calculated by `sizeof(v.buf) / sizeof(v.buf[0])`.
Otherwise, the capacity is `(size_t) 1 << v.capacity`, which is essentially $2^{\ v.capacity}$.
Notice that if the vector is on heap, when `v.capacity` increases by 1, the space the vector uses is doubled.
#### vec_pos
```c
#define vec_pos(v, n) vec_data(v)[n] /* lvalue */
```
Access to n-th element of vector data.
#### vec_reserve
```c
#define vec_reserve(v, n) __vec_reserve(&v, n, vec_elemsize(v), vec_capacity(v))
```
Reserve space for at least `n` elements.
This macro will expand to the [__vec_reserve](https://hackmd.io/1eFVSD3VQ2iwFB4Bly4Lcw#__vec_reserve) functon call.
#### vec_push_back
```c
#define vec_push_back(v, e) \
__vec_push_back(&v, &(__typeof__(v.buf[0])[]){e}, vec_elemsize(v), \
vec_capacity(v))
```
Push `e` into the vector.
Thsimacro will expand to the [__vec_push_back](https://hackmd.io/1eFVSD3VQ2iwFB4Bly4Lcw#__vec_push_back) function call.
`&(__typeof__(v.buf[0])[]){e}` will create a pointer of an array whose first element is `e`.
Note that the address of an array and that of the first element in array is the same, this gives a reason why `__vec_push_back` can directly use `memcpy`.
#### vec_pop_back
```c
#define vec_pop_back(v) (void) (v.size -= 1)
```
Decrease `size` by 1 to give the **effect** of poping out an element.
### Functions
#### ilog2
```c
static inline int ilog2(size_t n)
{
return 64 - __builtin_clzl(n) - ((n & (n - 1)) == 0);
}
```
Calculate `ceil(log2(n))`.
Examples:
- `ilog2(7)` returns 3
- `ilog2(5)` returns 3
- `ilog2(4)` returns 2
#### __vec_reserve
```c
static NON_NULL void __vec_reserve(void *vec,
size_t n,
size_t elemsize,
size_t capacity)
{
union {
STRUCT_BODY(void);
struct {
size_t filler;
char buf[];
};
} *v = vec;
if (n > capacity) {
if (v->on_heap) {
v->ptr = realloc(v->ptr,
elemsize * (size_t) 1 << (v->capacity = ilog2(n)));
} else {
void *tmp =
malloc(elemsize * (size_t) 1 << (v->capacity = ilog2(n)));
memcpy(tmp, v->buf, elemsize * v->size);
v->ptr = tmp;
v->on_heap = 1;
}
}
}
```
`NON_NULL` will expand to `__attribute__((nonnull))` to avoid `vec` being a null pointer.
This function make `v`'s capacity being the smalllest power of 2 that is greater than or equal to `n`.
#### __vec_push_back
```c
static NON_NULL void __vec_push_back(void *restrict vec,
void *restrict e,
size_t elemsize,
size_t capacity)
{
union {
STRUCT_BODY(char);
struct {
size_t filler;
char buf[];
};
} *v = vec;
if (v->on_heap) {
if (v->size == capacity)
v->ptr = realloc(v->ptr, elemsize * (size_t) 1 << ++v->capacity);
memcpy(&v->ptr[v->size++ * elemsize], e, elemsize);
} else {
if (v->size == capacity) {
void *tmp =
malloc(elemsize * (size_t) 1 << (v->capacity = capacity + 1));
memcpy(tmp, v->buf, elemsize * v->size);
v->ptr = tmp;
v->on_heap = 1;
memcpy(&v->ptr[v->size++ * elemsize], e, elemsize);
} else
memcpy(&v->buf[v->size++ * elemsize], e, elemsize);
}
}
```
Put `*e` to the end of `vec`.
If the capacity of `vec` is not big enough, it will expand in order to accept more elements.
The expanding behaviour of capacity is described as below:
- When elements of `vec` stores on heap
In this case, the capacity is doubled. Check [the representation of capacity](https://hackmd.io/1eFVSD3VQ2iwFB4Bly4Lcw#vec_capacity) in case you forgot.
- When elements of `vec` stores on stack
In this case, the elements will be moved to heap, and the capacity of `vec` will become $2^{1+(old\ capacity)}$.
For example, given a vector `v(float, 8, vec)`, its capacity after 9-th `vec_push_back` will be $512=2^{1+8}$.
### Verify layout
## Test 2