[vlc-devel] [PATCH 05/13] qt: medialib: introduce list cache

Romain Vimont rom1v at videolabs.io
Mon Nov 23 17:30:19 CET 2020


Add a separate class to provide local data caching, independant of the
MLBaseModel implementation.

It will be used in further commits.
---
 modules/gui/qt/Makefile.am        |   2 +
 modules/gui/qt/util/listcache.hpp | 171 ++++++++++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 modules/gui/qt/util/listcache.hpp

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index c158dae6eb..9d36265842 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -202,6 +202,7 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/util/color_scheme_model.cpp gui/qt/util/color_scheme_model.hpp \
 	gui/qt/util/imagehelper.cpp gui/qt/util/imagehelper.hpp \
 	gui/qt/util/i18n.cpp gui/qt/util/i18n.hpp \
+	gui/qt/util/listcache.hpp \
 	gui/qt/util/navigation_history.cpp gui/qt/util/navigation_history.hpp \
 	gui/qt/util/qml_main_context.cpp \
 	gui/qt/util/qml_main_context.hpp \
@@ -342,6 +343,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/util/audio_device_model.moc.cpp \
 	gui/qt/util/color_scheme_model.moc.cpp \
 	gui/qt/util/i18n.moc.cpp \
+	gui/qt/util/listcache.moc.cpp \
 	gui/qt/util/navigation_history.moc.cpp \
 	gui/qt/util/qml_main_context.moc.cpp \
 	gui/qt/util/qmleventfilter.moc.cpp \
diff --git a/modules/gui/qt/util/listcache.hpp b/modules/gui/qt/util/listcache.hpp
new file mode 100644
index 0000000000..d18abe8eb7
--- /dev/null
+++ b/modules/gui/qt/util/listcache.hpp
@@ -0,0 +1,171 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef LISTCACHE_HPP
+#define LISTCACHE_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "vlc_common.h"
+#include <memory>
+#include <vector>
+#include <QtGlobal>
+#include <QObject>
+
+/**
+ * `ListCache<T>` represents a cache for a (constant) list of items.
+ *
+ * The caller must provide a `ListCacheLoader<T>`, defining the following
+ * methods:
+ *  - `count()` returns the number of items in the list;
+ *  - `load(index, count)` returning the items for the requested interval.
+ *
+ * The precise cache strategy is unspecified (it may change in the future), but
+ * the general principle is to keep locally only a part of the whole data.
+ *
+ * The list of items it represents is assumed constant:
+ *  1. the list size will never change once initialized,
+ *  2. the items retrieved by several calls at a specific location should be
+ *     "the same".
+ *
+ * Note that (2.) might not always be respected in practice, for example if the
+ * data is retrieved from a database where content changes between calls. The
+ * cache does not really care, the data will just be inconcistent for the user.
+ *
+ * All its public methods must be called from the UI thread.
+ */
+
+template <typename T>
+struct ListCacheLoader
+{
+    virtual ~ListCacheLoader() = default;
+    virtual size_t count() const = 0;
+    virtual std::vector<T> load(size_t index, size_t count) const = 0;
+};
+
+/* Non-template class for signals */
+class ListCacheBase : public QObject
+{
+    Q_OBJECT
+
+signals:
+    void localDataChanged(size_t index, size_t count);
+};
+
+template <typename T>
+class ListCache : public ListCacheBase
+{
+public:
+    static constexpr ssize_t COUNT_UNINITIALIZED = -1;
+
+    ListCache(std::unique_ptr<ListCacheLoader<T>> loader, size_t chunkSize = 100)
+        : m_loader(std::move(loader))
+        , m_chunkSize(chunkSize) {}
+
+    /**
+     * Return the item at specified index
+     *
+     * This returns the local item (`nullptr` if not present), and does not
+     * retrieve anything from the loader.
+     */
+    const T *get(size_t index) const;
+
+
+    /**
+     * Return the number of items or `COUNT_UNINITIALIZED`
+     *
+     * This returns the local count, and does not retrieve anything from the
+     * loader.
+     */
+    ssize_t count() const;
+
+    /**
+     * Init the list size
+     *
+     * This method must be called at most once (the list size is not expected
+     * to change).
+     */
+    void initCount();
+
+    /**
+     * Request to load data so that the item as index will become available
+     */
+    void refer(size_t index);
+
+private:
+    std::unique_ptr<ListCacheLoader<T>> m_loader;
+    size_t m_chunkSize;
+
+    std::vector<T> m_list;
+    ssize_t m_total_count = COUNT_UNINITIALIZED;
+    size_t m_offset = 0;
+};
+
+template <typename T>
+const T *ListCache<T>::get(size_t index) const
+{
+    assert(m_total_count >= 0 && index < static_cast<size_t>(m_total_count));
+    if (index < m_offset || index >= m_offset + m_list.size())
+        return nullptr;
+
+    return &m_list[index - m_offset];
+}
+
+template <typename T>
+ssize_t ListCache<T>::count() const
+{
+    return m_total_count;
+}
+
+template <typename T>
+void ListCache<T>::initCount()
+{
+    assert(m_total_count == COUNT_UNINITIALIZED);
+    m_total_count = static_cast<ssize_t>(m_loader->count());
+}
+
+template <typename T>
+void ListCache<T>::refer(size_t index)
+{
+    if (m_total_count == -1 || index >= static_cast<size_t>(m_total_count))
+    {
+        /*
+         * The request is incompatible with the total count of the list.
+         *
+         * Either the count is not retrieved yet, or the content has changed in
+         * the loader source.
+         */
+        return;
+    }
+
+    /* index outside the known portion of the list */
+    if (index < m_offset || index >= m_offset + m_list.size())
+    {
+        /* FIXME bad heuristic if the interval of visible items crosses a cache
+         * page boundary */
+        m_offset = index - index % m_chunkSize;
+        size_t count = qMin(m_total_count - m_offset, m_chunkSize);
+        m_list = m_loader->load(m_offset, count);
+        if (m_list.size())
+            emit localDataChanged(m_offset, m_list.size());
+    }
+}
+
+#endif
-- 
2.29.2



More information about the vlc-devel mailing list