resource.c (5561B)
1 2 #include <stdlib.h> 3 #include <string.h> 4 #include <assert.h> 5 6 #include "resource.h" 7 #include "debug.h" 8 #include "util.h" 9 10 static u64 resource_uuids = 0; 11 12 static inline void *index_resource_(u8 *res, u32 elem_size, int i) 13 { 14 assert(res); 15 return res + (i * elem_size); 16 } 17 18 static inline void *index_resource(struct resource_manager *r, int i) 19 { 20 return index_resource_(r->resources, r->elem_size, i); 21 } 22 23 24 void *get_all_resources(struct resource_manager *r, u32 *count, struct resource_id **ids) { 25 if (count != 0) 26 *count = r->resource_count; 27 28 if (ids != 0) 29 *ids = r->ids; 30 31 return r->resources; 32 } 33 34 void init_id(struct resource_id *id) { 35 id->index = -1; 36 id->generation = -1; 37 id->uuid = -1; 38 } 39 40 void null_id(struct resource_id *id) 41 { 42 id->generation = 0; 43 id->uuid = -1; 44 id->index = -1; 45 } 46 47 void init_resource_manager(struct resource_manager *r, u32 elem_size, 48 u32 initial_elements, u32 max_elements, 49 const char *name) 50 { 51 r->generation = 1; 52 r->resource_count = 0; 53 r->elem_size = elem_size; 54 r->max_capacity = max_elements; 55 r->current_capacity = initial_elements; 56 r->name = name; 57 58 assert(initial_elements != 0); 59 60 r->resources = calloc(r->current_capacity, elem_size); 61 r->ids = calloc(r->current_capacity, sizeof(struct resource_id)); 62 } 63 64 void destroy_resource_manager(struct resource_manager *r) { 65 free(r->ids); 66 free(r->resources); 67 } 68 69 static int refresh_id(struct resource_manager *r, struct resource_id *id, 70 struct resource_id *new) 71 { 72 u32 i; 73 // rollover is ok 74 /* assert(->generation <= esys.generation); */ 75 if (id->generation != r->generation) { 76 /* debug("id %llu gen %d != res gen %d, refreshing\n", */ 77 /* id->uuid, id->generation, r->generation); */ 78 /* try to find uuid in new memory layout */ 79 for (i = 0; i < r->resource_count; i++) { 80 struct resource_id *newer_id = &r->ids[i]; 81 if (newer_id->uuid == id->uuid) { 82 /* debug("found %llu, ind %d -> %d\n", new_id->uuid, new_id->index, new->index); */ 83 new->index = newer_id->index; 84 new->generation = r->generation; 85 return REFRESHED_ID; 86 } 87 } 88 89 // entity was deleted 90 return RESOURCE_DELETED; 91 } 92 93 // doesn't need refreshed 94 return REFRESH_NOT_NEEDED; 95 } 96 97 int is_resource_destroyed(struct resource_id *id) { 98 return id->generation == 0; 99 } 100 101 static void new_id(struct resource_manager *r, struct resource_id *id) 102 { 103 id->index = r->resource_count; 104 id->uuid = ++resource_uuids; 105 id->generation = r->generation; 106 assert(id->generation); 107 } 108 109 static void resize(struct resource_manager *r) 110 { 111 void *new_mem; 112 u32 new_size; 113 114 debug("resizing %s resources, count %d+1 > current capacity %d\n", 115 r->name, r->resource_count, r->current_capacity); 116 117 new_size = r->resource_count * 1.5; 118 if (new_size >= r->max_capacity) { 119 new_size = r->max_capacity; 120 } 121 122 /* debug("resizing new_size %d\n", new_size); */ 123 124 new_mem = realloc(r->resources, (new_size+1) * r->elem_size); 125 if (!new_mem) { 126 // yikes, out of memory, bail 127 assert(new_mem); 128 return; 129 } 130 131 r->resources = new_mem; 132 new_mem = realloc(r->ids, sizeof(struct resource_id) * (new_size+1)); 133 134 if (!new_mem) { 135 // yikes, out of memory, bail 136 assert(new_mem); 137 return; 138 } 139 r->current_capacity = new_size; 140 r->ids = new_mem; 141 } 142 143 void print_id(struct resource_id *id, int nl) 144 { 145 printf("id(u:%llu i:%d g:%d)%s", 146 id->uuid, id->index, id->generation, nl?"\n":""); 147 } 148 149 150 void *new_resource(struct resource_manager *r, struct resource_id *id) 151 { 152 struct resource_id *fresh_id; 153 154 assert(id); 155 assert(id->index == 0xFFFFFFFF && "res_id is uninitialized"); 156 157 if (r->resource_count + 1 > r->max_capacity) { 158 printf("new_resource: count %d > max cap %d\n", r->resource_count, r->max_capacity); 159 return NULL; 160 } 161 162 if (r->resource_count + 1 > r->current_capacity) 163 resize(r); 164 165 fresh_id = &r->ids[r->resource_count]; 166 new_id(r, fresh_id); 167 *id = *fresh_id; 168 169 return index_resource(r, r->resource_count++); 170 } 171 172 173 void *get_resource(struct resource_manager *r, struct resource_id *id) { 174 enum refresh_status res; 175 176 assert((int64_t)id->generation != -1 && "id intialized but not allocated (needs new_ call)"); 177 178 if (id->generation == 0) { 179 /* unusual("getting already deleted resource %llu\n", id->uuid); */ 180 return NULL; 181 } 182 183 res = refresh_id(r, id, id); 184 185 if (res == RESOURCE_DELETED) { 186 /* unusual("getting deleted %s resource %llu\n", r->name, id->uuid); */ 187 return NULL; 188 } 189 190 return index_resource(r, id->index); 191 } 192 193 194 void destroy_resource(struct resource_manager *r, struct resource_id *id) { 195 enum refresh_status res; 196 u32 i; 197 198 if (is_resource_destroyed(id)) { 199 unusual("trying to destroy resource %llu which was already destroyed\n", id->uuid); 200 return; 201 } 202 203 res = refresh_id(r, id, id); 204 205 // entity already deleted 206 /* debug("refresh res %d uuid %llu gen %d index %d\n", res, */ 207 /* id->uuid, id->generation, id->index); */ 208 209 if (res == RESOURCE_DELETED) { 210 unusual("trying to destroy resource %llu which was already destroyed (2)\n", id->uuid); 211 id->generation = 0; 212 return; 213 } 214 215 /* debug("destroying %s resource %llu ind %d res_count %d\n", */ 216 /* r->name, id->uuid, id->index, r->resource_count); */ 217 218 r->resource_count--; 219 r->generation++; 220 221 assert((int)r->resource_count - (int)id->index >= 0); 222 223 // TODO: we're copying OOB here 224 memmove(index_resource(r, id->index), 225 index_resource(r, id->index+1), 226 r->elem_size * (r->resource_count - id->index)); 227 228 memmove(&r->ids[id->index], 229 &r->ids[id->index+1], 230 sizeof(struct resource_id) * (r->resource_count - id->index)); 231 232 233 for (i = id->index; i < r->resource_count; i++) { 234 r->ids[i].index--; 235 } 236 }