summaryrefslogtreecommitdiff
path: root/src/db/VHelper.cxx
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2018-09-02 12:35:10 +0200
committerMax Kellermann <max@musicpd.org>2018-09-02 17:37:02 +0200
commitc7c32a3ce92f4fa483a4a1b41aa39cdaa0afcf45 (patch)
tree500f6fefdadcb1953fb958213da434e865659ca7 /src/db/VHelper.cxx
parent53170ca2f24112df6f85b7e8313adf8ce839c627 (diff)
db/Print: move sort/window emulation code to class DatabaseVisitorHelper
That way, each plugin can decide to implement it better.
Diffstat (limited to 'src/db/VHelper.cxx')
-rw-r--r--src/db/VHelper.cxx133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/db/VHelper.cxx b/src/db/VHelper.cxx
new file mode 100644
index 000000000..f2bbc79a6
--- /dev/null
+++ b/src/db/VHelper.cxx
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "VHelper.hxx"
+#include "song/DetachedSong.hxx"
+#include "song/LightSong.hxx"
+#include "song/Filter.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+DatabaseVisitorHelper::DatabaseVisitorHelper(const DatabaseSelection &_selection,
+ VisitSong &visit_song) noexcept
+ :selection(_selection)
+{
+ // TODO: apply URI and SongFilter
+ assert(selection.uri.empty());
+ assert(selection.filter == nullptr);
+
+ if (selection.sort != TAG_NUM_OF_ITEM_TYPES) {
+ /* the client has asked us to sort the result; this is
+ pretty expensive, because instead of streaming the
+ result to the client, we need to copy it all into
+ this std::vector, and then sort it */
+
+ original_visit_song = std::move(visit_song);
+ visit_song = [this](const auto &song){
+ songs.emplace_back(song);
+ };
+ } else if (selection.window != RangeArg::All()) {
+ original_visit_song = std::move(visit_song);
+ visit_song = [this](const auto &song){
+ if (selection.window.Contains(counter++))
+ original_visit_song(song);
+ };
+ }
+}
+
+DatabaseVisitorHelper::~DatabaseVisitorHelper() noexcept = default;
+
+gcc_pure
+static bool
+CompareNumeric(const char *a, const char *b) noexcept
+{
+ long a_value = strtol(a, nullptr, 10);
+ long b_value = strtol(b, nullptr, 10);
+
+ return a_value < b_value;
+}
+
+gcc_pure
+static bool
+CompareTags(TagType type, bool descending, const Tag &a, const Tag &b) noexcept
+{
+ const char *a_value = a.GetSortValue(type);
+ const char *b_value = b.GetSortValue(type);
+
+ if (descending) {
+ using std::swap;
+ swap(a_value, b_value);
+ }
+
+ switch (type) {
+ case TAG_DISC:
+ case TAG_TRACK:
+ return CompareNumeric(a_value, b_value);
+
+ default:
+ return strcmp(a_value, b_value) < 0;
+ }
+}
+
+void
+DatabaseVisitorHelper::Commit()
+{
+ /* only needed if sorting is enabled */
+ if (selection.sort == TAG_NUM_OF_ITEM_TYPES)
+ return;
+
+ assert(original_visit_song);
+
+ /* sort the song collection */
+ const auto sort = selection.sort;
+ const auto descending = selection.descending;
+
+ if (sort == TagType(SORT_TAG_LAST_MODIFIED))
+ std::stable_sort(songs.begin(), songs.end(),
+ [descending](const DetachedSong &a, const DetachedSong &b){
+ return descending
+ ? a.GetLastModified() > b.GetLastModified()
+ : a.GetLastModified() < b.GetLastModified();
+ });
+ else
+ std::stable_sort(songs.begin(), songs.end(),
+ [sort, descending](const DetachedSong &a,
+ const DetachedSong &b){
+ return CompareTags(sort, descending,
+ a.GetTag(),
+ b.GetTag());
+ });
+
+ /* apply the "window" */
+ if (selection.window.end < songs.size())
+ songs.erase(std::next(songs.begin(), selection.window.end),
+ songs.end());
+
+ if (selection.window.start >= songs.size())
+ return;
+
+ songs.erase(songs.begin(),
+ std::next(songs.begin(), selection.window.start));
+
+ /* now pass all songs to the original visitor callback */
+ for (const auto &song : songs)
+ original_visit_song((LightSong)song);
+}