/* * Copyright 2023 jacqueline * * SPDX-License-Identifier: GPL-3.0-only */ #pragma once #include #include #include #include #include #include #include #include #include #include "collation.hpp" #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 { struct Continuation { std::pmr::string prefix; std::pmr::string start_key; bool forward; bool was_prev_forward; size_t page_size; }; /* * Wrapper for a set of results from the database. Owns the list of results, as * well as a continuation token that can be used to continue fetching more * results if they were paginated. */ template class Result { public: auto values() const -> const std::vector>& { return values_; } auto next_page() -> std::optional& { return next_page_; } auto prev_page() -> std::optional& { return prev_page_; } Result(const std::vector>&& values, std::optional next, std::optional prev) : values_(values), next_page_(next), prev_page_(prev) {} Result(const Result&) = delete; Result& operator=(const Result&) = delete; private: std::vector> values_; std::optional next_page_; std::optional prev_page_; }; class IndexRecord { public: explicit IndexRecord(const IndexKey&, std::optional, std::optional); auto text() const -> std::optional; auto track() const -> std::optional; auto Expand(std::size_t) const -> std::optional; auto ExpandHeader() const -> IndexKey::Header; private: IndexKey key_; std::optional override_text_; std::optional track_; }; class Database { public: enum DatabaseError { ALREADY_OPEN, FAILED_TO_OPEN, }; static auto Open(IFileGatherer& file_gatherer, ITagParser& tag_parser, locale::ICollator& collator, tasks::Worker& bg_worker) -> cpp::result; static auto Destroy() -> void; ~Database(); auto Update() -> std::future; auto GetTrackPath(TrackId id) -> std::future>; auto GetTrack(TrackId id) -> std::future>; /* * Fetches data for multiple tracks more efficiently than multiple calls to * GetTrack. */ auto GetBulkTracks(std::vector id) -> std::future>>; auto GetIndexes() -> std::vector; auto GetTracksByIndex(IndexId index, std::size_t page_size) -> std::future*>; auto GetTracks(std::size_t page_size) -> std::future*>; auto GetDump(std::size_t page_size) -> std::future*>; template auto GetPage(Continuation* c) -> std::future*>; 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_; std::shared_ptr worker_task_; // Not owned. IFileGatherer& file_gatherer_; ITagParser& tag_parser_; locale::ICollator& collator_; Database(leveldb::DB* db, leveldb::Cache* cache, IFileGatherer& file_gatherer, ITagParser& tag_parser, locale::ICollator& collator, std::shared_ptr worker); 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; template auto dbGetPage(const Continuation& c) -> Result*; template auto ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val) -> std::shared_ptr; }; template <> auto Database::ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val) -> std::shared_ptr; template <> auto Database::ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val) -> std::shared_ptr; template <> auto Database::ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val) -> std::shared_ptr; /* * Utility for accessing a large set of database records, one record at a time. */ class Iterator { public: Iterator(std::weak_ptr, const IndexInfo&); Iterator(std::weak_ptr, const Continuation&); Iterator(const Iterator &); auto database() const { return db_; } using Callback = std::function)>; auto Next(Callback) -> void; auto NextSync() -> std::optional; auto Prev(Callback) -> void; private: auto InvokeNull(Callback) -> void; std::weak_ptr db_; std::mutex pos_mutex_; std::optional current_pos_; std::optional prev_pos_; }; } // namespace database