protoverse

A metaverse protocol
git clone git://jb55.com/protoverse
Log | Files | Refs | README | LICENSE

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 }