You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
4.9 KiB
175 lines
4.9 KiB
/*
|
|
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <stdint.h>
|
|
#include <cstdint>
|
|
#include <future>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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 "records.hpp"
|
|
#include "result.hpp"
|
|
#include "shared_string.h"
|
|
#include "tag_parser.hpp"
|
|
#include "tasks.hpp"
|
|
#include "track.hpp"
|
|
|
|
namespace database {
|
|
|
|
template <typename T>
|
|
struct Continuation {
|
|
std::shared_ptr<std::unique_ptr<leveldb::Iterator>> iterator;
|
|
std::string prefix;
|
|
std::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 <typename T>
|
|
class Result {
|
|
public:
|
|
auto values() const -> const std::vector<T>& { return values_; }
|
|
|
|
auto next_page() -> std::optional<Continuation<T>>& { return next_page_; }
|
|
auto prev_page() -> std::optional<Continuation<T>>& { return prev_page_; }
|
|
|
|
Result(const std::vector<T>&& values,
|
|
std::optional<Continuation<T>> next,
|
|
std::optional<Continuation<T>> prev)
|
|
: values_(values), next_page_(next), prev_page_(prev) {}
|
|
|
|
Result(const Result&) = delete;
|
|
Result& operator=(const Result&) = delete;
|
|
|
|
private:
|
|
std::vector<T> values_;
|
|
std::optional<Continuation<T>> next_page_;
|
|
std::optional<Continuation<T>> prev_page_;
|
|
};
|
|
|
|
class IndexRecord {
|
|
public:
|
|
explicit IndexRecord(const IndexKey&,
|
|
std::optional<shared_string>,
|
|
std::optional<TrackId>);
|
|
|
|
auto text() const -> std::optional<shared_string>;
|
|
auto track() const -> std::optional<TrackId>;
|
|
|
|
auto Expand(std::size_t) const -> std::optional<Continuation<IndexRecord>>;
|
|
|
|
private:
|
|
IndexKey key_;
|
|
std::optional<shared_string> override_text_;
|
|
std::optional<TrackId> track_;
|
|
};
|
|
|
|
class Database {
|
|
public:
|
|
enum DatabaseError {
|
|
ALREADY_OPEN,
|
|
FAILED_TO_OPEN,
|
|
};
|
|
static auto Open(IFileGatherer& file_gatherer, ITagParser& tag_parser)
|
|
-> cpp::result<Database*, DatabaseError>;
|
|
static auto Open() -> cpp::result<Database*, DatabaseError>;
|
|
|
|
static auto Destroy() -> void;
|
|
|
|
~Database();
|
|
|
|
auto Update() -> std::future<void>;
|
|
|
|
auto GetTrackPath(TrackId id) -> std::future<std::optional<std::string>>;
|
|
|
|
auto GetTrack(TrackId id) -> std::future<std::optional<Track>>;
|
|
|
|
/*
|
|
* Fetches data for multiple tracks more efficiently than multiple calls to
|
|
* GetTrack.
|
|
*/
|
|
auto GetBulkTracks(std::vector<TrackId> id)
|
|
-> std::future<std::vector<std::optional<Track>>>;
|
|
|
|
auto GetIndexes() -> std::vector<IndexInfo>;
|
|
auto GetTracksByIndex(const IndexInfo& index, std::size_t page_size)
|
|
-> std::future<Result<IndexRecord>*>;
|
|
auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>;
|
|
auto GetDump(std::size_t page_size) -> std::future<Result<std::string>*>;
|
|
|
|
template <typename T>
|
|
auto GetPage(Continuation<T>* c) -> std::future<Result<T>*>;
|
|
|
|
Database(const Database&) = delete;
|
|
Database& operator=(const Database&) = delete;
|
|
|
|
private:
|
|
// Owned. Dumb pointers because destruction needs to be done in an explicit
|
|
// order.
|
|
leveldb::DB* db_;
|
|
leveldb::Cache* cache_;
|
|
|
|
std::shared_ptr<tasks::Worker> worker_task_;
|
|
|
|
// Not owned.
|
|
IFileGatherer& file_gatherer_;
|
|
ITagParser& tag_parser_;
|
|
|
|
Database(leveldb::DB* db,
|
|
leveldb::Cache* cache,
|
|
IFileGatherer& file_gatherer,
|
|
ITagParser& tag_parser,
|
|
std::shared_ptr<tasks::Worker> worker);
|
|
|
|
auto dbMintNewTrackId() -> TrackId;
|
|
auto dbEntomb(TrackId track, uint64_t hash) -> void;
|
|
|
|
auto dbPutTrackData(const TrackData& s) -> void;
|
|
auto dbGetTrackData(TrackId id) -> std::optional<TrackData>;
|
|
auto dbPutHash(const uint64_t& hash, TrackId i) -> void;
|
|
auto dbGetHash(const uint64_t& hash) -> std::optional<TrackId>;
|
|
auto dbCreateIndexesForTrack(Track track) -> void;
|
|
|
|
template <typename T>
|
|
auto dbGetPage(const Continuation<T>& c) -> Result<T>*;
|
|
|
|
template <typename T>
|
|
auto ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val)
|
|
-> std::optional<T>;
|
|
};
|
|
|
|
template <>
|
|
auto Database::ParseRecord<IndexRecord>(const leveldb::Slice& key,
|
|
const leveldb::Slice& val)
|
|
-> std::optional<IndexRecord>;
|
|
template <>
|
|
auto Database::ParseRecord<Track>(const leveldb::Slice& key,
|
|
const leveldb::Slice& val)
|
|
-> std::optional<Track>;
|
|
template <>
|
|
auto Database::ParseRecord<std::string>(const leveldb::Slice& key,
|
|
const leveldb::Slice& val)
|
|
-> std::optional<std::string>;
|
|
|
|
} // namespace database
|
|
|