From 1573a8c4cde1cd9528b422b2dcc598e37ffe94a7 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 2 May 2024 19:12:26 +1000 Subject: WIP merge cyclically dependent components into one big component --- src/tangara/database/database.hpp | 244 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 src/tangara/database/database.hpp (limited to 'src/tangara/database/database.hpp') diff --git a/src/tangara/database/database.hpp b/src/tangara/database/database.hpp new file mode 100644 index 00000000..35b76a13 --- /dev/null +++ b/src/tangara/database/database.hpp @@ -0,0 +1,244 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "collation.hpp" +#include "cppbor.h" +#include "file_gatherer.hpp" +#include "index.hpp" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/slice.h" +#include "memory_resource.hpp" +#include "records.hpp" +#include "result.hpp" +#include "tag_parser.hpp" +#include "tasks.hpp" +#include "track.hpp" + +namespace database { + +const uint8_t kCurrentDbVersion = 6; + +struct SearchKey; +class Record; +class Iterator; + +/* + * Handle to an open database. This can be used to store large amounts of + * persistent data on the SD card, in a manner that can be retrieved later very + * quickly. + * + * A database includes a number of 'indexes'. Each index is a sorted, + * hierarchical view of all the playable tracks on the device. + */ +class Database { + public: + enum DatabaseError { + ALREADY_OPEN, + FAILED_TO_OPEN, + }; + static auto Open(IFileGatherer& file_gatherer, + ITagParser& tag_parser, + locale::ICollator& collator, + tasks::WorkerPool& bg_worker) + -> cpp::result; + + static auto Destroy() -> void; + + ~Database(); + + auto schemaVersion() -> std::string; + + auto sizeOnDiskBytes() -> size_t; + + /* Adds an arbitrary record to the database. */ + auto put(const std::string& key, const std::string& val) -> void; + + /* Retrives a value previously stored with `put`. */ + auto get(const std::string& key) -> std::optional; + + auto getTrackPath(TrackId id) -> std::optional; + auto getTrack(TrackId id) -> std::shared_ptr; + + auto getIndexes() -> std::vector; + auto updateIndexes() -> void; + auto isUpdating() -> bool; + + // Cannot be copied or moved. + Database(const Database&) = delete; + Database& operator=(const Database&) = delete; + + private: + friend class Iterator; + + // Owned. Dumb pointers because destruction needs to be done in an explicit + // order. + leveldb::DB* db_; + leveldb::Cache* cache_; + + // Not owned. + IFileGatherer& file_gatherer_; + ITagParser& tag_parser_; + locale::ICollator& collator_; + + std::atomic is_updating_; + + Database(leveldb::DB* db, + leveldb::Cache* cache, + IFileGatherer& file_gatherer, + ITagParser& tag_parser, + locale::ICollator& collator); + + auto dbMintNewTrackId() -> TrackId; + + auto dbEntomb(TrackId track, uint64_t hash) -> void; + auto dbPutTrackData(const TrackData& s) -> void; + auto dbGetTrackData(TrackId id) -> std::shared_ptr; + auto dbPutHash(const uint64_t& hash, TrackId i) -> void; + auto dbGetHash(const uint64_t& hash) -> std::optional; + + auto dbCreateIndexesForTrack(const Track& track) -> void; + auto dbRemoveIndexes(std::shared_ptr) -> void; + + auto dbIngestTagHashes(const TrackTags&, + std::pmr::unordered_map&) -> void; + auto dbRecoverTagsFromHashes(const std::pmr::unordered_map&) + -> std::shared_ptr; + + auto getRecord(const SearchKey& c) + -> std::optional>; + auto countRecords(const SearchKey& c) -> size_t; +}; + +/* + * Container for the data needed to iterate through database records. This is a + * lower-level type that the higher-level iterators are built from; most users + * outside this namespace shouldn't need to work with continuations. + */ +struct SearchKey { + std::pmr::string prefix; + /* If not given, then iteration starts from `prefix`. */ + std::optional key; + int offset; + + auto startKey() const -> std::string_view; +}; + +/* + * A record belonging to one of the database's indexes. This may either be a + * leaf record, containing a track id, or a branch record, containing a new + * Header to retrieve results at the next level of the index. + */ +class Record { + public: + Record(const IndexKey&, const leveldb::Slice&); + + Record(const Record&) = default; + Record& operator=(const Record& other) = default; + + auto text() const -> std::string_view; + auto contents() const -> const std::variant&; + + private: + std::pmr::string text_; + std::variant contents_; +}; + +/* + * Utility for accessing a large set of database records, one record at a time. + */ +class Iterator { + public: + Iterator(std::shared_ptr, IndexId); + Iterator(std::shared_ptr, const IndexKey::Header&); + + Iterator(const Iterator&) = default; + Iterator& operator=(const Iterator& other) = default; + + auto value() const -> const std::optional&; + std::optional operator*() const { return value(); } + + auto next() -> void; + std::optional operator++() { + next(); + return value(); + } + std::optional operator++(int) { + auto val = value(); + next(); + return val; + } + + auto prev() -> void; + std::optional operator--() { + prev(); + return value(); + } + std::optional operator--(int) { + auto val = value(); + prev(); + return val; + } + + auto count() const -> size_t; + + private: + auto iterate(const SearchKey& key) -> void; + + friend class TrackIterator; + + std::weak_ptr db_; + SearchKey key_; + std::optional current_; +}; + +class TrackIterator { + public: + TrackIterator(const Iterator&); + + TrackIterator(const TrackIterator&) = default; + TrackIterator& operator=(TrackIterator&& other) = default; + + auto value() const -> std::optional; + std::optional operator*() const { return value(); } + + auto next() -> void; + std::optional operator++() { + next(); + return value(); + } + std::optional operator++(int) { + auto val = value(); + next(); + return val; + } + + auto count() const -> size_t; + + private: + TrackIterator(std::weak_ptr); + auto next(bool advance) -> void; + + std::weak_ptr db_; + std::vector levels_; +}; + +} // namespace database -- cgit v1.2.3