AuroraRuntime/Source/Registry/Registry.cpp

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);
}
}