summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kon/konassets.c86
-rw-r--r--kon/konassets.h9
2 files changed, 95 insertions, 0 deletions
diff --git a/kon/konassets.c b/kon/konassets.c
index 75b2fae..b55a25a 100644
--- a/kon/konassets.c
+++ b/kon/konassets.c
@@ -58,6 +58,8 @@ struct kon_assets {
int nextCustom;
kon_assetEntry_t *entries;
int count, capacity;
+ int *table; /* hash of name to entry number plus one, 0 is empty. the size is a power of two */
+ int tableSize;
const char *error;
};
@@ -237,7 +239,58 @@ static int kon_typeForName_(const kon_assets_t *assets, const char *name) {
return KON_ASSET_BLOB;
}
+static uint32_t kon_hashName_(const char *name) {
+ uint32_t hash = 2166136261u;
+
+ while (*name) {
+ hash ^= (uint8_t)*name++;
+ hash *= 16777619u;
+ }
+ return hash;
+}
+
+static void kon_tableInsert_(kon_assets_t *assets, int index) {
+ uint32_t mask = (uint32_t)assets->tableSize - 1;
+ uint32_t slot = kon_hashName_(assets->entries[index].name) & mask;
+
+ while (assets->table[slot]) slot = (slot + 1) & mask;
+ assets->table[slot] = index + 1;
+}
+
+/* the table is built again whenever entries move or it gets too full. if it can't be built, lookups just search the list */
+static void kon_tableRebuild_(kon_assets_t *assets) {
+ int size = 64;
+ int *table;
+
+ while (size < assets->count * 2 + 2) size *= 2;
+ if (size != assets->tableSize) {
+ table = realloc(assets->table, (size_t)size * sizeof *table);
+ if (!table) {
+ free(assets->table);
+ assets->table = NULL;
+ assets->tableSize = 0;
+ return;
+ }
+ assets->table = table;
+ assets->tableSize = size;
+ }
+ memset(assets->table, 0, (size_t)assets->tableSize * sizeof *assets->table);
+ for (int i = 0; i < assets->count; i++) kon_tableInsert_(assets, i);
+}
+
static kon_assetEntry_t *kon_findEntry_(const kon_assets_t *assets, const char *name) {
+ if (assets->tableSize) {
+ uint32_t mask = (uint32_t)assets->tableSize - 1;
+ uint32_t slot = kon_hashName_(name) & mask;
+
+ while (assets->table[slot]) {
+ kon_assetEntry_t *entry = &assets->entries[assets->table[slot] - 1];
+ if (strcmp(entry->name, name) == 0) return entry;
+ slot = (slot + 1) & mask;
+ }
+ return NULL;
+ }
+
for (int i = 0; i < assets->count; i++) {
if (strcmp(assets->entries[i].name, name) == 0) return &assets->entries[i];
}
@@ -353,6 +406,7 @@ void kon_assetsDestroy(kon_assets_t *assets) {
for (int i = 0; i < assets->count; i++) kon_unloadEntry_(assets, &assets->entries[i]);
free(assets->entries);
+ free(assets->table);
free(assets->looseDir);
free(assets);
}
@@ -440,6 +494,8 @@ void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type) {
entry->loose = loose;
entry->unbound = 0;
assets->count++;
+ if (assets->tableSize && assets->count * 2 <= assets->tableSize) kon_tableInsert_(assets, assets->count - 1);
+ else kon_tableRebuild_(assets);
/* this can load more assets and move the entries, so entry is not used after it */
if (assets->loaders[found].bind) assets->loaders[found].bind(object, assets);
@@ -447,6 +503,14 @@ void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type) {
return object;
}
+void *kon_assetFind(const kon_assets_t *assets, const char *name) {
+ kon_assetEntry_t *entry;
+
+ if (!assets || !name) return NULL;
+ entry = kon_findEntry_(assets, name);
+ return entry ? entry->object : NULL;
+}
+
void kon_assetRelease(kon_assets_t *assets, const char *name) {
kon_assetEntry_t *entry;
@@ -455,6 +519,27 @@ void kon_assetRelease(kon_assets_t *assets, const char *name) {
if (entry && entry->refs > 0) entry->refs--;
}
+int kon_assetUnload(kon_assets_t *assets, const char *name) {
+ kon_assetEntry_t *entry;
+ int index;
+
+ if (!assets || !name) return 0;
+ entry = kon_findEntry_(assets, name);
+ if (!entry) return 0;
+ if (entry->refs > 0) entry->refs--;
+ if (entry->refs > 0) return 0;
+
+ /* unbind can give back uses of other assets, which never moves the entries, so this one is still where it was */
+ index = (int)(entry - assets->entries);
+ if (assets->loaders[entry->type].unbind) assets->loaders[entry->type].unbind(entry->object, assets);
+ entry = &assets->entries[index];
+ kon_unloadEntry_(assets, entry);
+ memmove(entry, entry + 1, (size_t)(assets->count - index - 1) * sizeof *entry);
+ assets->count--;
+ kon_tableRebuild_(assets);
+ return 1;
+}
+
int kon_assetsCollect(kon_assets_t *assets) {
int freed = 0, again;
@@ -481,6 +566,7 @@ int kon_assetsCollect(kon_assets_t *assets) {
}
}
assets->count = kept;
+ kon_tableRebuild_(assets);
/* something that just became unused has to be looked at again */
again = freed != before;
diff --git a/kon/konassets.h b/kon/konassets.h
index 3b35629..4a25f15 100644
--- a/kon/konassets.h
+++ b/kon/konassets.h
@@ -100,9 +100,18 @@ void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type);
#define kon_assetImage(assets, name) ((kon_image *)kon_assetGetAs((assets), (name), KON_ASSET_IMAGE))
#define kon_assetSound(assets, name) ((kon_sound_t *)kon_assetGetAs((assets), (name), KON_ASSET_SOUND))
+/* the loaded object, or NULL if it isn't loaded. does not load anything and does not count a use, so the pointer is only good
+ until the asset is collected. meant for getting at something another part of the program loaded */
+void *kon_assetFind(const kon_assets_t *assets, const char *name);
+
/* gives back one use. nothing is freed until kon_assetsCollect */
void kon_assetRelease(kon_assets_t *assets, const char *name);
+/* gives back one use and frees the asset right away if nothing uses it any more, other unused assets stay.
+ what it was using, like the textures of a model, is only given back, they are freed by the next kon_assetsCollect.
+ returns 1 if it was freed */
+int kon_assetUnload(kon_assets_t *assets, const char *name);
+
/* frees everything nobody is using, and then what only that was using, returns how many it freed */
int kon_assetsCollect(kon_assets_t *assets);