Simplify catalog API
This commit is contained in:
		
							parent
							
								
									2eff450a72
								
							
						
					
					
						commit
						9b1223d5de
					
				| @ -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) | ||||
|  | ||||
							
								
								
									
										174
									
								
								dqn.h
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								dqn.h
									
									
									
									
									
								
							| @ -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) | ||||
|     { | ||||
|         T newData        = {}; | ||||
|     DqnFileInfo info = {}; | ||||
|         if (DqnFile_GetInfo(file.str, &info) && LoadAsset(file, &newData)) | ||||
|     if (!DqnFile_GetInfo(file.str, &info)) | ||||
|     { | ||||
|             result->lastWriteTimeInS = info.lastWriteTimeInS; | ||||
|             result->data             = newData; | ||||
|             result->updated          = true; | ||||
|         DQN_LOGE("Catalog could not get file info for: %s\n", file.str); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (entry->lastWriteTimeInS == info.lastWriteTimeInS) | ||||
|         return true; | ||||
| 
 | ||||
|     T newData = {}; | ||||
|     if (LoadAsset(file, &newData)) | ||||
|     { | ||||
|         entry->lastWriteTimeInS = info.lastWriteTimeInS; | ||||
|         entry->data             = newData; | ||||
|         entry->updated          = true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|             result = nullptr; | ||||
|         DQN_LOGE("Catalog could not load file: %s\n", file.str); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     return (entry) ? &entry->data : nullptr; | ||||
| } | ||||
| 
 | ||||
| DQN_CATALOG_TEMPLATE void DQN_CATALOG_DECL::PollAssets() | ||||
| { | ||||
|     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); | ||||
|         entry = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     return &entry->data; | ||||
| } | ||||
| 
 | ||||
| DQN_CATALOG_TEMPLATE bool DQN_CATALOG_DECL::PollAssets() | ||||
| { | ||||
|     bool result = false; | ||||
|     for (auto it = this->assetTable.Begin(); it != this->assetTable.End(); ++it) | ||||
|     { | ||||
|         DqnCatalogPath const *file = &it.entry->key; | ||||
|         Entry *entry               = &it.entry->item; | ||||
|         result |= QueryAndUpdateAsset(*file, entry); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user