|
| 1 | +// panic: refcount |
| 2 | +#include <assert.h> |
| 3 | +#include <stddef.h> |
| 4 | +#include <stdlib.h> |
| 5 | +#include <string.h> |
| 6 | + |
| 7 | +struct In { |
| 8 | + short a; |
| 9 | + int b; |
| 10 | +}; |
| 11 | + |
| 12 | +struct S { |
| 13 | + int x; |
| 14 | + struct In in; |
| 15 | + unsigned char bytes[4]; |
| 16 | + int arr[3]; |
| 17 | + long tail; |
| 18 | +}; |
| 19 | + |
| 20 | +// The value of type T whose bytes are all v. |
| 21 | +#define REP(T, v) ((T)(0x0101010101010101ULL * (unsigned char)(v))) |
| 22 | + |
| 23 | +static void set_bytes(unsigned char *p, size_t n, unsigned char v) { |
| 24 | + for (size_t i = 0; i < n; i++) |
| 25 | + p[i] = v; |
| 26 | +} |
| 27 | + |
| 28 | +static int all_bytes(const unsigned char *p, size_t n, unsigned char v) { |
| 29 | + for (size_t i = 0; i < n; i++) |
| 30 | + if (p[i] != v) |
| 31 | + return 0; |
| 32 | + return 1; |
| 33 | +} |
| 34 | + |
| 35 | +// Checks that field F of type T, at byte OFF of *s, is written through, in |
| 36 | +// both directions, |
| 37 | +// by the bytes of the field and by the bytes of the whole struct: |
| 38 | +// - bytes of the field -> field, and field -> bytes of the field, |
| 39 | +// - bytes of the struct -> field, and field -> bytes of the struct. |
| 40 | +// Leaves the field with all bytes equal to v + 3. |
| 41 | +#define CHECK_FIELD(s, F, T, OFF, v) \ |
| 42 | + do { \ |
| 43 | + unsigned char *fb = (unsigned char *)&(s)->F; \ |
| 44 | + unsigned char *sb = (unsigned char *)(s) + (OFF); \ |
| 45 | + set_bytes(fb, sizeof(T), (v)); \ |
| 46 | + assert((s)->F == REP(T, (v))); \ |
| 47 | + assert(all_bytes(sb, sizeof(T), (v))); \ |
| 48 | + (s)->F = REP(T, (v) + 1); \ |
| 49 | + assert(all_bytes(fb, sizeof(T), (v) + 1)); \ |
| 50 | + assert(all_bytes(sb, sizeof(T), (v) + 1)); \ |
| 51 | + set_bytes(sb, sizeof(T), (v) + 2); \ |
| 52 | + assert((s)->F == REP(T, (v) + 2)); \ |
| 53 | + assert(all_bytes(fb, sizeof(T), (v) + 2)); \ |
| 54 | + (s)->F = REP(T, (v) + 3); \ |
| 55 | + assert(all_bytes(sb, sizeof(T), (v) + 3)); \ |
| 56 | + assert(all_bytes(fb, sizeof(T), (v) + 3)); \ |
| 57 | + } while (0) |
| 58 | + |
| 59 | +// Checks that all fields hold the values CHECK_FIELD left in them, i.e., that |
| 60 | +// no write through bytes clobbered a sibling field. |
| 61 | +static void check_final(struct S *s) { |
| 62 | + assert(s->x == REP(int, 0x13)); |
| 63 | + assert(s->in.a == REP(short, 0x23)); |
| 64 | + assert(s->in.b == REP(int, 0x33)); |
| 65 | + assert(s->bytes[0] == 0x43 && s->bytes[1] == 0x53); |
| 66 | + assert(s->bytes[2] == 0x63 && s->bytes[3] == 0x73); |
| 67 | + assert(s->arr[0] == REP(int, 0x83) && s->arr[1] == REP(int, 0x93)); |
| 68 | + assert(s->arr[2] == REP(int, 0x0b)); |
| 69 | + assert(s->tail == REP(long, 0x1b)); |
| 70 | +} |
| 71 | + |
| 72 | +#define IN_OFF offsetof(struct S, in) |
| 73 | +#define BYTES_OFF offsetof(struct S, bytes) |
| 74 | +#define ARR_OFF offsetof(struct S, arr) |
| 75 | + |
| 76 | +static void check_struct(struct S *s) { |
| 77 | + // Scalar fields, fields of a nested struct, and array elements. |
| 78 | + CHECK_FIELD(s, x, int, offsetof(struct S, x), 0x10); |
| 79 | + CHECK_FIELD(s, in.a, short, IN_OFF + offsetof(struct In, a), 0x20); |
| 80 | + CHECK_FIELD(s, in.b, int, IN_OFF + offsetof(struct In, b), 0x30); |
| 81 | + CHECK_FIELD(s, bytes[0], unsigned char, BYTES_OFF + 0, 0x40); |
| 82 | + CHECK_FIELD(s, bytes[1], unsigned char, BYTES_OFF + 1, 0x50); |
| 83 | + CHECK_FIELD(s, bytes[2], unsigned char, BYTES_OFF + 2, 0x60); |
| 84 | + CHECK_FIELD(s, bytes[3], unsigned char, BYTES_OFF + 3, 0x70); |
| 85 | + CHECK_FIELD(s, arr[0], int, ARR_OFF + 0 * sizeof(int), 0x80); |
| 86 | + CHECK_FIELD(s, arr[1], int, ARR_OFF + 1 * sizeof(int), 0x90); |
| 87 | + CHECK_FIELD(s, arr[2], int, ARR_OFF + 2 * sizeof(int), 0x08); |
| 88 | + CHECK_FIELD(s, tail, long, offsetof(struct S, tail), 0x18); |
| 89 | + check_final(s); |
| 90 | + |
| 91 | + // A nested struct as a whole, through its bytes. |
| 92 | + set_bytes((unsigned char *)&s->in, sizeof(struct In), 0x21); |
| 93 | + assert(s->in.a == REP(short, 0x21) && s->in.b == REP(int, 0x21)); |
| 94 | + struct In in = {REP(short, 0x23), REP(int, 0x33)}; |
| 95 | + s->in = in; |
| 96 | + assert(all_bytes((unsigned char *)&s->in.a, sizeof(short), 0x23)); |
| 97 | + assert(all_bytes((unsigned char *)&s->in.b, sizeof(int), 0x33)); |
| 98 | + |
| 99 | + // Array fields as a whole, through their bytes and through a different |
| 100 | + // element type. |
| 101 | + set_bytes((unsigned char *)s->arr, sizeof(s->arr), 0x85); |
| 102 | + assert(s->arr[0] == REP(int, 0x85) && s->arr[2] == REP(int, 0x85)); |
| 103 | + set_bytes((unsigned char *)&s->arr, sizeof(s->arr), 0x86); |
| 104 | + assert(s->arr[1] == REP(int, 0x86)); |
| 105 | + s->arr[0] = REP(int, 0x83); |
| 106 | + s->arr[1] = REP(int, 0x93); |
| 107 | + s->arr[2] = REP(int, 0x0b); |
| 108 | + int *ib = (int *)s->bytes; |
| 109 | + *ib = REP(int, 0x45); |
| 110 | + assert(all_bytes(s->bytes, sizeof(s->bytes), 0x45)); |
| 111 | + s->bytes[0] = 0x43; |
| 112 | + s->bytes[1] = 0x53; |
| 113 | + s->bytes[2] = 0x63; |
| 114 | + s->bytes[3] = 0x73; |
| 115 | + assert(*ib == *(int *)(unsigned char *)&s->bytes); |
| 116 | + |
| 117 | + // Through void *, memcpy and memset. |
| 118 | + void *v = &s->in.b; |
| 119 | + *(int *)v = 11; |
| 120 | + assert(s->in.b == 11); |
| 121 | + int twelve = 12; |
| 122 | + memcpy(&s->x, &twelve, sizeof(int)); |
| 123 | + assert(s->x == 12); |
| 124 | + memset(&s->in.b, 0x33, sizeof(int)); |
| 125 | + memset(&s->x, 0x13, sizeof(int)); |
| 126 | + check_final(s); |
| 127 | +} |
| 128 | + |
| 129 | +int main(void) { |
| 130 | + // A struct on the stack. |
| 131 | + struct S local; |
| 132 | + memset(&local, 0, sizeof(local)); |
| 133 | + check_struct(&local); |
| 134 | + |
| 135 | + // A struct in an array. |
| 136 | + struct S arr[2]; |
| 137 | + memset(arr, 0, sizeof(arr)); |
| 138 | + check_struct(&arr[1]); |
| 139 | + assert(all_bytes((unsigned char *)&arr[0], sizeof(struct S), 0)); |
| 140 | + |
| 141 | + // A struct in malloc'ed memory, i.e., a view of a byte buffer. |
| 142 | + struct S *heap = calloc(1, sizeof(struct S)); |
| 143 | + assert(heap != NULL); |
| 144 | + check_struct(heap); |
| 145 | + free(heap); |
| 146 | + |
| 147 | + // A struct in a byte buffer that is written through the buffer. |
| 148 | + unsigned char *raw = malloc(sizeof(struct S)); |
| 149 | + assert(raw != NULL); |
| 150 | + memset(raw, 0, sizeof(struct S)); |
| 151 | + struct S *view = (struct S *)raw; |
| 152 | + check_struct(view); |
| 153 | + assert(raw[offsetof(struct S, tail)] == 0x1b); |
| 154 | + set_bytes(raw + offsetof(struct S, arr), sizeof(int), 0x77); |
| 155 | + assert(view->arr[0] == REP(int, 0x77)); |
| 156 | + view->bytes[3] = 0x11; |
| 157 | + assert(raw[offsetof(struct S, bytes) + 3] == 0x11); |
| 158 | + free(raw); |
| 159 | + return 0; |
| 160 | +} |
0 commit comments