Simplify catalog API

This commit is contained in:
Doyle T 2018-07-28 17:40:57 +10:00
parent 2eff450a72
commit 9b1223d5de
2 changed files with 159 additions and 105 deletions

View File

@ -1844,6 +1844,15 @@ void DqnFile_Test()
}
void PlatformSleep(int milliseconds)
{
#if defined(DQN__IS_UNIX)
usleep(milliseconds * 1000);
#else
Sleep(milliseconds);
#endif
}
void DqnTimer_Test()
{
LOG_HEADER();
@ -1851,18 +1860,13 @@ void DqnTimer_Test()
if (1)
{
int sleepTimeInMs = 250;
f64 startInMs = DqnTimer_NowInMs();
#if defined(DQN__IS_UNIX)
u32 sleepTimeInMs = 1;
sleep(sleepTimeInMs);
Log("start: %f, end: %f", startInMs, endInMs);
#else
u32 sleepTimeInMs = 1000;
Sleep(sleepTimeInMs);
#endif
PlatformSleep(sleepTimeInMs);
f64 endInMs = DqnTimer_NowInMs();
DQN_ASSERT((startInMs + sleepTimeInMs) <= endInMs);
Log("start: %f, end: %f", startInMs, endInMs);
Log(Status::Ok, "Timer advanced in time over 1 second");
globalIndent++;
@ -2342,44 +2346,52 @@ void DqnCatalog_Test()
{
LOG_HEADER();
DqnCatalog<RawBuf, CatalogRawLoad> textCatalog = {};
textCatalog.PollAssets();
DqnCatalog<RawBuf, CatalogRawLoad> catalog = {};
catalog.PollAssets();
char const bufA[] = "aaaa";
char const bufX[] = "xxxx";
DqnCatalogPath path = "DqnCatalog_TrackFile";
DqnFile_Delete(path.str);
DqnFixedString128 testFile = "DqnCatalog_TrackFile";
DqnFile file = {};
// Write file A and check we are able to open it up in the catalog
// Initially write the file and check the catalog is able to open it up
{
DQN_ASSERTM(
file.Open(testFile.str, DqnFile::Flag::FileReadWrite, DqnFile::Action::ForceCreate),
"Could not create testing file for DqnCatalog");
file.Write(reinterpret_cast<u8 const *>(bufA), DQN_CHAR_COUNT(bufA), 0);
file.Close();
RawBuf *buf = textCatalog.GetIfUpdated(testFile);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, bufA, DQN_CHAR_COUNT(bufA)) == 0);
char const writeBuf[] = "aaaa";
DqnFile_WriteAll(path.str, reinterpret_cast<u8 const *>(writeBuf), DQN_CHAR_COUNT(writeBuf));
RawBuf *buf = catalog.GetIfUpdated(path);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, writeBuf, DQN_CHAR_COUNT(writeBuf)) == 0);
Log(Status::Ok, "Catalog finds and loads on demand new file");
}
// Write file B check that it has been updated
// Update the file and check that the GetIfUpdated returns a non-nullptr (because the entry is updated)
{
file = {};
DQN_ASSERTM(
file.Open(testFile.str, DqnFile::Flag::FileReadWrite, DqnFile::Action::ForceCreate),
"Could not create testing file for DqnCatalog");
file.Write(reinterpret_cast<u8 const *>(bufX), DQN_CHAR_COUNT(bufX), 0);
file.Close();
RawBuf *buf = textCatalog.GetIfUpdated(testFile);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, bufX, DQN_CHAR_COUNT(bufX)) == 0);
PlatformSleep(1000);
char const writeBuf[] = "xxxx";
DqnFile_WriteAll(path.str, reinterpret_cast<u8 const *>(writeBuf), DQN_CHAR_COUNT(writeBuf));
RawBuf *buf = catalog.GetIfUpdated(path);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, writeBuf, DQN_CHAR_COUNT(writeBuf)) == 0);
Log(Status::Ok, "Catalog finds updated file after subsequent write");
}
DqnFile_Delete(testFile.str);
// Update the file and get the catalog to poll the entries and check it has been updated
{
PlatformSleep(1000);
char const writeBuf[] = "abcd";
DqnFile_WriteAll(path.str, reinterpret_cast<u8 const *>(writeBuf), DQN_CHAR_COUNT(writeBuf));
catalog.PollAssets();
RawBuf *buf = catalog.GetIfUpdated(path);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, writeBuf, DQN_CHAR_COUNT(writeBuf)) == 0);
Log(Status::Ok, "Catalog finds updated file using the poll asset interface");
}
// Update the file and get the catalog to poll the entries and check it has been updated
{
catalog.Erase(path.str);
RawBuf *buf = catalog.Get(path);
DQN_ASSERT(buf == nullptr);
Log(Status::Ok, "Catalog erase removes file from catalog");
}
DqnFile_Delete(path.str);
}
int main(void)

