406 lines
10 KiB
C++
406 lines
10 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: Registry.cpp
|
|
Date: 2021-6-13
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "Registry.hpp"
|
|
|
|
#include <nlohmann/json.hpp>
|
|
using json = nlohmann::json;
|
|
|
|
namespace Aurora::Registry
|
|
{
|
|
IOPath::IOPath()
|
|
{
|
|
|
|
}
|
|
|
|
IOPath::IOPath(const AuString &path)
|
|
{
|
|
filePath = path;
|
|
isSpecified = true;
|
|
}
|
|
|
|
bool IOPath::operator ==(const IOPath &other) const
|
|
{
|
|
return isSpecified == other.isSpecified && (!other.isSpecified || (other.filePath == filePath/* && ..*/));
|
|
}
|
|
|
|
IOPath::operator bool() const
|
|
{
|
|
return isSpecified;
|
|
}
|
|
|
|
|
|
class FSRegistry : public IRegistry
|
|
{
|
|
public:
|
|
|
|
bool Init();
|
|
|
|
void OpenReadWriteback(const IOPath &path) override;
|
|
void OpenRead(const IOPath &path) override;
|
|
bool Read(const IOPath &path, EReadType read) override;
|
|
void SetWriteModeNone() override;
|
|
void SetWriteModeCache() override;
|
|
void SetWriteModeStream(const AuOptional<IOPath> &path) override;
|
|
void SetWriteModeBuffered(const AuOptional<IOPath> &path) override;
|
|
void WriteAllCacheBack() override;
|
|
void SaveBuffered() override;
|
|
void SaveAll(const AuOptional<IOPath> &path) override;
|
|
void OpenSave(const IOPath &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<IOPath> lastPath_;
|
|
AuList<AuString> restrictedKeys_;
|
|
AuList<AuString> 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<IOPath> &path)
|
|
{
|
|
AU_LOCK_GUARD(lock_->AsWritable());
|
|
writeMode_ = EWriteMode::eWriteModeFileStream;
|
|
if (path) lastPath_ = path;
|
|
changes_.clear();
|
|
}
|
|
|
|
void FSRegistry::SetWriteModeBuffered(const AuOptional<IOPath> &path)
|
|
{
|
|
AU_LOCK_GUARD(lock_->AsWritable());
|
|
writeMode_ = EWriteMode::eWriteModeFileBuffer;
|
|
if (path) lastPath_ = path;
|
|
changes_.clear();
|
|
}
|
|
|
|
bool FSRegistry::Read(const IOPath &path, EReadType read)
|
|
{
|
|
AU_LOCK_GUARD(lock_->AsWritable());
|
|
this->lastPath_ = path;
|
|
try
|
|
{
|
|
AuString file;
|
|
|
|
currentStreamDocument_ = {};
|
|
|
|
if (read == EReadType::eClearCacheAndStore)
|
|
{
|
|
cacheDocument_ = {};
|
|
}
|
|
|
|
if (!AuIOFS::ReadString(path.filePath, file))
|
|
{
|
|
return AuIOFS::WriteString(path.filePath, "{}");
|
|
}
|
|
|
|
currentStreamDocument_ = json::parse(file);
|
|
|
|
for (const auto &[key, value] : currentStreamDocument_.items())
|
|
{
|
|
if (read == EReadType::eStoreNew)
|
|
{
|
|
if (cacheDocument_.find(AuROString(key)) != cacheDocument_.end())
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
cacheDocument_[AuROString(key)] = value;
|
|
}
|
|
|
|
}
|
|
catch (...)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FSRegistry::KeyExists(const AuString &key, RegistryType &type)
|
|
{
|
|
AU_LOCK_GUARD(lock_->AsReadable());
|
|
|
|
auto itrObject = cacheDocument_.find(AuROString(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(AuROString(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<Data::EDataType>(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 (writeMode_ == EWriteMode::eWriteModeLocked)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (AuExists(restrictedKeys_, key))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto oldValueItr = cacheDocument_.find(AuROString(key));
|
|
bool hasOldValue = oldValueItr == cacheDocument_.end();
|
|
json oldValue = hasOldValue ? json{} : *oldValueItr;
|
|
|
|
object["type"] = value.type;
|
|
Parse::SerializeToken(static_cast<Parse::ParsableTag>(value.type), value.value, stringified);
|
|
object["value"] = stringified;
|
|
|
|
AuTryInsert(changes_, key);
|
|
|
|
switch (writeMode_)
|
|
{
|
|
case EWriteMode::eWriteModeFileBuffer:
|
|
cacheDocument_[AuROString(key)] = object;
|
|
break;
|
|
case EWriteMode::eWriteModeFileStream:
|
|
cacheDocument_[AuROString(key)] = object;
|
|
currentStreamDocument_[AuROString(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_[AuROString(change)] = cacheDocument_[AuROString(change)];
|
|
}
|
|
}
|
|
Save();
|
|
}
|
|
|
|
|
|
void FSRegistry::WriteAllCacheBack()
|
|
{
|
|
AU_LOCK_GUARD(lock_->AsWritable());
|
|
|
|
for (const auto &[key, val] : cacheDocument_.items())
|
|
{
|
|
AuTryInsert(changes_, AuString(key));
|
|
}
|
|
}
|
|
|
|
void FSRegistry::SaveAll(const AuOptional<IOPath>& path)
|
|
{
|
|
OpenSave(path.value_or(lastPath_.value_or(IOPath())), true);
|
|
WriteAllCacheBack();
|
|
SaveBuffered();
|
|
CloseSave();
|
|
}
|
|
|
|
void FSRegistry::OpenRead(const IOPath &path)
|
|
{
|
|
Read(path, EReadType::eClearCacheAndStore);
|
|
}
|
|
|
|
void FSRegistry::OpenReadWriteback(const IOPath &path)
|
|
{
|
|
Read(path, EReadType::eClearCacheAndStore);
|
|
OpenSave(path, false);
|
|
}
|
|
|
|
void FSRegistry::OpenSave(const IOPath &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());
|
|
AuTryRemove(restrictedKeys_, str);
|
|
}
|
|
|
|
bool FSRegistry::Save()
|
|
{
|
|
bool ret {};
|
|
|
|
if (!lastPath_.has_value())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!lastPath_.value())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!AuIOFS::WriteString(lastPath_.value().filePath, currentStreamDocument_.dump(4)))
|
|
{
|
|
SysPushErrorIO("Couldn't write registry to {}", lastPath_.value().filePath);
|
|
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)
|
|
{
|
|
AuSafeDelete<FSRegistry *>(registry);
|
|
}
|
|
} |