diff --git a/modules/gui/qt/medialibrary/mlbasemodel.cpp b/modules/gui/qt/medialibrary/mlbasemodel.cpp index 888ffa6ff1..b16386b1e2 100644 --- a/modules/gui/qt/medialibrary/mlbasemodel.cpp +++ b/modules/gui/qt/medialibrary/mlbasemodel.cpp @@ -48,7 +48,7 @@ void MLBaseModel::sortByColumn(QByteArray name, Qt::SortOrder order) { m_sort_desc = (order == Qt::SortOrder::DescendingOrder); m_sort = nameToCriteria(name); - clear(); + resetCache(); } //------------------------------------------------------------------------------------------------- @@ -169,7 +169,7 @@ QVariant MLBaseModel::data(const QModelIndex &index, int role) const void MLBaseModel::onResetRequested() { - clear(); + invalidateCache(); } void MLBaseModel::onLocalSizeAboutToBeChanged(size_t size) @@ -185,14 +185,6 @@ void MLBaseModel::onLocalSizeChanged(size_t size) emit countChanged(size); } -void MLBaseModel::onLocalDataChanged(size_t offset, size_t count) -{ - assert(count); - auto first = index(offset); - auto last = index(offset + count - 1); - emit dataChanged(first, last); -} - void MLBaseModel::onVlcMlEvent(const MLEvent &event) { switch(event.i_type) @@ -268,14 +260,14 @@ MLItemId MLBaseModel::parentId() const void MLBaseModel::setParentId(MLItemId parentId) { m_parent = parentId; - clear(); + invalidateCache(); emit parentIdChanged(); } void MLBaseModel::unsetParentId() { m_parent = MLItemId(); - clear(); + invalidateCache(); emit parentIdChanged(); } @@ -305,7 +297,7 @@ void MLBaseModel::setSearchPattern( const QString& pattern ) return; m_search_pattern = patternToApply; - clear(); + invalidateCache(); } Qt::SortOrder MLBaseModel::getSortOrder() const @@ -316,7 +308,7 @@ Qt::SortOrder MLBaseModel::getSortOrder() const void MLBaseModel::setSortOder(Qt::SortOrder order) { m_sort_desc = (order == Qt::SortOrder::DescendingOrder); - clear(); + resetCache(); emit sortOrderChanged(); } @@ -328,14 +320,14 @@ const QString MLBaseModel::getSortCriteria() const void MLBaseModel::setSortCriteria(const QString& criteria) { m_sort = nameToCriteria(criteria.toUtf8()); - clear(); + resetCache(); emit sortCriteriaChanged(); } void MLBaseModel::unsetSortCriteria() { m_sort = VLC_ML_SORTING_DEFAULT; - clear(); + resetCache(); emit sortCriteriaChanged(); } @@ -349,14 +341,6 @@ int MLBaseModel::rowCount(const QModelIndex &parent) const return m_cache->count(); } -void MLBaseModel::clear() -{ - beginResetModel(); - invalidateCache(); - endResetModel(); - emit countChanged( static_cast(0) ); -} - QVariant MLBaseModel::getIdForIndex(QVariant index) const { MLItem* obj = nullptr; @@ -417,26 +401,78 @@ unsigned MLBaseModel::getCount() const return static_cast(m_cache->count()); } + +void MLBaseModel::onCacheDataChanged(int first, int last) +{ + emit dataChanged(index(first), index(last)); +} + +void MLBaseModel::onCacheBeginInsertRows(int first, int last) +{ + emit beginInsertRows({}, first, last); +} + +void MLBaseModel::onCacheBeginRemoveRows(int first, int last) +{ + emit beginRemoveRows({}, first, last); +} + +void MLBaseModel::onCacheBeginMoveRows(int first, int last, int destination) +{ + emit beginMoveRows({}, first, last, {}, destination); +} + void MLBaseModel::validateCache() const { if (m_cache) return; + if (!m_mediaLib) + return; + auto loader = createLoader(); - m_cache.reset(new MLListCache(m_mediaLib, loader.release())); - connect(&*m_cache, &MLListCache::localSizeAboutToBeChanged, + m_cache = std::make_unique(m_mediaLib, std::move(loader)); + connect(m_cache.get(), &MLListCache::localSizeAboutToBeChanged, this, &MLBaseModel::onLocalSizeAboutToBeChanged); - connect(&*m_cache, &MLListCache::localSizeChanged, + connect(m_cache.get(), &MLListCache::localSizeChanged, this, &MLBaseModel::onLocalSizeChanged); - connect(&*m_cache, &MLListCache::localDataChanged, - this, &MLBaseModel::onLocalDataChanged); + + connect(m_cache.get(), &MLListCache::localDataChanged, + this, &MLBaseModel::onCacheDataChanged); + + connect(m_cache.get(), &MLListCache::beginInsertRows, + this, &MLBaseModel::onCacheBeginInsertRows); + connect(m_cache.get(), &MLListCache::endInsertRows, + this, &MLBaseModel::endInsertRows); + + connect(m_cache.get(), &MLListCache::beginRemoveRows, + this, &MLBaseModel::onCacheBeginRemoveRows); + connect(m_cache.get(), &MLListCache::endRemoveRows, + this, &MLBaseModel::endRemoveRows); + + connect(m_cache.get(), &MLListCache::endMoveRows, + this, &MLBaseModel::endMoveRows); + connect(m_cache.get(), &MLListCache::beginMoveRows, + this, &MLBaseModel::onCacheBeginMoveRows); m_cache->initCount(); } + +void MLBaseModel::resetCache() +{ + beginResetModel(); + m_cache.reset(); + endResetModel(); + validateCache(); +} + void MLBaseModel::invalidateCache() { - m_cache.reset(); + if (m_cache) + m_cache->invalidate(); + else + validateCache(); } //------------------------------------------------------------------------------------------------- @@ -445,9 +481,12 @@ MLItem *MLBaseModel::item(int signedidx) const { validateCache(); + if (!m_cache) + return nullptr; + ssize_t count = m_cache->count(); - if (count == COUNT_UNINITIALIZED || signedidx < 0 || signedidx >= count) + if (count == 0 || signedidx < 0 || signedidx >= count) return nullptr; unsigned int idx = static_cast(signedidx); @@ -468,6 +507,9 @@ MLItem *MLBaseModel::itemCache(int signedidx) const { unsigned int idx = static_cast(signedidx); + if (!m_cache) + return nullptr; + const std::unique_ptr *item = m_cache->get(idx); if (!item) @@ -514,13 +556,7 @@ void MLBaseModel::updateItemInCache(const MLItemId& mlid) if (!ctx.item) return; - int row = m_cache->updateItem(std::move(ctx.item)); - if (row != -1) - { - //notify every roles - emit dataChanged(index(row), index(row)); - } - //otherwise don't notify, it will be updated when the cache reload the corresponding segment + m_cache->updateItem(std::move(ctx.item)); }); } @@ -531,17 +567,7 @@ void MLBaseModel::deleteItemInCache(const MLItemId& mlid) emit resetRequested(); return; } - int row = m_cache->deleteItem(mlid); - if (row < 0) - { - // items isn't in cache, we don't know if it's before or after our cache, request a reset - emit resetRequested(); - } - else - { - beginRemoveRows({}, row, row); - endRemoveRows(); - } + m_cache->deleteItem(mlid); } //------------------------------------------------------------------------------------------------- diff --git a/modules/gui/qt/medialibrary/mlbasemodel.hpp b/modules/gui/qt/medialibrary/mlbasemodel.hpp index df0ae28784..5ca9540c48 100644 --- a/modules/gui/qt/medialibrary/mlbasemodel.hpp +++ b/modules/gui/qt/medialibrary/mlbasemodel.hpp @@ -91,13 +91,11 @@ protected slots: void onResetRequested(); void onLocalSizeAboutToBeChanged(size_t size); void onLocalSizeChanged(size_t size); - void onLocalDataChanged(size_t index, size_t count); private: static void onVlcMlEvent( void* data, const vlc_ml_event_t* event ); protected: - virtual void clear(); virtual vlc_ml_sorting_criteria_t roleToCriteria(int role) const = 0; static QString getFirstSymbol(QString str); virtual vlc_ml_sorting_criteria_t nameToCriteria(QByteArray) const { @@ -109,6 +107,7 @@ protected: } void validateCache() const; + void resetCache(); void invalidateCache(); MLItem *item(int signedidx) const; @@ -170,6 +169,12 @@ public: int rowCount(const QModelIndex &parent = {}) const override; virtual unsigned int getCount() const; +private: + void onCacheDataChanged(int first, int last); + void onCacheBeginInsertRows(int first, int last); + void onCacheBeginRemoveRows(int first, int last); + void onCacheBeginMoveRows(int first, int last, int destination); + protected: MLItemId m_parent; diff --git a/modules/gui/qt/medialibrary/mllistcache.cpp b/modules/gui/qt/medialibrary/mllistcache.cpp index 78c7a7ff09..7ff6930d9c 100644 --- a/modules/gui/qt/medialibrary/mllistcache.cpp +++ b/modules/gui/qt/medialibrary/mllistcache.cpp @@ -17,147 +17,403 @@ *****************************************************************************/ #include "mllistcache.hpp" +#include "vlc_diffutil.h" + +namespace { + +//callbacks for the diff algorithm to access the data + +uint32_t cacheDataLength(const void* data) +{ + auto list = static_cast*>(data); + assert(list); + return list->size(); +} + +bool cacheDataCompare(const void* dataOld, uint32_t oldIndex, const void* dataNew, uint32_t newIndex) +{ + auto listOld = static_cast*>(dataOld); + auto listNew = static_cast*>(dataNew); + assert(listOld); + assert(listNew); + assert(oldIndex < listOld->size()); + assert(newIndex < listNew->size()); + return listOld->at(oldIndex)->getId() == listNew->at(newIndex)->getId(); +} + +} + +MLListCache::MLListCache(MediaLib* medialib, std::unique_ptr>&& loader, size_t chunkSize) + : m_medialib(medialib) + , m_loader(loader.release()) + , m_chunkSize(chunkSize) +{ + assert(medialib); +} const MLListCache::ItemType* MLListCache::get(size_t index) const { - assert(m_total_count >= 0 && index < static_cast(m_total_count)); - if (index < m_offset || index >= m_offset + m_list.size()) + //the view may access the model while we're updating it + //everything before m_partialIndex is updated in the new model, + //everything after m_partialIndex is still valid in the old model + if (unlikely(m_oldData)) + { + if (m_cachedData) + { + if (index >= m_partialLoadedCount) + return nullptr; + else if (index >= m_partialIndex) + return &m_oldData->list.at(index - m_partialIndex + m_partialX); + else + return &m_cachedData->list.at(index); + } + else + { + if (index >= m_oldData->loadedCount) + return nullptr; + + return &m_oldData->list.at(index); + } + } + + if (!m_cachedData) return nullptr; - return &m_list[index - m_offset]; + if (index + 1 > m_cachedData->loadedCount) + return nullptr; + + return &m_cachedData->list.at(index); } const MLListCache::ItemType* MLListCache::find(const std::function &&f, int *index) const { - if (m_total_count <= 0) + + if (!m_cachedData || m_cachedData->totalCount == 0) return nullptr; - for (auto iter = std::begin(m_list); iter != std::end(m_list); ++iter) - { - if (f(*iter)) - { - if (index) - *index = m_offset + std::distance(std::begin(m_list), iter); + auto it = std::find_if(m_cachedData->list.cbegin(), m_cachedData->list.cend(), f); + if (it == m_cachedData->list.cend()) + return nullptr; - return &(*iter); - } - } + if (index) + *index = std::distance(m_cachedData->list.cbegin(), it); - return nullptr; + return &(*it); } int MLListCache::updateItem(std::unique_ptr&& newItem) { + //we can't update an item locally while the model has pending updates + //no worry, we'll receive the update once the actual model notifies us + if (m_oldData) + return -1; + MLItemId mlid = newItem->getId(); - auto it = std::find_if(m_list.begin(), m_list.end(), [mlid](const ItemType& item) { + //this may be inneficient to look at every items, maybe we can have a hashmap to access the items by id + auto it = std::find_if(m_cachedData->list.begin(), m_cachedData->list.end(), [mlid](const ItemType& item) { return (item->getId() == mlid); }); //item not found - if (it == m_list.end()) + if (it == m_cachedData->list.end()) return -1; - int pos = m_offset + std::distance(m_list.begin(), it); + int pos = std::distance(m_cachedData->list.begin(), it); *it = std::move(newItem); + emit localDataChanged(pos, pos); return pos; } int MLListCache::deleteItem(const MLItemId& mlid) { - auto it = std::find_if(m_list.begin(), m_list.end(), [mlid](const ItemType& item) { + //we can't update an item locally while the model has pending updates + //no worry, we'll receive the update once the actual model notifies us + if (m_oldData) + return -1; + + auto it = std::find_if(m_cachedData->list.begin(), m_cachedData->list.end(), [mlid](const ItemType& item) { return (item->getId() == mlid); }); + //item not found - if (it == m_list.end()) + if (it == m_cachedData->list.end()) return -1; - int pos = m_offset + std::distance(m_list.begin(), it); - m_list.erase(it, it); - m_total_count -= 1; + + int pos = std::distance(m_cachedData->list.begin(), it); + + emit beginRemoveRows(pos, pos); + m_cachedData->list.erase(it, it+1); + size_t delta = m_cachedData->loadedCount - m_cachedData->list.size(); + m_cachedData->loadedCount -= delta; + m_cachedData->totalCount -= delta; + emit endRemoveRows(); + emit localSizeChanged(m_cachedData->totalCount); + return pos; } ssize_t MLListCache::count() const { - return m_total_count; + if (!m_cachedData) + return -1; + return m_cachedData->totalCount; } void MLListCache::initCount() { - assert(!m_countRequested); - asyncCount(); + assert(!m_cachedData); + asyncCountAndLoad(); } void MLListCache::refer(size_t index) { - if (m_total_count == -1 || index >= static_cast(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; - } + //m_maxReferedIndex is in terms of number of item, not the index + index++; - /* index outside the known portion of the list */ - if (!m_lastRangeRequested.contains(index)) + if (!m_cachedData) + return; + + if (index > m_cachedData->totalCount) + return; + + /* index is already in the list */ + if (index <= m_cachedData->loadedCount) + return; + + if (index > m_maxReferedIndex) { - /* FIXME bad heuristic if the interval of visible items crosses a cache - * page boundary */ - size_t offset = index - index % m_chunkSize; - size_t count = qMin(m_total_count - offset, m_chunkSize); - asyncLoad(offset, count); + m_maxReferedIndex = index; + if (!m_appendTask && !m_countTask) + { + if (m_cachedData) + asyncFetchMore(); + else + asyncCountAndLoad(); + } } } -void MLListCache::asyncCount() +void MLListCache::invalidate() { - assert(!m_countTask); - - m_countRequested = true; - struct Ctx { - ssize_t totalCount; - }; - m_medialib->runOnMLThread(this, - //ML thread - [loader = m_loader](vlc_medialibrary_t* ml, Ctx& ctx) + if (m_cachedData) + { + if (m_cachedData && !m_oldData) { + m_oldData = std::move(m_cachedData); + m_partialX = 0; + } + else + m_cachedData.reset(); + } + + if (m_appendTask) + { + m_medialib->cancelMLTask(this, m_appendTask); + m_appendTask = 0; + } + + if (m_countTask) + { + m_needReload = true; + } + else + { + asyncCountAndLoad(); + } +} + +void MLListCache::partialUpdate() +{ + //compare the model the user have and the updated model + //and notify for changes + + vlc_diffutil_callback_t diffOp = { + cacheDataLength, + cacheDataLength, + cacheDataCompare + }; + + diffutil_snake_t* snake = vlc_diffutil_build_snake(&diffOp, &m_oldData->list, &m_cachedData->list); + vlc_diffutil_changelist_t* changes = vlc_diffutil_build_change_list( + snake, &diffOp, &m_oldData->list, &m_cachedData->list, + VLC_DIFFUTIL_RESULT_AGGREGATE); + + m_partialIndex = 0; + m_partialLoadedCount = m_oldData->loadedCount; + size_t partialTotalCount = m_oldData->totalCount; + for (size_t i = 0; i < changes->size; i++) + { + vlc_diffutil_change_t& op = changes->data[i]; + switch (op.type) + { + case VLC_DIFFUTIL_OP_IGNORE: + break; + case VLC_DIFFUTIL_OP_INSERT: + m_partialX = op.op.insert.x; + m_partialIndex = op.op.insert.index; + emit beginInsertRows(op.op.insert.index, op.op.insert.index + op.count - 1); + m_partialIndex += op.count; + m_partialLoadedCount += op.count; + partialTotalCount += op.count; + emit endInsertRows(); + emit localSizeChanged(partialTotalCount); + break; + case VLC_DIFFUTIL_OP_REMOVE: + m_partialX = op.op.remove.x; + m_partialIndex = op.op.remove.index + op.count - 1; + emit beginRemoveRows(op.op.remove.index, op.op.remove.index + op.count - 1); + m_partialLoadedCount -= op.count; + m_partialX += op.count; + m_partialIndex = op.op.remove.index + 1; + partialTotalCount -= op.count; + emit endRemoveRows(); + emit localSizeChanged(partialTotalCount); + break; + case VLC_DIFFUTIL_OP_MOVE: + emit beginMoveRows(op.op.move.from, op.op.move.from + op.count - 1, op.op.move.to); + //TODO + emit endMoveRows(); + break; + } + } + vlc_diffutil_free_change_list(changes); + vlc_diffutil_free_snake(snake); + + //ditch old model + m_oldData.reset(); + + //if we have change outside our cache + //just notify for addition/removal at a the end of the list + if (partialTotalCount != m_cachedData->totalCount) + { + if (partialTotalCount > m_cachedData->totalCount) + { + emit beginRemoveRows(m_cachedData->totalCount - 1, partialTotalCount - 1); + emit endRemoveRows(); + emit localSizeChanged(m_cachedData->totalCount); + } + else + { + emit beginInsertRows(partialTotalCount - 1, m_cachedData->totalCount - 1); + emit endInsertRows(); + emit localSizeChanged(m_cachedData->totalCount); + } + } +} + +void MLListCache::asyncCountAndLoad() +{ + if (m_countTask) + m_medialib->cancelMLTask(this, m_countTask); + + size_t count = std::max(m_maxReferedIndex, m_chunkSize); + + struct Ctx { + size_t totalCount; + std::vector list; + }; + + m_countTask = m_medialib->runOnMLThread(this, + //ML thread + [loader = m_loader, count = count](vlc_medialibrary_t* ml, Ctx& ctx) + { + ctx.list = loader->load(ml, 0, count); ctx.totalCount = loader->count(ml); }, //UI thread - [this](quint64, Ctx& ctx){ - m_total_count = ctx.totalCount; + [this](quint64 taskId, Ctx& ctx) + { + if (m_countTask != taskId) + return; + + //quite unlikley but model may change between count and load + if (unlikely(ctx.list.size() > ctx.totalCount)) + { + ctx.totalCount = ctx.list.size(); + m_needReload = true; + } + + m_cachedData = std::make_unique(std::move(ctx.list), ctx.totalCount); + + if (m_oldData) + { + partialUpdate(); + } + else + { + if (m_cachedData->totalCount > 0) + { + //no previous data, we insert everything + emit beginInsertRows(0, m_cachedData->totalCount - 1); + emit endInsertRows(); + emit localSizeChanged(m_cachedData->totalCount); + } + } + m_countTask = 0; - emit localSizeChanged(m_total_count); + if (m_needReload) + { + m_needReload = false; + m_oldData = std::move(m_cachedData); + m_partialX = 0; + asyncCountAndLoad(); + } + else if (m_maxReferedIndex < m_cachedData->loadedCount) + { + m_maxReferedIndex = m_cachedData->loadedCount; + } + else if (m_maxReferedIndex > m_cachedData->loadedCount + && m_maxReferedIndex <= m_cachedData->totalCount) + { + asyncFetchMore(); + } } ); } -void MLListCache::asyncLoad(size_t offset, size_t count) +void MLListCache::asyncFetchMore() { - if (m_loadTask) - m_medialib->cancelMLTask(this, m_loadTask); + if (m_maxReferedIndex <= m_cachedData->loadedCount) + return; + + assert(m_cachedData); + if (m_appendTask) + m_medialib->cancelMLTask(this, m_appendTask); + + m_maxReferedIndex = std::min(m_cachedData->totalCount, m_maxReferedIndex); + size_t count = ((m_maxReferedIndex - m_cachedData->loadedCount) / m_chunkSize + 1 ) * m_chunkSize; - m_lastRangeRequested = { offset, count }; struct Ctx { std::vector list; }; - m_loadTask = m_medialib->runOnMLThread(this, + m_appendTask = m_medialib->runOnMLThread(this, //ML thread - [loader = m_loader, offset, count] + [loader = m_loader, offset = m_cachedData->loadedCount, count] (vlc_medialibrary_t* ml, Ctx& ctx) { ctx.list = loader->load(ml, offset, count); }, //UI thread - [this, offset](quint64, Ctx& ctx) + [this](quint64 taskId, Ctx& ctx) { - m_loadTask = 0; + if (taskId != m_appendTask) + return; - m_offset = offset; - m_list = std::move(ctx.list); - if (m_list.size()) - emit localDataChanged(offset, m_list.size()); + assert(m_cachedData); + + int updatedCount = ctx.list.size(); + if (updatedCount >= 0) + { + int updatedOffset = m_cachedData->loadedCount; + std::move(ctx.list.begin(), ctx.list.end(), std::back_inserter(m_cachedData->list)); + m_cachedData->loadedCount += updatedCount; + emit localDataChanged(updatedOffset, updatedOffset + updatedCount - 1); + } + + m_appendTask = 0; + if (m_maxReferedIndex > m_cachedData->loadedCount) + { + asyncFetchMore(); + } } ); } diff --git a/modules/gui/qt/medialibrary/mllistcache.hpp b/modules/gui/qt/medialibrary/mllistcache.hpp index 96e23a2286..d6b5e3c276 100644 --- a/modules/gui/qt/medialibrary/mllistcache.hpp +++ b/modules/gui/qt/medialibrary/mllistcache.hpp @@ -72,13 +72,44 @@ struct MLRange { } - bool isEmpty() { + MLRange(const MLRange& other) + : offset(other.offset) + , count(other.count) + {} + + MLRange& operator=(const MLRange& other) + { + offset = other.offset; + count = other.count; + return *this; + } + + bool isEmpty() const { return count == 0; } - bool contains(size_t index) { + bool contains(size_t index) const { return index >= offset && index < offset + count; } + + void reset() + { + offset = 0; + count = 0; + } + + ///returns the overlapping range of the current range and @a other + MLRange overlap(const MLRange& other) const + { + if (isEmpty() || other.isEmpty()) + return MLRange{}; + if (contains(other.offset)) + return MLRange(other.offset, (offset + count) - other.offset); + else if (other.contains(offset)) + return MLRange(offset, (other.offset + other.count) - offset); + else + return MLRange{}; + } }; class MLListCache : public QObject @@ -87,14 +118,12 @@ class MLListCache : public QObject public: typedef std::unique_ptr ItemType; + public: static constexpr ssize_t COUNT_UNINITIALIZED = -1; - MLListCache(MediaLib* medialib, ListCacheLoader *loader, - size_t chunkSize = 100) - : m_medialib(medialib) - , m_loader(loader) - , m_chunkSize(chunkSize) {} + MLListCache(MediaLib* medialib, std::unique_ptr>&& loader, + size_t chunkSize = 100); /** * Return the item at specified index @@ -147,16 +176,30 @@ public: */ void refer(size_t index); -signals: - /* useful for signaling QAbstractItemModel::modelAboutToBeReset() */ - void localSizeAboutToBeChanged(size_t size); + /* + * reload + */ + void invalidate(); +signals: + void localSizeAboutToBeChanged(size_t size); void localSizeChanged(size_t size); - void localDataChanged(size_t index, size_t count); + + void localDataChanged(int sourceFirst, int sourceLast); + + void beginInsertRows(int sourceFirst, int sourceLast); + void endInsertRows(); + + void beginRemoveRows(int sourceFirst, int sourceLast); + void endRemoveRows(); + + void beginMoveRows(int sourceFirst, int sourceLast, int destination); + void endMoveRows(); private: - void asyncLoad(size_t offset, size_t count); - void asyncCount(); + void asyncFetchMore(); + void asyncCountAndLoad(); + void partialUpdate(); MediaLib* m_medialib = nullptr; @@ -165,15 +208,37 @@ private: QSharedPointer> m_loader; size_t m_chunkSize; - std::vector m_list; - ssize_t m_total_count = COUNT_UNINITIALIZED; - size_t m_offset = 0; + //highest index requested by the view (1 based, 0 is nothing referenced) + size_t m_maxReferedIndex = 0; - bool m_countRequested = false; - MLRange m_lastRangeRequested; + bool m_needReload = false; - uint64_t m_loadTask = 0; + uint64_t m_appendTask = 0; uint64_t m_countTask = 0; + + struct CacheData { + explicit CacheData(std::vector&& list_, size_t totalCount_) + : list(std::move(list_)) + , totalCount(totalCount_) + { + loadedCount = list.size(); + } + + std::vector list; + size_t totalCount = 0; + size_t loadedCount = 0; + }; + + std::unique_ptr m_cachedData; + std::unique_ptr m_oldData; + + MLRange m_rangeRequested; + + + //access the list while it's being updated + size_t m_partialIndex = 0; + size_t m_partialX = 0; + size_t m_partialLoadedCount = 0; }; #endif