diff options
| -rw-r--r-- | kon/konassets.c | 86 | ||||
| -rw-r--r-- | kon/konassets.h | 9 |
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); |
