diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f9b131da..c5dd15c03 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -101,6 +101,8 @@ "ranges": "cpp", "stop_token": "cpp", "version": "cpp", - "shared_mutex": "cpp" + "shared_mutex": "cpp", + "compare": "cpp", + "concepts": "cpp" } } diff --git a/src/common/module.mk b/src/common/module.mk index b7a425d98..7d9af0692 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -33,6 +33,8 @@ MODULE_OBJS := \ src/common/sdl_blitter/QisBlitter.o \ src/common/sdl_blitter/BlitterFactory.o \ src/common/repository/KeyValueRepositoryPropertyFile.o \ + src/common/repository/KeyValueRepositoryJsonFile.o \ + src/common/repository/CompositeKVRJsonAdapter.o MODULE_DIRS += \ src/common diff --git a/src/common/repository/CompositeKVRJsonAdapter.cxx b/src/common/repository/CompositeKVRJsonAdapter.cxx new file mode 100644 index 000000000..d6081b652 --- /dev/null +++ b/src/common/repository/CompositeKVRJsonAdapter.cxx @@ -0,0 +1,77 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include + +#include "CompositeKVRJsonAdapter.hxx" +#include "repository/KeyValueRepositoryJsonFile.hxx" + +namespace { + class ProxyRepository : public KeyValueRepository { + public: + ProxyRepository(KeyValueRepositoryAtomic& kvr, const string& key) + : myKvr(kvr), myKey(key) + {} + + std::map load() override { + if (!myKvr.has(myKey)) return std::map(); + + string serialized; + myKvr.get(myKey, serialized); + + stringstream in{serialized}; + + return KeyValueRepositoryJsonFile::load(in); + } + + bool save(const std::map& values) override { + stringstream out; + + if (!KeyValueRepositoryJsonFile::save(out, values)) return false; + + return myKvr.save(myKey, out.str()); + } + + private: + + KeyValueRepositoryAtomic& myKvr; + const string& myKey; + }; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CompositeKVRJsonAdapter::CompositeKVRJsonAdapter(KeyValueRepositoryAtomic& kvr) + : myKvr(kvr) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +shared_ptr CompositeKVRJsonAdapter::get(const string& key) +{ + return make_shared(myKvr, key); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CompositeKVRJsonAdapter::has(const string& key) +{ + return myKvr.has(key); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CompositeKVRJsonAdapter::remove(const string& key) +{ + return myKvr.remove(key); +} diff --git a/src/common/repository/CompositeKVRJsonAdapter.hxx b/src/common/repository/CompositeKVRJsonAdapter.hxx new file mode 100644 index 000000000..950782207 --- /dev/null +++ b/src/common/repository/CompositeKVRJsonAdapter.hxx @@ -0,0 +1,41 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef COMPOSITE_KVR_JSON_ADAPTER_HXX +#define COMPOSITE_KVR_JSON_ADAPTER_HXX + +#include "repository/CompositeKeyValueRepository.hxx" +#include "repository/KeyValueRepository.hxx" +#include "bspf.hxx" + +class CompositeKVRJsonAdapter : public CompositeKeyValueRepository { + public: + + CompositeKVRJsonAdapter(KeyValueRepositoryAtomic& kvr); + + shared_ptr get(const string& key) override; + + bool has(const string& key) override; + + void remove(const string& key) override; + + private: + + KeyValueRepositoryAtomic& myKvr; +}; + +#endif // COMPOSITE_KVR_JSON_ADAPTER_HXX diff --git a/src/common/repository/KeyValueRepository.hxx b/src/common/repository/KeyValueRepository.hxx index 885c6e185..5b943f173 100644 --- a/src/common/repository/KeyValueRepository.hxx +++ b/src/common/repository/KeyValueRepository.hxx @@ -23,6 +23,11 @@ #include "Variant.hxx" #include "bspf.hxx" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" + +class KeyValueRepositoryAtomic; + class KeyValueRepository { public: @@ -33,9 +38,22 @@ class KeyValueRepository virtual bool save(const std::map& values) = 0; + virtual KeyValueRepositoryAtomic* atomic() { return nullptr; } +}; + +class KeyValueRepositoryAtomic : public KeyValueRepository { + public: + virtual bool has(const string& key) = 0; + + virtual bool get(const string& key, string& value) = 0; + virtual bool save(const string& key, const Variant& value) = 0; virtual void remove(const string& key) = 0; + + KeyValueRepositoryAtomic* atomic() override { return this; } }; +#pragma clang diagnostic pop + #endif // KEY_VALUE_REPOSITORY_HXX diff --git a/src/common/repository/KeyValueRepositoryConfigfile.hxx b/src/common/repository/KeyValueRepositoryConfigfile.hxx index 248b7db1a..ef46417a5 100644 --- a/src/common/repository/KeyValueRepositoryConfigfile.hxx +++ b/src/common/repository/KeyValueRepositoryConfigfile.hxx @@ -21,7 +21,7 @@ #include "FSNode.hxx" #include "KeyValueRepository.hxx" -class KeyValueRepositoryConfigfile : public KeyValueRepository +class KeyValueRepositoryConfigfile : public KeyValueRepositoryAtomic { public: @@ -31,10 +31,6 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository bool save(const std::map& values) override; - bool save(const string& key, const Variant& value) override { return false; } - - void remove(const string& key) override {} - private: FilesystemNode myFile; diff --git a/src/common/repository/KeyValueRepositoryFile.hxx b/src/common/repository/KeyValueRepositoryFile.hxx new file mode 100644 index 000000000..46a2a9941 --- /dev/null +++ b/src/common/repository/KeyValueRepositoryFile.hxx @@ -0,0 +1,99 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef KEY_VALUE_REPOSITORY_FILE_HXX +#define KEY_VALUE_REPOSITORY_FILE_HXX + +#include + +#include "KeyValueRepository.hxx" +#include "Logger.hxx" +#include "FSNode.hxx" +#include "bspf.hxx" + +template +class KeyValueRepositoryFile : public KeyValueRepository { + public: + KeyValueRepositoryFile(const FilesystemNode& node); + + std::map load() override; + + bool save(const std::map& values) override; + + protected: + + const FilesystemNode& myNode; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMEMNTATION +/////////////////////////////////////////////////////////////////////////////// + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +template +KeyValueRepositoryFile::KeyValueRepositoryFile(const FilesystemNode& node) + : myNode(node) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +template +std::map KeyValueRepositoryFile::load() +{ + if (!myNode.exists()) return std::map(); + + stringstream in; + + try { + myNode.read(in); + return T::load(in); + } + catch (const runtime_error& err) { + Logger::error(err.what()); + + return std::map(); + } + catch (...) { + return std::map(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +template +bool KeyValueRepositoryFile::save(const std::map& values) +{ + if (values.size() == 0) return true; + + stringstream out; + + try { + T::save(out, values); + myNode.write(out); + + return true; + } + catch (const runtime_error& err) { + Logger::error(err.what()); + + return false; + } + catch (...) + { + return false; + } +} + +#endif // KEY_VALUE_REPOSITORY_FILE diff --git a/src/common/repository/KeyValueRepositoryJsonFile.cxx b/src/common/repository/KeyValueRepositoryJsonFile.cxx new file mode 100644 index 000000000..d52a225bb --- /dev/null +++ b/src/common/repository/KeyValueRepositoryJsonFile.cxx @@ -0,0 +1,81 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "repository/KeyValueRepositoryJsonFile.hxx" +#include "Logger.hxx" +#include "json_lib.hxx" + +using nlohmann::json; + +namespace { + json jsonIfValid(const string& s) { + json parsed = json::parse(s, nullptr, false); + + return parsed.is_discarded() ? json(s) : parsed; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +KeyValueRepositoryJsonFile::KeyValueRepositoryJsonFile(const FilesystemNode& node) + : KeyValueRepositoryFile(node) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +std::map KeyValueRepositoryJsonFile::load(istream& in) +{ + try { + std::map map; + + json deserialized = json::parse(in); + + if (!deserialized.is_object()) { + Logger::error("KeyVallueRepositoryJsonFile: not an object"); + + return map; + } + + for (const auto& [key, value] : deserialized.items()) + map[key] = value.is_string() ? value.get() : value.dump(); + + return map; + } + catch (const json::exception& err) { + Logger::error("KeyValueRepositoryJsonFile: error during deserialization: " + string(err.what())); + + return std::map(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool KeyValueRepositoryJsonFile::save(ostream& out, const std::map& values) +{ + try { + json serializedJson = json::object(); + + for (const auto& [key, value] : values) + serializedJson[key] = jsonIfValid(value.toString()); + + out << serializedJson.dump(2); + + return true; + } + catch (const json::exception& err) { + Logger::error("KeyValueRepositoryJsonFile: error during serialization: " + string(err.what())); + + return false; + } +} diff --git a/src/common/repository/KeyValueRepositoryJsonFile.hxx b/src/common/repository/KeyValueRepositoryJsonFile.hxx new file mode 100644 index 000000000..ae54fc5f3 --- /dev/null +++ b/src/common/repository/KeyValueRepositoryJsonFile.hxx @@ -0,0 +1,36 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef KEY_VALUE_REPOSITORY_JSON_FILE_HXX +#define KEY_VALUE_REPOSITORY_JSON_FILE_HXX + +#include +#include + +#include "repository/KeyValueRepositoryFile.hxx" +#include "bspf.hxx" + +class KeyValueRepositoryJsonFile : public KeyValueRepositoryFile { + public: + KeyValueRepositoryJsonFile(const FilesystemNode& node); + + static std::map load(istream& in); + + static bool save(ostream& out, const std::map& values); +}; + +#endif // KEY_VALUE_REPOSITORY_JSON_FILE_HXX diff --git a/src/common/repository/KeyValueRepositoryNoop.hxx b/src/common/repository/KeyValueRepositoryNoop.hxx index 6025556bb..631e8bd7f 100644 --- a/src/common/repository/KeyValueRepositoryNoop.hxx +++ b/src/common/repository/KeyValueRepositoryNoop.hxx @@ -20,7 +20,7 @@ #include "KeyValueRepository.hxx" -class KeyValueRepositoryNoop : public KeyValueRepository +class KeyValueRepositoryNoop : public KeyValueRepositoryAtomic { public: @@ -28,6 +28,10 @@ class KeyValueRepositoryNoop : public KeyValueRepository return std::map(); } + bool has(const string& key) override { return false; } + + bool get(const string& key, string& value) override { return false; } + bool save(const std::map& values) override { return false; } bool save(const string& key, const Variant& value) override { return false; } diff --git a/src/common/repository/KeyValueRepositoryPropertyFile.cxx b/src/common/repository/KeyValueRepositoryPropertyFile.cxx index deddf6441..07b3f7879 100644 --- a/src/common/repository/KeyValueRepositoryPropertyFile.cxx +++ b/src/common/repository/KeyValueRepositoryPropertyFile.cxx @@ -8,20 +8,14 @@ // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include -#include -#include -#include - #include "repository/KeyValueRepositoryPropertyFile.hxx" -#include "Logger.hxx" namespace { @@ -77,30 +71,14 @@ namespace { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - KeyValueRepositoryPropertyFile::KeyValueRepositoryPropertyFile(const FilesystemNode& node) - : myNode(node) + : KeyValueRepositoryFile(node) {} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -std::map KeyValueRepositoryPropertyFile::load() +std::map KeyValueRepositoryPropertyFile::load(istream& in) { std::map map; - if (!myNode.exists()) return map; - - stringstream in; - - try { - myNode.read(in); - } - catch (const runtime_error& err) { - Logger::error(err.what()); - - return map; - } - catch (...) { - return map; - } - // Loop reading properties string key, value; for(;;) @@ -129,12 +107,8 @@ std::map KeyValueRepositoryPropertyFile::load() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool KeyValueRepositoryPropertyFile::save(const std::map& values) +bool KeyValueRepositoryPropertyFile::save(ostream& out, const std::map& values) { - if (values.size() == 0) return true; - - stringstream out; - for (auto& [key, value]: values) { writeQuotedString(out, key); out.put(' '); @@ -142,22 +116,5 @@ bool KeyValueRepositoryPropertyFile::save(const std::map& value out.put('\n'); } - writeQuotedString(out, ""); - out.put('\n'); - out.put('\n'); - - try { - myNode.write(out); - - return true; - } - catch (const runtime_error& err) { - Logger::error(err.what()); - - return false; - } - catch (...) - { - return false; - } + return true; } diff --git a/src/common/repository/KeyValueRepositoryPropertyFile.hxx b/src/common/repository/KeyValueRepositoryPropertyFile.hxx index bf8405f05..13115f688 100644 --- a/src/common/repository/KeyValueRepositoryPropertyFile.hxx +++ b/src/common/repository/KeyValueRepositoryPropertyFile.hxx @@ -8,7 +8,7 @@ // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of @@ -18,25 +18,19 @@ #ifndef KEY_VALUE_REPOSITORY_PROPERTY_FILE_HXX #define KEY_VALUE_REPOSITORY_PROPERTY_FILE_HXX -#include "repository/KeyValueRepository.hxx" -#include "FSNode.hxx" +#include +#include + +#include "repository/KeyValueRepositoryFile.hxx" #include "bspf.hxx" -class KeyValueRepositoryPropertyFile : public KeyValueRepository { +class KeyValueRepositoryPropertyFile : public KeyValueRepositoryFile { public: KeyValueRepositoryPropertyFile(const FilesystemNode& node); - std::map load() override; + static std::map load(istream& in); - bool save(const std::map& values) override; - - bool save(const string& key, const Variant& value) override { return false; } - - void remove(const string& key) override {} - - private: - - const FilesystemNode& myNode; + static bool save(ostream& out, const std::map& values); }; -#endif +#endif // KEY_VALUE_REPOSITORY_PROPERTY_FILE_HXX diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx index abdb8755e..70603e324 100644 --- a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx @@ -40,6 +40,47 @@ std::map AbstractKeyValueRepositorySqlite::load() return values; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool AbstractKeyValueRepositorySqlite::has(const string& key) +{ + try { + SqliteStatement& stmt{stmtCount(key)}; + + if (!stmt.step()) throw new SqliteError("count failed"); + + const bool result = stmt.columnInt(0) != 0; + stmt.reset(); + + return result; + } + catch (const SqliteError& err) { + Logger::error(err.what()); + + return false; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool AbstractKeyValueRepositorySqlite::get(const string& key, string& value) +{ + try { + SqliteStatement& stmt{stmtSelectOne(key)}; + + if (!stmt.step()) return false; + value = stmt.columnText(0); + + stmt.reset(); + + return true; + } + catch (const SqliteError& err) { + Logger::error(err.what()); + + return false; + } +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool AbstractKeyValueRepositorySqlite::save(const std::map& values) { diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx index c3beb5179..1d9b7232a 100644 --- a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx @@ -6,10 +6,14 @@ #include "SqliteDatabase.hxx" #include "SqliteStatement.hxx" -class AbstractKeyValueRepositorySqlite : public KeyValueRepository +class AbstractKeyValueRepositorySqlite : public KeyValueRepositoryAtomic { public: + bool has(const string& key) override; + + bool get(const string& key, string& value) override; + std::map load() override; bool save(const std::map& values) override; @@ -23,6 +27,8 @@ class AbstractKeyValueRepositorySqlite : public KeyValueRepository virtual SqliteStatement& stmtInsert(const string& key, const string& value) = 0; virtual SqliteStatement& stmtSelect() = 0; virtual SqliteStatement& stmtDelete(const string& key) = 0; + virtual SqliteStatement& stmtCount(const string& key) = 0; + virtual SqliteStatement& stmtSelectOne(const string& key) = 0; virtual SqliteDatabase& database() = 0; }; diff --git a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx index d500fb26e..02bcf797c 100644 --- a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx @@ -35,15 +35,15 @@ shared_ptr CompositeKeyValueRepositorySqlite::get(const stri bool CompositeKeyValueRepositorySqlite::has(const string& key) { try { - myStmtCount->reset(); - myStmtCount->bind(1, key.c_str()); + myStmtCountSet->reset(); + myStmtCountSet->bind(1, key.c_str()); - if (!myStmtCount->step()) + if (!myStmtCountSet->step()) throw new SqliteError("count failed"); - Int32 rowCount = myStmtCount->columnInt(0); + Int32 rowCount = myStmtCountSet->columnInt(0); - myStmtCount->reset(); + myStmtCountSet->reset(); return rowCount > 0; } catch (const SqliteError& err) { @@ -78,9 +78,11 @@ void CompositeKeyValueRepositorySqlite::initialize() myStmtInsert = make_unique(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?, ?)"); myStmtSelect = make_unique(myDb, "SELECT `key2`, `VALUE` FROM `" + myTableName + "` WHERE `key1` = ?"); - myStmtCount = make_unique(myDb, "SELECT COUNT(*) FROM `" + myTableName + "` WHERE `key1` = ?"); + myStmtCountSet = make_unique(myDb, "SELECT COUNT(*) FROM `" + myTableName + "` WHERE `key1` = ?"); myStmtDelete = make_unique(myDb, "DELETE FROM `" + myTableName + "` WHERE `key1` = ? AND `key2` = ?"); myStmtDeleteSet = make_unique(myDb, "DELETE FROM `" + myTableName + "` WHERE `key1` = ?"); + myStmtSelectOne = make_unique(myDb, "SELECT `value` FROM `" + myTableName + "` WHERE `key1` = ? AND `key2` = ?"); + myStmtCount = make_unique(myDb, "SELECT COUNT(`key1`) FROM `" + myTableName + "` WHERE `key1` = ? AND `key2` = ?"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -121,6 +123,25 @@ SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtDelete( .bind(2, key.c_str()); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtSelectOne(const string& key) +{ + myRepo.myStmtSelectOne->reset(); + + return (*myRepo.myStmtSelectOne) + .bind(1, myKey.c_str()) + .bind(2, key.c_str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtCount(const string& key) +{ + myRepo.myStmtCount->reset(); + + return (*myRepo.myStmtCount) + .bind(1, myKey.c_str()) + .bind(2, key.c_str()); +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SqliteDatabase& CompositeKeyValueRepositorySqlite::ProxyRepository::database() diff --git a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx index 2dfc5f430..33dcb5024 100644 --- a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx +++ b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx @@ -49,6 +49,8 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository { SqliteStatement& stmtInsert(const string& key, const string& value) override; SqliteStatement& stmtSelect() override; SqliteStatement& stmtDelete(const string& key) override; + SqliteStatement& stmtCount(const string& key) override; + SqliteStatement& stmtSelectOne(const string& key) override; SqliteDatabase& database() override; private: @@ -71,9 +73,11 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository { unique_ptr myStmtInsert; unique_ptr myStmtSelect; - unique_ptr myStmtCount; + unique_ptr myStmtCountSet; unique_ptr myStmtDelete; unique_ptr myStmtDeleteSet; + unique_ptr myStmtSelectOne; + unique_ptr myStmtCount; private: diff --git a/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx index 179a5fdee..1a18272f2 100644 --- a/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx @@ -53,6 +53,24 @@ SqliteStatement& KeyValueRepositorySqlite::stmtDelete(const string& key) .bind(1, key.c_str()); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& KeyValueRepositorySqlite::stmtCount(const string& key) +{ + myStmtCount->reset(); + + return (*myStmtCount) + .bind(1, key.c_str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& KeyValueRepositorySqlite::stmtSelectOne(const string& key) +{ + myStmtSelectOne->reset(); + + return (*myStmtSelectOne) + .bind(1, key.c_str()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SqliteDatabase& KeyValueRepositorySqlite::database() { @@ -67,6 +85,8 @@ void KeyValueRepositorySqlite::initialize() ); myStmtInsert = make_unique(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?)"); - myStmtSelect = make_unique(myDb, "SELECT `key`, `VALUE` FROM `" + myTableName + "`"); + myStmtSelect = make_unique(myDb, "SELECT `key`, `value` FROM `" + myTableName + "`"); myStmtDelete = make_unique(myDb, "DELETE FROM `" + myTableName + "` WHERE `key` = ?"); + myStmtSelectOne = make_unique(myDb, "SELECT `value` FROM `" + myTableName + "` WHERE `key` = ?"); + myStmtCount = make_unique(myDb, "SELECT COUNT(`key`) FROM `" + myTableName + "` WHERE `key` = ?"); } diff --git a/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx index 7f7ac5591..062176548 100644 --- a/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx +++ b/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx @@ -37,6 +37,8 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite SqliteStatement& stmtSelect() override; SqliteStatement& stmtDelete(const string& key) override; SqliteDatabase& database() override; + SqliteStatement& stmtCount(const string& key) override; + SqliteStatement& stmtSelectOne(const string& key) override; private: @@ -46,6 +48,8 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite unique_ptr myStmtInsert; unique_ptr myStmtSelect; unique_ptr myStmtDelete; + unique_ptr myStmtSelectOne; + unique_ptr myStmtCount; private: diff --git a/src/common/repository/sqlite/SettingsDb.cxx b/src/common/repository/sqlite/SettingsDb.cxx index 788bceeb3..37a16535e 100644 --- a/src/common/repository/sqlite/SettingsDb.cxx +++ b/src/common/repository/sqlite/SettingsDb.cxx @@ -36,8 +36,10 @@ bool SettingsDb::initialize() mySettingsRepository = make_unique(*myDb, "settings"); mySettingsRepository->initialize(); - myPropertyRepository = make_unique(*myDb, "properties"); - myPropertyRepository->initialize(); + myPropertyRepositoryHost = make_unique(*myDb, "properties"); + myPropertyRepositoryHost->initialize(); + + myPropertyRepository = make_unique(*myPropertyRepositoryHost); } catch (const SqliteError& err) { Logger::error("sqlite DB " + databaseFileName() + " failed to initialize: " + err.what()); diff --git a/src/common/repository/sqlite/SettingsDb.hxx b/src/common/repository/sqlite/SettingsDb.hxx index 86aab53bd..317c5a63b 100644 --- a/src/common/repository/sqlite/SettingsDb.hxx +++ b/src/common/repository/sqlite/SettingsDb.hxx @@ -21,7 +21,7 @@ #include "bspf.hxx" #include "SqliteDatabase.hxx" #include "KeyValueRepositorySqlite.hxx" -#include "CompositeKeyValueRepositorySqlite.hxx" +#include "repository/CompositeKVRJsonAdapter.hxx" class SettingsDb { @@ -44,7 +44,8 @@ class SettingsDb shared_ptr myDb; unique_ptr mySettingsRepository; - unique_ptr myPropertyRepository; + unique_ptr myPropertyRepositoryHost; + unique_ptr myPropertyRepository; }; #endif // SETTINGS_DB_HXX diff --git a/src/emucore/Props.cxx b/src/emucore/Props.cxx index ff8fd496a..229b5750e 100644 --- a/src/emucore/Props.cxx +++ b/src/emucore/Props.cxx @@ -50,10 +50,11 @@ bool Properties::save(KeyValueRepository& repo) const std::map props; for (size_t i = 0; i < static_cast(PropType::NumTypes); i++) { - if (myProperties[i] == ourDefaultProperties[i]) - repo.remove(ourPropertyNames[i]); - else + if (myProperties[i] == ourDefaultProperties[i]) { + if (repo.atomic()) repo.atomic()->remove(ourPropertyNames[i]); + } else { props[ourPropertyNames[i]] = myProperties[i]; + } } return repo.save(props); diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index f9d8e459e..a56a49bb7 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -717,7 +717,7 @@ void Settings::setValue(const string& key, const Variant& value, bool persist) { auto it = myPermanentSettings.find(key); if(it != myPermanentSettings.end()) { - if (persist && it->second != value) myRespository->save(key, value); + if (persist && it->second != value && myRespository->atomic()) myRespository->atomic()->save(key, value); it->second = value; } else @@ -760,5 +760,6 @@ void Settings::migrate() { while (getInt(SETTINGS_VERSION_KEY) < SETTINGS_VERSION) migrateOne(); - myRespository->save(SETTINGS_VERSION_KEY, SETTINGS_VERSION); + if (myRespository->atomic()) + myRespository->atomic()->save(SETTINGS_VERSION_KEY, SETTINGS_VERSION); }