/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Registry.cpp Date: 2021-6-13 Author: Reece ***/ #include #include "Registry.hpp" #include using json = nlohmann::json; namespace Aurora::Registry { class FSRegistry : public IRegistry { public: bool Init(); void OpenReadWriteback(const AuString &path) override; void OpenRead(const AuString &path) override; bool Read(const AuString &path, EReadType read) override; void SetWriteModeNone() override; void SetWriteModeCache() override; void SetWriteModeStream(const AuOptional &path) override; void SetWriteModeBuffered(const AuOptional &path) override; void WriteAllCacheBack() override; void SaveBuffered() override; void SaveAll(const AuOptional &path) override; void OpenSave(const AuString &path, bool buffered = false) override; void CloseSave() override; bool KeyExists(const AuString &key, RegistryType &type) override; bool GetOrCreateKey(const AuString &key, const RegistryValue &def, RegistryValue &value) override; bool SetKey(const AuString &key, const RegistryValue &value) override; bool GetKey(const AuString &key, RegistryValue &value) override; void LockKey(const AuString &str) override; void UnlockKey(const AuString &str) override; bool SetKeyLocked(const AuString &key, const RegistryValue &value); bool GetKeyLocked(const AuString &key, RegistryValue &value); bool Save(); private: AuThreadPrimitives::RWLockUnique_t lock_; EWriteMode writeMode_; json currentStreamDocument_; json cacheDocument_; AuOptional lastPath_; AuList restrictedKeys_; AuList changes_; }; bool FSRegistry::Init() { lock_ = AuThreadPrimitives::RWLockUnique(); return lock_ ? true : false; } void FSRegistry::SetWriteModeNone() { AU_LOCK_GUARD(lock_->AsWritable()); writeMode_ = EWriteMode::eWriteModeLocked; changes_.clear(); } void FSRegistry::SetWriteModeCache() { AU_LOCK_GUARD(lock_->AsWritable()); writeMode_ = EWriteMode::eWriteModeFileBuffer; //lastPath_.reset(); changes_.clear(); } void FSRegistry::SetWriteModeStream(const AuOptional &path) { AU_LOCK_GUARD(lock_->AsWritable()); writeMode_ = EWriteMode::eWriteModeFileStream; if (path) lastPath_ = path; changes_.clear(); } void FSRegistry::SetWriteModeBuffered(const AuOptional &path) { AU_LOCK_GUARD(lock_->AsWritable()); writeMode_ = EWriteMode::eWriteModeFileBuffer; if (path) lastPath_ = path; changes_.clear(); } bool FSRegistry::Read(const AuString &path, EReadType read) { AU_LOCK_GUARD(lock_->AsWritable()); this->lastPath_ = path; try { AuString file; currentStreamDocument_ = {}; if (read == EReadType::eClearCacheAndStore) { cacheDocument_ = {}; } if (!IO::FS::ReadString(path, file)) { return IO::FS::WriteString(path, "{}"); } currentStreamDocument_ = json::parse(file); for (const auto &[key, value] : currentStreamDocument_.items()) { if (read == EReadType::eStoreNew) { if (cacheDocument_.find(key) != cacheDocument_.end()) { continue; } } cacheDocument_[key] = value; } } catch (...) { return false; } return true; } bool FSRegistry::KeyExists(const AuString &key, RegistryType &type) { AU_LOCK_GUARD(lock_->AsReadable()); auto itrObject = cacheDocument_.find(key); if (itrObject == cacheDocument_.end()) { return false; } auto itrType = itrObject->find("type"); if (itrType == itrObject->end()) { return false; } auto itrValue = itrObject->find("value"); if (itrValue == itrObject->end()) { return false; } return true; } bool FSRegistry::GetOrCreateKey(const AuString &key, const RegistryValue &ref, RegistryValue &value) { AU_LOCK_GUARD(lock_->AsWritable()); if (GetKeyLocked(key, value)) { return true; } value = ref; return SetKeyLocked(key, ref); } bool FSRegistry::GetKey(const AuString &key, RegistryValue &value) { AU_LOCK_GUARD(lock_->AsReadable()); return SetKeyLocked(key, value); } bool FSRegistry::GetKeyLocked(const AuString &key, RegistryValue &value) { auto itrObject = cacheDocument_.find(key); if (itrObject == cacheDocument_.end()) { return false; } auto itrType = itrObject->find("type"); auto itrValue = itrObject->find("value"); if ((itrType == itrObject->end()) || (itrValue == itrObject->end())) { return false; } if (!itrValue->is_string()) { return false; } if (!itrType->is_number_integer()) { return false; } Parse::ParsableTag type = *itrType; AuString strValue = *itrValue; Parse::ParseValueEx parsedValue; if (!Parse::ConsumeToken(type, strValue , parsedValue)) { return false; } value.type = static_cast(type); value.value = parsedValue; return true; } bool FSRegistry::SetKey(const AuString &key, const RegistryValue &value) { AU_LOCK_GUARD(lock_->AsWritable()); return SetKeyLocked(key, value); } bool FSRegistry::SetKeyLocked(const AuString &key, const RegistryValue &value) { json object {}; AuString stringified; if (std::find(restrictedKeys_.begin(), restrictedKeys_.end(), key) != restrictedKeys_.end()) { return false; } if (writeMode_ == EWriteMode::eWriteModeLocked) { return false; } auto oldValueItr = cacheDocument_.find(key); bool hasOldValue = oldValueItr == cacheDocument_.end(); json oldValue = hasOldValue ? json{} : *oldValueItr; object["type"] = value.type; Parse::SerializeToken(static_cast(value.type), value.value, stringified); object["value"] = stringified; AuTryInsert(changes_, key); switch (writeMode_) { case EWriteMode::eWriteModeFileBuffer: cacheDocument_[key] = object; break; case EWriteMode::eWriteModeFileStream: cacheDocument_[key] = object; currentStreamDocument_[key] = object; Save(); break; } return true; } void FSRegistry::SaveBuffered() { AU_LOCK_GUARD(lock_->AsWritable()); { AU_LOCK_GUARD(lock_->AsWritable()); for (const auto &change : changes_) { currentStreamDocument_[change] = cacheDocument_[change]; } } Save(); } void FSRegistry::WriteAllCacheBack() { AU_LOCK_GUARD(lock_->AsWritable()); for (const auto &[key, val] : cacheDocument_.items()) { AuTryInsert(changes_, key); } } void FSRegistry::SaveAll(const AuOptional& path) { OpenSave(path.value_or(lastPath_.value_or(AuString())), true); WriteAllCacheBack(); SaveBuffered(); CloseSave(); } void FSRegistry::OpenRead(const AuString &path) { Read(path, EReadType::eClearCacheAndStore); } void FSRegistry::OpenReadWriteback(const AuString &path) { Read(path, EReadType::eClearCacheAndStore); OpenSave(path, false); } void FSRegistry::OpenSave(const AuString &path, bool buffered) { if (buffered) { SetWriteModeBuffered(path); } else { SetWriteModeStream(path); } } void FSRegistry::CloseSave() { SetWriteModeNone(); } void FSRegistry::LockKey(const AuString &str) { AU_LOCK_GUARD(lock_->AsWritable()); AuTryInsert(restrictedKeys_, str); } void FSRegistry::UnlockKey(const AuString &str) { AU_LOCK_GUARD(lock_->AsWritable()); AuTryDeleteList(restrictedKeys_, str); } bool FSRegistry::Save() { bool ret {}; if (!lastPath_.has_value()) { return false; } if (!IO::FS::WriteString(lastPath_.value(), currentStreamDocument_.dump(4))) { SysPushErrorIO("Couldn't write registry to {}", lastPath_.value()); return false; } return true; } AUKN_SYM IRegistry *RegistryNew(ERegistrySource source) { SysAssert(source == ERegistrySource::eFS, "Unknown registry type"); auto registry = _new FSRegistry(); if (!registry) { return nullptr; } if (!registry->Init()) { delete registry; return nullptr; } return registry; } AUKN_SYM void RegistryRelease(IRegistry *registry) { SafeDelete(registry); } }