Files
transmission/libtransmission/torrent-queue.cc
Yat Ho 47eb4ee2bc refactor: dedicated class for torrent queue (#7332)
* Revert "feat: save queue order between sessions (#6753)"

This reverts commit 4db50dae10.

* refactor: new torrent queue class

* refactor: replace queue code with new class

* test: new tests for new class

* feat: store and load queue order across sessions

* build: xcode

* refactor: use set_difference instead of unordered_set

* code review: use `tr_torrent_id_t` as key

* fix: don't overflow when moving up/down

---------

Co-authored-by: Dzmitry Neviadomski <nevack.d@gmail.com>
2025-03-09 19:08:50 -05:00

135 lines
3.3 KiB
C++

// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <algorithm>
#include <string>
#include <string_view>
#include <vector>
#include "libtransmission/torrent-queue.h"
#include "libtransmission/tr-strbuf.h"
#include "libtransmission/variant.h"
namespace
{
using namespace std::literals;
[[nodiscard]] auto get_file_path(std::string_view config_dir) noexcept
{
return tr_pathbuf{ config_dir, '/', "queue.json"sv };
}
} // namespace
size_t tr_torrent_queue::add(tr_torrent_id_t const id)
{
queue_.push_back(id);
return std::size(queue_) - 1U;
}
void tr_torrent_queue::remove(tr_torrent_id_t const id)
{
auto const uid = static_cast<size_t>(id);
auto const pos = uid < std::size(pos_cache_) ? pos_cache_[uid] : 0U;
if (pos < std::size(queue_) && queue_[pos] == id)
{
queue_.erase(std::begin(queue_) + pos);
}
else
{
auto const remove_it = std::remove(std::begin(queue_), std::end(queue_), id);
queue_.erase(remove_it, std::end(queue_));
}
}
size_t tr_torrent_queue::get_pos(tr_torrent_id_t const id)
{
auto const uid = static_cast<size_t>(id);
if (auto n_cache = std::size(pos_cache_);
uid >= n_cache || pos_cache_[uid] >= std::size(queue_) || id != queue_[pos_cache_[uid]])
{
auto const begin = std::begin(queue_);
auto const end = std::end(queue_);
auto it = std::find(begin, end, id);
if (it == end)
{
return MaxQueuePosition;
}
pos_cache_.resize(std::max(uid + 1U, n_cache));
pos_cache_[uid] = it - begin;
}
return pos_cache_[uid];
}
void tr_torrent_queue::set_pos(tr_torrent_id_t const id, size_t new_pos)
{
auto const old_pos = get_pos(id);
auto const n_queue = std::size(queue_);
if (old_pos >= n_queue || queue_[old_pos] != id)
{
return;
}
new_pos = std::min(new_pos, n_queue - 1U);
if (old_pos == new_pos)
{
return;
}
auto const begin = std::begin(queue_);
auto const old_it = std::next(begin, old_pos);
auto const next_it = std::next(old_it);
auto const new_it = std::next(begin, new_pos);
if (old_pos > new_pos)
{
std::rotate(new_it, old_it, next_it);
}
else
{
std::rotate(old_it, next_it, std::next(new_it));
}
}
bool tr_torrent_queue::to_file() const
{
auto vec = tr_variant::Vector{};
vec.reserve(std::size(queue_));
for (auto const id : queue_)
{
vec.emplace_back(mediator_.store_filename(id));
}
return tr_variant_serde::json().to_file(std::move(vec), get_file_path(mediator_.config_dir()));
}
std::vector<std::string> tr_torrent_queue::from_file()
{
auto top = tr_variant_serde::json().parse_file(get_file_path(mediator_.config_dir()));
if (!top)
{
return {};
}
auto const* const vec = top->get_if<tr_variant::Vector>();
if (vec == nullptr)
{
return {};
}
auto ret = std::vector<std::string>{};
ret.reserve(std::size(*vec));
for (auto const& var : *vec)
{
if (auto file = var.value_if<std::string_view>(); file)
{
ret.emplace_back(*file);
}
}
return ret;
}