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() | 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) | ||||||
|  | |||||||
							
								
								
									
										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 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; |  | ||||||
|     Entry *result = this->assetTable.GetOrMake(file, &existed); |  | ||||||
| 
 |  | ||||||
|     if (!existed) |  | ||||||
|     { |  | ||||||
|         T newData        = {}; |  | ||||||
|     DqnFileInfo info = {}; |     DqnFileInfo info = {}; | ||||||
|         if (DqnFile_GetInfo(file.str, &info) && LoadAsset(file, &newData)) |     if (!DqnFile_GetInfo(file.str, &info)) | ||||||
|     { |     { | ||||||
|             result->lastWriteTimeInS = info.lastWriteTimeInS; |         DQN_LOGE("Catalog could not get file info for: %s\n", file.str); | ||||||
|             result->data             = newData; |         return false; | ||||||
|             result->updated          = true; |     } | ||||||
|  | 
 | ||||||
|  |     if (entry->lastWriteTimeInS == info.lastWriteTimeInS) | ||||||
|  |         return true; | ||||||
|  | 
 | ||||||
|  |     T newData = {}; | ||||||
|  |     if (LoadAsset(file, &newData)) | ||||||
|  |     { | ||||||
|  |         entry->lastWriteTimeInS = info.lastWriteTimeInS; | ||||||
|  |         entry->data             = newData; | ||||||
|  |         entry->updated          = true; | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|             result = nullptr; |  | ||||||
|         DQN_LOGE("Catalog could not load file: %s\n", file.str); |         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); |     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; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     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 |     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