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() void DqnTimer_Test()
{ {
LOG_HEADER(); LOG_HEADER();
@ -1851,18 +1860,13 @@ void DqnTimer_Test()
if (1) if (1)
{ {
int sleepTimeInMs = 250;
f64 startInMs = DqnTimer_NowInMs(); f64 startInMs = DqnTimer_NowInMs();
#if defined(DQN__IS_UNIX) PlatformSleep(sleepTimeInMs);
u32 sleepTimeInMs = 1;
sleep(sleepTimeInMs);
Log("start: %f, end: %f", startInMs, endInMs);
#else
u32 sleepTimeInMs = 1000;
Sleep(sleepTimeInMs);
#endif
f64 endInMs = DqnTimer_NowInMs(); f64 endInMs = DqnTimer_NowInMs();
DQN_ASSERT((startInMs + sleepTimeInMs) <= endInMs); DQN_ASSERT((startInMs + sleepTimeInMs) <= endInMs);
Log("start: %f, end: %f", startInMs, endInMs);
Log(Status::Ok, "Timer advanced in time over 1 second"); Log(Status::Ok, "Timer advanced in time over 1 second");
globalIndent++; globalIndent++;
@ -2342,44 +2346,52 @@ void DqnCatalog_Test()
{ {
LOG_HEADER(); LOG_HEADER();
DqnCatalog<RawBuf, CatalogRawLoad> textCatalog = {}; DqnCatalog<RawBuf, CatalogRawLoad> catalog = {};
textCatalog.PollAssets(); catalog.PollAssets();
char const bufA[] = "aaaa"; DqnCatalogPath path = "DqnCatalog_TrackFile";
char const bufX[] = "xxxx"; DqnFile_Delete(path.str);
DqnFixedString128 testFile = "DqnCatalog_TrackFile"; // Initially write the file and check the catalog is able to open it up
DqnFile file = {};
// Write file A and check we are able to open it up in the catalog
{ {
DQN_ASSERTM( char const writeBuf[] = "aaaa";
file.Open(testFile.str, DqnFile::Flag::FileReadWrite, DqnFile::Action::ForceCreate), DqnFile_WriteAll(path.str, reinterpret_cast<u8 const *>(writeBuf), DQN_CHAR_COUNT(writeBuf));
"Could not create testing file for DqnCatalog"); RawBuf *buf = catalog.GetIfUpdated(path);
file.Write(reinterpret_cast<u8 const *>(bufA), DQN_CHAR_COUNT(bufA), 0); DQN_ASSERT(DqnMem_Cmp(buf->buffer, writeBuf, DQN_CHAR_COUNT(writeBuf)) == 0);
file.Close();
RawBuf *buf = textCatalog.GetIfUpdated(testFile);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, bufA, DQN_CHAR_COUNT(bufA)) == 0);
Log(Status::Ok, "Catalog finds and loads on demand new file"); 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 = {}; PlatformSleep(1000);
DQN_ASSERTM( char const writeBuf[] = "xxxx";
file.Open(testFile.str, DqnFile::Flag::FileReadWrite, DqnFile::Action::ForceCreate), DqnFile_WriteAll(path.str, reinterpret_cast<u8 const *>(writeBuf), DQN_CHAR_COUNT(writeBuf));
"Could not create testing file for DqnCatalog"); RawBuf *buf = catalog.GetIfUpdated(path);
file.Write(reinterpret_cast<u8 const *>(bufX), DQN_CHAR_COUNT(bufX), 0); DQN_ASSERT(DqnMem_Cmp(buf->buffer, writeBuf, DQN_CHAR_COUNT(writeBuf)) == 0);
file.Close();
RawBuf *buf = textCatalog.GetIfUpdated(testFile);
DQN_ASSERT(DqnMem_Cmp(buf->buffer, bufX, DQN_CHAR_COUNT(bufX)) == 0);
Log(Status::Ok, "Catalog finds updated file after subsequent write"); 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) 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 DqnVHashTableHashingProc = isize(*)(isize count, Key const &data);
template <typename Key> using DqnVHashTableEqualsProc = bool (*)(Key const &a, Key const &b); 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) const u64 DQN_VHASH_TABLE_DEFAULT_SEED = 0x9747B28CAB3F8A7B;
DQN_VHASH_TABLE_HASHING_PROC(DqnVHashTableDefaultHash) #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)
const u64 SEED = 0x9747B28CAB3F8A7B;
u64 index64 = DqnHash_Murmur64Seed(&key, sizeof(key), SEED); template <typename T> DQN_VHASH_TABLE_HASHING_PROC(DqnVHashTableDefaultHash, T) { return DqnHash_Murmur64Seed(&key, sizeof(key), DQN_VHASH_TABLE_DEFAULT_SEED) % count; }
isize result = index64 % count; template <typename T> DQN_VHASH_TABLE_EQUALS_PROC (DqnVHashTableDefaultEquals, T) { return (DqnMem_Cmp(&a, &b, sizeof(a)) == 0); }
return result;
} // 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 \ #define DQN_VHASH_TABLE_TEMPLATE \
template <typename Key, \ template <typename Key, \
@ -2934,12 +2950,40 @@ struct DqnSmartFile : public DqnFile
// XPlatform > #DqnCatalog API // XPlatform > #DqnCatalog API
// ================================================================================================= // =================================================================================================
template <typename T> using DqnCatalogLoadProc = bool (*)(DqnFixedString128 const &file, T *data); using DqnCatalogPath = DqnFixedString1024;
#define DQN_CATALOG_LOAD_PROC(name, type) bool name(DqnFixedString128 const &file, type *data) 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_TEMPLATE template <typename T, DqnCatalogLoadProc<T> LoadAsset>
#define DQN_CATALOG_DECL DqnCatalog<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 DQN_CATALOG_TEMPLATE struct DqnCatalog
{ {
struct Entry struct Entry
@ -2949,81 +2993,79 @@ DQN_CATALOG_TEMPLATE struct DqnCatalog
bool updated; bool updated;
}; };
DqnVHashTable<DqnFixedString128, Entry> assetTable; DqnVHashTable<DqnCatalogPath, Entry> assetTable;
// Register the asset and load it if it hasn't yet. // Adds the file to the catalog if it has not been added yet.
Entry *GetEntry (DqnFixedString128 const &file); // return: Entry, or nullptr if the asset could not be loaded // return: Asset if an update has been detected and not consumed yet otherwise nullptr. Update is consumed after called.
T *Get (DqnFixedString128 const &file) { Entry *entry = GetEntry(file.str); return (entry) ? &entry->data : nullptr; } T *GetIfUpdated(DqnCatalogPath const &file);
T *GetIfUpdated(DqnFixedString128 const &file); // return: Asset if an update has been detected and not consumed yet. Update is consumed after called. 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. // return: Iterate all loaded assets for updates, true if atleast 1 asset was updated.
void PollAssets (); bool PollAssets ();
void Free () { assetTable.Free(); } 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_TEMPLATE bool DQN_CATALOG_DECL::QueryAndUpdateAsset(DqnCatalogPath const &file, Entry *entry)
DQN_CATALOG_DECL::GetEntry(DqnFixedString128 const &file)
{ {
bool existed = false; DqnFileInfo info = {};
Entry *result = this->assetTable.GetOrMake(file, &existed); if (!DqnFile_GetInfo(file.str, &info))
if (!existed)
{ {
T newData = {}; DQN_LOGE("Catalog could not get file info for: %s\n", file.str);
DqnFileInfo info = {}; return false;
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);
}
} }
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); Entry *entry = this->assetTable.GetOrMake(file);
if (entry) if (QueryAndUpdateAsset(file, entry))
{ {
if (entry->updated) entry->updated = false; if (entry->updated) entry->updated = false;
else entry = nullptr; 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) for (auto it = this->assetTable.Begin(); it != this->assetTable.End(); ++it)
{ {
DqnFixedString128 const *file = &it.entry->key; DqnCatalogPath const *file = &it.entry->key;
Entry *entry = &it.entry->item; Entry *entry = &it.entry->item;
result |= QueryAndUpdateAsset(*file, entry);
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);
}
} }
return result;
} }