1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#pragma once
#include <stdint.h>
#include <cstdint>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "file_gatherer.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 "song.hpp"
#include "tag_parser.hpp"
namespace database {
typedef std::unique_ptr<leveldb::Iterator> Continuation;
/*
* 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() -> std::vector<T>* { return values_.release(); }
auto continuation() -> Continuation { return std::move(c_); }
auto HasMore() -> bool { return c_->Valid(); }
Result(std::vector<T>* values, Continuation c)
: values_(values), c_(std::move(c)) {}
Result(std::unique_ptr<std::vector<T>> values, Continuation c)
: values_(std::move(values)), c_(std::move(c)) {}
Result(Result&& other)
: values_(move(other.values_)), c_(std::move(other.c_)) {}
Result operator=(Result&& other) {
return Result(other.values(), std::move(other.continuation()));
}
Result(const Result&) = delete;
Result& operator=(const Result&) = delete;
private:
std::unique_ptr<std::vector<T>> values_;
Continuation c_;
};
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 GetSongs(std::size_t page_size) -> std::future<Result<Song>>;
auto GetMoreSongs(std::size_t page_size, Continuation c)
-> std::future<Result<Song>>;
auto GetDump(std::size_t page_size) -> std::future<Result<std::string>>;
auto GetMoreDump(std::size_t page_size, Continuation c)
-> std::future<Result<std::string>>;
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_;
// Not owned.
IFileGatherer* file_gatherer_;
ITagParser* tag_parser_;
Database(leveldb::DB* db,
leveldb::Cache* cache,
IFileGatherer* file_gatherer,
ITagParser* tag_parser);
auto dbMintNewSongId() -> SongId;
auto dbEntomb(SongId song, uint64_t hash) -> void;
auto dbPutSongData(const SongData& s) -> void;
auto dbGetSongData(SongId id) -> std::optional<SongData>;
auto dbPutHash(const uint64_t& hash, SongId i) -> void;
auto dbGetHash(const uint64_t& hash) -> std::optional<SongId>;
auto dbPutSong(SongId id, const std::string& path, const uint64_t& hash)
-> void;
template <typename T>
using Parser = std::function<std::optional<T>(const leveldb::Slice& key,
const leveldb::Slice& value)>;
template <typename T>
auto Query(const leveldb::Slice& prefix,
std::size_t max_results,
Parser<T> parser) -> Result<T> {
leveldb::Iterator* it = db_->NewIterator(leveldb::ReadOptions());
it->Seek(prefix);
return Query(it, max_results, parser);
}
template <typename T>
auto Query(leveldb::Iterator* it, std::size_t max_results, Parser<T> parser)
-> Result<T> {
auto results = std::make_unique<std::vector<T>>();
for (std::size_t i = 0; i < max_results && it->Valid(); i++) {
std::optional<T> r = std::invoke(parser, it->key(), it->value());
if (r) {
results->push_back(*r);
}
it->Next();
}
return {std::move(results), std::unique_ptr<leveldb::Iterator>(it)};
}
};
} // namespace database
|