summaryrefslogtreecommitdiff
path: root/src/database/database.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-12-05 11:36:34 +1100
committerjacqueline <me@jacqueline.id.au>2023-12-05 11:36:34 +1100
commit4f5422e906b1d17720592d97bc0d5e82a71b1e5f (patch)
tree4e8116510d57fb54c30fc18f1fe591a9aff4e3a0 /src/database/database.cpp
parent67f2f2de8393951d2eabac26f9afab2dc9388713 (diff)
downloadtangara-fw-4f5422e906b1d17720592d97bc0d5e82a71b1e5f.tar.gz
Rewrite the track queue to work directly with database iterators
Diffstat (limited to 'src/database/database.cpp')
-rw-r--r--src/database/database.cpp139
1 files changed, 121 insertions, 18 deletions
diff --git a/src/database/database.cpp b/src/database/database.cpp
index 76d3a2ab..03451c05 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -756,6 +756,18 @@ auto Database::dbGetPage(const Continuation& c) -> Result<T>* {
return new Result<T>(std::move(records), next_page, prev_page);
}
+auto Database::dbCount(const Continuation& c) -> size_t {
+ std::unique_ptr<leveldb::Iterator> it{
+ db_->NewIterator(leveldb::ReadOptions{})};
+ size_t count = 0;
+ for (it->Seek({c.start_key.data(), c.start_key.size()});
+ it->Valid() && it->key().starts_with({c.prefix.data(), c.prefix.size()});
+ it->Next()) {
+ count++;
+ }
+ return count;
+}
+
template auto Database::dbGetPage<Track>(const Continuation& c)
-> Result<Track>*;
template auto Database::dbGetPage<std::pmr::string>(const Continuation& c)
@@ -880,6 +892,12 @@ Iterator::Iterator(const Iterator& other)
current_pos_(other.current_pos_),
prev_pos_(other.prev_pos_) {}
+Iterator& Iterator::operator=(const Iterator& other) {
+ current_pos_ = other.current_pos_;
+ prev_pos_ = other.prev_pos_;
+ return *this;
+}
+
auto Iterator::Next(Callback cb) -> void {
auto db = db_.lock();
if (!db) {
@@ -910,24 +928,35 @@ auto Iterator::NextSync() -> std::optional<IndexRecord> {
if (!db) {
return {};
}
- return db->worker_task_
- ->Dispatch<std::optional<IndexRecord>>(
- [=]() -> std::optional<IndexRecord> {
- std::lock_guard lock{pos_mutex_};
- if (!current_pos_) {
- return {};
- }
- std::unique_ptr<Result<IndexRecord>> res{
- db->dbGetPage<IndexRecord>(*current_pos_)};
- prev_pos_ = current_pos_;
- current_pos_ = res->next_page();
- if (!res || res->values().empty() || !res->values()[0]) {
- ESP_LOGI(kTag, "dropping empty result");
- return {};
- }
- return *res->values()[0];
- })
- .get();
+ std::lock_guard lock{pos_mutex_};
+ if (!current_pos_) {
+ return {};
+ }
+ std::unique_ptr<Result<IndexRecord>> res{
+ db->dbGetPage<IndexRecord>(*current_pos_)};
+ prev_pos_ = current_pos_;
+ current_pos_ = res->next_page();
+ if (!res || res->values().empty() || !res->values()[0]) {
+ ESP_LOGI(kTag, "dropping empty result");
+ return {};
+ }
+ return *res->values()[0];
+}
+
+auto Iterator::PeekSync() -> std::optional<IndexRecord> {
+ auto db = db_.lock();
+ if (!db) {
+ return {};
+ }
+ auto pos = current_pos_;
+ if (!pos) {
+ return {};
+ }
+ std::unique_ptr<Result<IndexRecord>> res{db->dbGetPage<IndexRecord>(*pos)};
+ if (!res || res->values().empty() || !res->values()[0]) {
+ return {};
+ }
+ return *res->values()[0];
}
auto Iterator::Prev(Callback cb) -> void {
@@ -950,8 +979,82 @@ auto Iterator::Prev(Callback cb) -> void {
});
}
+auto Iterator::Size() const -> size_t {
+ auto db = db_.lock();
+ if (!db) {
+ return {};
+ }
+ std::optional<Continuation> pos = current_pos_;
+ if (!pos) {
+ return 0;
+ }
+ return db->dbCount(*pos);
+}
+
auto Iterator::InvokeNull(Callback cb) -> void {
std::invoke(cb, std::optional<IndexRecord>{});
}
+TrackIterator::TrackIterator(const Iterator& it) : db_(it.db_), levels_() {
+ if (it.current_pos_) {
+ levels_.push_back(it);
+ }
+ NextLeaf();
+}
+
+TrackIterator::TrackIterator(const TrackIterator& other)
+ : db_(other.db_), levels_(other.levels_) {}
+
+TrackIterator& TrackIterator::operator=(TrackIterator&& other) {
+ levels_ = std::move(other.levels_);
+ return *this;
+}
+
+auto TrackIterator::Next() -> std::optional<TrackId> {
+ std::optional<TrackId> next{};
+ while (!next && !levels_.empty()) {
+ auto next_record = levels_.back().NextSync();
+ if (!next_record) {
+ levels_.pop_back();
+ NextLeaf();
+ continue;
+ }
+ // May still be nullopt_t; hence the loop.
+ next = next_record->track();
+ }
+ return next;
+}
+
+auto TrackIterator::Size() const -> size_t {
+ size_t size = 0;
+ TrackIterator copy{*this};
+ while (!copy.levels_.empty()) {
+ size += copy.levels_.back().Size();
+ copy.levels_.pop_back();
+ copy.NextLeaf();
+ }
+ return size;
+}
+
+auto TrackIterator::NextLeaf() -> void {
+ while (!levels_.empty()) {
+ ESP_LOGI(kTag, "check next candidate");
+ Iterator& candidate = levels_.back();
+ auto next = candidate.PeekSync();
+ if (!next) {
+ ESP_LOGI(kTag, "candidate is empty");
+ levels_.pop_back();
+ continue;
+ }
+ if (!next->track()) {
+ ESP_LOGI(kTag, "candidate is a branch");
+ candidate.NextSync();
+ levels_.push_back(Iterator{db_, next->Expand(1).value()});
+ continue;
+ }
+ ESP_LOGI(kTag, "candidate is a leaf");
+ break;
+ }
+}
+
} // namespace database