178
dqn.h
View File

@ -2600,21 +2600,37 @@ template <typename T> void DqnVArray<T>::EraseStable(isize index)
template <typename Key> using DqnVHashTableHashingProc = isize(*)(isize count, Key const &data);
template <typename Key> using DqnVHashTableEqualsProc = bool (*)(Key const &a, Key const &b);
#define DQN_VHASH_TABLE_HASHING_PROC(name) template <typename Key> inline isize name(isize count, Key const &key)
DQN_VHASH_TABLE_HASHING_PROC(DqnVHashTableDefaultHash)
{
const u64 SEED = 0x9747B28CAB3F8A7B;
u64 index64 = DqnHash_Murmur64Seed(&key, sizeof(key), SEED);
isize result = index64 % count;
return result;
}
const u64 DQN_VHASH_TABLE_DEFAULT_SEED = 0x9747B28CAB3F8A7B;
#define DQN_VHASH_TABLE_HASHING_PROC(name, Type) inline isize name(isize count, Type const &key)
#define DQN_VHASH_TABLE_EQUALS_PROC(name, Type) inline bool name(Type const &a, Type const &b)
template <typename T> DQN_VHASH_TABLE_HASHING_PROC(DqnVHashTableDefaultHash, T) { return DqnHash_Murmur64Seed(&key, sizeof(key), DQN_VHASH_TABLE_DEFAULT_SEED) % count; }
template <typename T> DQN_VHASH_TABLE_EQUALS_PROC (DqnVHashTableDefaultEquals, T) { return (DqnMem_Cmp(&a, &b, sizeof(a)) == 0); }
// TODO(doyle): Fix this so we don't have to manually declare the fixed string sizes for hashing and equals
#define DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(StringCapacity) \
template <> \
DQN_VHASH_TABLE_HASHING_PROC(DqnVHashTableDefaultHash<DqnFixedString<StringCapacity>>, \
DqnFixedString<StringCapacity>) \
{ \
return DqnHash_Murmur64Seed(key.str, key.len, DQN_VHASH_TABLE_DEFAULT_SEED) % count; \
} \
template <> \
DQN_VHASH_TABLE_EQUALS_PROC(DqnVHashTableDefaultEquals<DqnFixedString<StringCapacity>>, \
DqnFixedString<StringCapacity>) \
{ \
return (a.len == b.len) && (DqnStr_Cmp(a.str, b.str, a.len, Dqn::IgnoreCase::No) == 0); \
}
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(1024)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(512)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(256)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(128)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(64)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(32)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(16)
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(8)
#define DQN_VHASH_TABLE_EQUALS_PROC(name) template <typename Key> inline bool name(Key const &a, Key const &b)
DQN_VHASH_TABLE_EQUALS_PROC(DqnVHashTableDefaultEquals)
{
bool result = (DqnMem_Cmp(&a, &b, sizeof(a)) == 0);
return result;
}
#define DQN_VHASH_TABLE_TEMPLATE \
template <typename Key, \
@ -2934,12 +2950,40 @@ struct DqnSmartFile : public DqnFile
// XPlatform > #DqnCatalog API
// =================================================================================================
template <typename T> using DqnCatalogLoadProc = bool (*)(DqnFixedString128 const &file, T *data);
#define DQN_CATALOG_LOAD_PROC(name, type) bool name(DqnFixedString128 const &file, type *data)
using DqnCatalogPath = DqnFixedString1024;
template <typename T> using DqnCatalogLoadProc = bool (*)(DqnCatalogPath const &file, T *data);
#define DQN_CATALOG_LOAD_PROC(name, type) bool name(DqnCatalogPath const &file, type *data)
#define DQN_CATALOG_TEMPLATE template <typename T, DqnCatalogLoadProc<T> LoadAsset>
#define DQN_CATALOG_DECL DqnCatalog<T, LoadAsset>
#if 0
struct RawBuf { char *buffer; int len; };
DQN_CATALOG_LOAD_PROC(CatalogRawLoad, RawBuf)
{
size_t bufSize;
uint8_t *buf = DqnFile_ReadAll(file.str, &bufSize);
if (!buf) return false;
data->buffer = reinterpret_cast<char *>(buf);
data->len = static_cast<int>(bufSize);
return true;
}
int main(int, char)
{
DqnCatalog<RawBuf, CatalogRawLoad> catalog = {};
RawBuf *file = catalog.GetIfUpdated("path/to/file/");
if (file) { (void)file; // do work on file }
else { // file not updated since last query }
while (true) // Or event loop, poll the assets in the catalog
{
catalog.PollAssets();
}
catalog.Free();
}
#endif
DQN_CATALOG_TEMPLATE struct DqnCatalog
{
struct Entry
@ -2949,81 +2993,79 @@ DQN_CATALOG_TEMPLATE struct DqnCatalog
bool updated;
};
DqnVHashTable<DqnFixedString128, Entry> assetTable;
DqnVHashTable<DqnCatalogPath, Entry> assetTable;
// Register the asset and load it if it hasn't yet.
Entry *GetEntry (DqnFixedString128 const &file); // return: Entry, or nullptr if the asset could not be loaded
T *Get (DqnFixedString128 const &file) { Entry *entry = GetEntry(file.str); return (entry) ? &entry->data : nullptr; }
T *GetIfUpdated(DqnFixedString128 const &file); // return: Asset if an update has been detected and not consumed yet. Update is consumed after called.
// Adds the file to the catalog if it has not been added yet.
// return: Asset if an update has been detected and not consumed yet otherwise nullptr. Update is consumed after called.
T *GetIfUpdated(DqnCatalogPath const &file);
Entry *GetEntry (DqnCatalogPath const &file) { Entry *entry = assetTable.Get(file); return entry; }
T *Get (DqnCatalogPath const &file) { Entry *entry = assetTable.Get(file); return (entry) ? &entry->data : nullptr; }
void Erase (DqnCatalogPath const &file) { assetTable.Erase(file); };
// Call to iterate all loaded assets for updates.
void PollAssets ();
// return: Iterate all loaded assets for updates, true if atleast 1 asset was updated.
bool PollAssets ();
void Free () { assetTable.Free(); }
// NOTE: Unlikely you will need to use. Prefer GetIfUpdated.
// Manually invoke an update on the entry by querying its last write time on disk and updating accordingly.
bool QueryAndUpdateAsset(DqnCatalogPath const &file, Entry *entry);
};
DQN_CATALOG_TEMPLATE typename DQN_CATALOG_DECL::Entry *
DQN_CATALOG_DECL::GetEntry(DqnFixedString128 const &file)
DQN_CATALOG_TEMPLATE bool DQN_CATALOG_DECL::QueryAndUpdateAsset(DqnCatalogPath const &file, Entry *entry)
{
bool existed = false;
Entry *result = this->assetTable.GetOrMake(file, &existed);
if (!existed)
DqnFileInfo info = {};
if (!DqnFile_GetInfo(file.str, &info))
{
T newData = {};
DqnFileInfo info = {};
if (DqnFile_GetInfo(file.str, &info) && LoadAsset(file, &newData))
{
result->lastWriteTimeInS = info.lastWriteTimeInS;
result->data = newData;
result->updated = true;
}
else
{
result = nullptr;
DQN_LOGE("Catalog could not load file: %s\n", file.str);
}
DQN_LOGE("Catalog could not get file info for: %s\n", file.str);
return false;
}
return result;
if (entry->lastWriteTimeInS == info.lastWriteTimeInS)
return true;
T newData = {};
if (LoadAsset(file, &newData))
{
entry->lastWriteTimeInS = info.lastWriteTimeInS;
entry->data = newData;
entry->updated = true;
}
else
{
DQN_LOGE("Catalog could not load file: %s\n", file.str);
return false;
}
return true;
}
DQN_CATALOG_TEMPLATE T *DQN_CATALOG_DECL::GetIfUpdated(DqnFixedString128 const &file)
DQN_CATALOG_TEMPLATE T *DQN_CATALOG_DECL::GetIfUpdated(DqnCatalogPath const &file)
{
Entry *entry = this->GetEntry(file.str);
if (entry)
Entry *entry = this->assetTable.GetOrMake(file);
if (QueryAndUpdateAsset(file, entry))
{
if (entry->updated) entry->updated = false;
else entry = nullptr;
}
else
{
entry = nullptr;
}
return (entry) ? &entry->data : nullptr;
return &entry->data;
}
DQN_CATALOG_TEMPLATE void DQN_CATALOG_DECL::PollAssets()
DQN_CATALOG_TEMPLATE bool DQN_CATALOG_DECL::PollAssets()
{
bool result = false;
for (auto it = this->assetTable.Begin(); it != this->assetTable.End(); ++it)
{
DqnFixedString128 const *file = &it.entry->key;
Entry *entry = &it.entry->item;
DqnFileInfo info = {};
if (!DqnFile_GetInfo(file->str, &info))
continue;
if (entry->lastWriteTimeInS == info.lastWriteTimeInS)
continue;
T newData = {};
if (LoadAsset(*file, &newData))
{
entry->lastWriteTimeInS = info.lastWriteTimeInS;
entry->data = newData;
}
else
{
DQN_LOGE("Catalog could not load file: %s\n", file->str);
}
DqnCatalogPath const *file = &it.entry->key;
Entry *entry = &it.entry->item;
result |= QueryAndUpdateAsset(*file, entry);
}
return result;
}