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