Simple KV storage in session().local().

This commit is contained in:
John Preston
2025-11-13 12:49:41 +04:00
parent a293134b5e
commit 0e5e4ca7ea
2 changed files with 169 additions and 4 deletions

View File

@@ -100,6 +100,7 @@ enum { // Local Storage Keys
lskInlineBotsDownloads = 0x1b, // no data
lskMediaLastPlaybackPositions = 0x1c, // no data
lskBotStorages = 0x1d, // data: PeerId botId
lskPrefs = 0x1e, // no data
};
auto EmptyMessageDraftSources()
@@ -177,6 +178,7 @@ Account::Account(not_null<Main::Account*> owner, const QString &dataName)
, _cacheTotalTimeLimit(Database::Settings().totalTimeLimit)
, _cacheBigFileTotalTimeLimit(Database::Settings().totalTimeLimit)
, _writeMapTimer([=] { writeMap(); })
, _writePrefsTimer([=] { writePrefs(); })
, _writeLocationsTimer([=] { writeLocations(); })
, _writeSearchSuggestionsTimer([=] { writeSearchSuggestions(); }) {
}
@@ -184,8 +186,13 @@ Account::Account(not_null<Main::Account*> owner, const QString &dataName)
Account::~Account() {
Expects(!_writeSearchSuggestionsTimer.isActive());
if (_localKey && _mapChanged) {
writeMap();
if (_localKey) {
if (_prefsChanged) {
writePrefs();
}
if (_mapChanged) {
writeMap();
}
}
}
@@ -236,6 +243,7 @@ void Account::clearLegacyFiles() {
base::flat_set<QString> Account::collectGoodNames() const {
const auto keys = {
_prefsKey,
_locationsKey,
_settingsKey,
_installedStickersKey,
@@ -342,7 +350,7 @@ Account::ReadMapResult Account::readMapWith(
base::flat_map<PeerId, bool> draftsNotReadMap;
base::flat_map<PeerId, FileKey> botStoragesMap;
base::flat_map<PeerId, bool> botStoragesNotReadMap;
quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedPeersKey = 0;
quint64 prefsKey = 0, locationsKey = 0, reportSpamStatusesKey = 0, trustedPeersKey = 0;
quint64 recentStickersKeyOld = 0;
quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0;
quint64 installedMasksKey = 0, recentMasksKey = 0, archivedMasksKey = 0;
@@ -398,6 +406,9 @@ Account::ReadMapResult Account::readMapWith(
// Just ignore the key, it will be removed as a leaked one.
}
} break;
case lskPrefs: {
map.stream >> prefsKey;
} break;
case lskLocations: {
map.stream >> locationsKey;
} break;
@@ -506,6 +517,7 @@ Account::ReadMapResult Account::readMapWith(
_botStoragesMap = botStoragesMap;
_botStoragesNotReadMap = botStoragesNotReadMap;
_prefsKey = prefsKey;
_locationsKey = locationsKey;
_trustedPeersKey = trustedPeersKey;
_recentStickersKeyOld = recentStickersKeyOld;
@@ -540,6 +552,9 @@ Account::ReadMapResult Account::readMapWith(
_mapChanged = false;
}
if (_prefsKey) {
readPrefs();
}
if (_locationsKey) {
readLocations();
}
@@ -620,6 +635,7 @@ void Account::writeMap() {
if (!self.isEmpty()) mapSize += sizeof(quint32) + Serialize::bytearraySize(self);
if (!_draftsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
if (!_draftCursorsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2;
if (_prefsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_trustedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
@@ -665,6 +681,9 @@ void Account::writeMap() {
mapData.stream << quint64(value) << SerializePeerId(key);
}
}
if (_prefsKey) {
mapData.stream << quint32(lskPrefs) << quint64(_prefsKey);
}
if (_locationsKey) {
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
}
@@ -750,7 +769,7 @@ void Account::reset() {
_draftsNotReadMap.clear();
_botStoragesMap.clear();
_botStoragesNotReadMap.clear();
_locationsKey = _trustedPeersKey = 0;
_prefsKey = _locationsKey = _trustedPeersKey = 0;
_recentStickersKeyOld = 0;
_installedStickersKey = 0;
_featuredStickersKey = 0;
@@ -3657,4 +3676,118 @@ Webview::StorageId TonSiteStorageId() {
return result;
}
template <>
std::optional<bool> Account::readPrefImpl<bool>(std::string_view key) {
if (const auto data = readPrefGeneric(key)) {
return !data->isEmpty();
}
return {};
}
template <>
void Account::writePrefImpl<bool>(std::string_view key, bool value) {
writePrefGeneric(key, value ? "\x1"_q : QByteArray());
}
void Account::clearPref(std::string_view key) {
const auto i = _prefs.find(QByteArray(key.data(), key.size()));
if (i == end(_prefs)) {
return;
}
_prefs.erase(i);
writePrefsDelayed();
}
void Account::writePrefGeneric(
std::string_view key,
const QByteArray &value) {
const auto raw = QByteArray(key.data(), key.size());
if (const auto i = _prefs.find(raw); i != end(_prefs)) {
if (i->second == value) {
return;
}
i->second = value;
} else {
_prefs.emplace(raw, value);
}
writePrefsDelayed();
}
std::optional<QByteArray> Account::readPrefGeneric(std::string_view key) {
const auto i = _prefs.find(QByteArray(key.data(), key.size()));
return (i != end(_prefs)) ? i->second : std::optional<QByteArray>();
}
void Account::writePrefsDelayed() {
_prefsChanged = true;
_writePrefsTimer.callOnce(kDelayedWriteTimeout);
}
void Account::writePrefs() {
_writePrefsTimer.cancel();
if (!_prefsChanged) {
return;
}
_prefsChanged = false;
if (_prefs.empty()) {
if (_prefsKey) {
ClearKey(_prefsKey, _basePath);
_prefsKey = 0;
writeMapDelayed();
}
} else {
if (!_prefsKey) {
_prefsKey = GenerateKey(_basePath);
writeMapQueued();
}
quint32 size = sizeof(quint32);
for (const auto &[key, value] : _prefs) {
size += 2 * sizeof(quint32) + key.size() + value.size();
}
EncryptedDescriptor data(size);
data.stream << quint32(_prefs.size());
for (const auto &[key, value] : _prefs) {
data.stream << quint32(key.size()) << quint32(value.size());
data.stream.writeRawData(key.constData(), key.size());
data.stream.writeRawData(value.constData(), value.size());
}
FileWriteDescriptor file(_prefsKey, _basePath);
file.writeEncrypted(data, _localKey);
}
}
void Account::readPrefs() {
FileReadDescriptor prefs;
if (!ReadEncryptedFile(prefs, _prefsKey, _basePath, _localKey)) {
ClearKey(_prefsKey, _basePath);
_prefsKey = 0;
writeMapDelayed();
return;
}
auto count = quint32();
prefs.stream >> count;
if (prefs.stream.status() != QDataStream::Ok) {
return;
}
auto map = base::flat_map<QByteArray, QByteArray>();
map.reserve(count);
for (auto i = quint32(); i != count; ++i) {
auto keySize = quint32(), valueSize = quint32();
prefs.stream >> keySize >> valueSize;
auto key = QByteArray(keySize, Qt::Uninitialized);
auto value = QByteArray(valueSize, Qt::Uninitialized);
prefs.stream.readRawData(key.data(), keySize);
prefs.stream.readRawData(value.data(), valueSize);
if (prefs.stream.status() != QDataStream::Ok) {
return;
}
map.emplace(std::move(key), std::move(value));
}
_prefs = std::move(map);
}
} // namespace Storage

View File

@@ -182,6 +182,19 @@ public:
[[nodiscard]] bool hasPeerTrustedPayForMessageEntry(PeerId peerId) const;
void clearPeerTrustedPayForMessage(PeerId peerId);
template <typename Type, typename Other>
void writePref(std::string_view key, Other &&value) {
writePrefImpl<Type>(key, std::forward<Other>(value));
}
void clearPref(std::string_view key);
template <typename Type, typename Other = Type>
[[nodiscard]] Type readPref(
std::string_view key,
Other &&fallback = Type()) {
return readPrefImpl<Type>(key).value_or(std::forward<Other>(fallback));
}
void enforceModernStorageIdBots();
[[nodiscard]] Webview::StorageId resolveStorageIdBots();
[[nodiscard]] Webview::StorageId resolveStorageIdOther();
@@ -238,6 +251,10 @@ private:
void writeLocationsQueued();
void writeLocationsDelayed();
void readPrefs();
void writePrefs();
void writePrefsDelayed();
std::unique_ptr<Main::SessionSettings> readSessionSettings();
void writeSessionSettings(Main::SessionSettings *stored);
@@ -282,6 +299,16 @@ private:
Fn<RecentHashtagPack()> getPack,
const QString &text);
template <typename Type>
void writePrefImpl(std::string_view key, Type value);
template <typename Type>
[[nodiscard]] std::optional<Type> readPrefImpl(std::string_view key);
void writePrefGeneric(std::string_view key, const QByteArray &value);
[[nodiscard]] std::optional<QByteArray> readPrefGeneric(
std::string_view key);
const not_null<Main::Account*> _owner;
const QString _dataName;
const FileKey _dataNameKey = 0;
@@ -307,6 +334,7 @@ private:
QByteArray _downloadsSerialized;
Fn<std::optional<QByteArray>()> _downloadsSerialize;
FileKey _prefsKey = 0;
FileKey _locationsKey = 0;
FileKey _trustedPeersKey = 0;
FileKey _installedStickersKey = 0;
@@ -351,12 +379,16 @@ private:
Webview::StorageId _webviewStorageIdBots;
Webview::StorageId _webviewStorageIdOther;
base::flat_map<QByteArray, QByteArray> _prefs;
int _oldMapVersion = 0;
base::Timer _writeMapTimer;
base::Timer _writePrefsTimer;
base::Timer _writeLocationsTimer;
base::Timer _writeSearchSuggestionsTimer;
bool _mapChanged = false;
bool _prefsChanged = false;
bool _locationsChanged = false;
QImage _roundPlaceholder;