-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhandle_manager.c
146 lines (125 loc) · 4.44 KB
/
handle_manager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
struct manager {
void *items; // items[0] is a reserved sentinel.
struct metadata *metadata;
unsigned short freelist;
unsigned short num_items;
unsigned short item_size;
};
struct metadata {
unsigned short generation;
unsigned short prev;
unsigned short next;
};
union handle {
unsigned value;
struct { unsigned short index, generation; } fields;
};
struct manager create(void *items, struct metadata *metadata, unsigned num_items, unsigned item_size) {
metadata[0].prev = 0;
metadata[0].next = 0;
metadata[0].generation = 0;
for (unsigned i = 1; i < num_items - 1; ++i) {
metadata[i].prev = 0;
metadata[i].next = (unsigned short)(i + 1);
metadata[i].generation = 0;
}
metadata[num_items - 1].prev = 0;
metadata[num_items - 1].next = 0;
metadata[num_items - 1].generation = 0;
return (struct manager) {
.items = items,
.metadata = metadata,
.freelist = 1,
.num_items = (unsigned short)num_items,
.item_size = (unsigned short)item_size,
};
}
union handle allocate(struct manager *manager) {
unsigned short index = manager->freelist;
if (index)
manager->freelist = manager->metadata[index].next;
manager->metadata[index].prev = manager->metadata[0].prev;
manager->metadata[index].next = 0;
manager->metadata[manager->metadata[0].prev].next = index;
manager->metadata[0].prev = index;
return (union handle) { .fields = {
.index = (unsigned short)index,
.generation = manager->metadata[index].generation
}};
}
void deallocate(struct manager *manager, union handle handle) {
unsigned short index = handle.fields.index;
if (!handle.value)
return;
if (index >= manager->num_items || handle.fields.generation != manager->metadata[index].generation)
return; // Handle is invalid.
unsigned short next = manager->metadata[index].next;
unsigned short prev = manager->metadata[index].prev;
manager->metadata[prev].next = next;
manager->metadata[next].prev = prev;
++manager->metadata[index].generation;
manager->metadata[index].next = (unsigned short)manager->freelist;
manager->freelist = index;
}
int is_valid(struct manager manager, union handle handle) {
unsigned index = handle.fields.index;
return index < manager.num_items && handle.fields.generation == manager.metadata[index].generation;
}
void *get_item_from_handle(struct manager manager, union handle handle) {
unsigned index = handle.fields.index;
if (index >= manager.num_items || handle.fields.generation != manager.metadata[index].generation)
index = 0; // Handle is invalid.
return (char *)manager.items + index * manager.item_size;
}
#include <assert.h>
int main(void) {
int items[10] = { -999, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
struct metadata metadata[10];
struct manager manager = create(items, metadata, 10, sizeof items[0]);
union handle handles[10];
for (int i = 1; i < 10; ++i) {
handles[i] = allocate(&manager);
assert(is_valid(manager, handles[i]));
int *item = get_item_from_handle(manager, handles[i]);
assert(*item == items[i]);
}
for (int i = 1; i < 10; ++i) {
assert(is_valid(manager, handles[i]));
deallocate(&manager, handles[i]);
assert(!is_valid(manager, handles[i]));
}
for (int i = 0; i < 10; ++i) {
union handle handle = allocate(&manager);
assert(is_valid(manager, handle));
int item = *(int *)get_item_from_handle(manager, handle);
deallocate(&manager, handle);
assert(!is_valid(manager, handle));
union handle new_handle = allocate(&manager);
assert(!is_valid(manager, handle));
assert(item == *(int *)get_item_from_handle(manager, new_handle));
deallocate(&manager, new_handle);
}
for (int i = 1; i < 10; ++i)
handles[i] = allocate(&manager);
assert(*(int *)get_item_from_handle(manager, allocate(&manager)) == items[0]);
assert(*(int *)get_item_from_handle(manager, allocate(&manager)) == items[0]);
assert(*(int *)get_item_from_handle(manager, allocate(&manager)) == items[0]);
for (int i = 1; i < 10; ++i)
deallocate(&manager, handles[i]);
for (int i = 1; i < 5; ++i)
handles[i] = allocate(&manager);
for (unsigned index = metadata[0].next, i = 1; index; index = metadata[index].next, ++i) {
assert(i < 5);
assert(items[index] == (int)i);
}
for (int i = 5; i < 10; ++i)
handles[i] = allocate(&manager);
for (unsigned index = metadata[0].next, i = 1; index; index = metadata[index].next, ++i) {
assert(i < 10);
assert(items[index] == (int)i);
}
for (int i = 1; i < 10; ++i)
deallocate(&manager, handles[i]);
for (unsigned index = metadata[0].next; index; index = metadata[index].next)
assert(0);
}