1// Copyright (C) 2017 Ford Motor Company
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
6#define QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include "qremoteobjectabstractitemmodeltypes_p.h"
20#include "qremoteobjectabstractitemmodelreplica.h"
21#include "qremoteobjectreplica.h"
22#include "qremoteobjectpendingcall.h"
23#include <list>
24#include <unordered_map>
25#include <unordered_set>
26
27QT_BEGIN_NAMESPACE
28
29namespace {
30 const int DefaultNodesCacheSize = 1000;
31}
32
33struct CacheEntry
34{
35 QHash<int, QVariant> data;
36 Qt::ItemFlags flags;
37
38 explicit CacheEntry()
39 : flags(Qt::NoItemFlags)
40 {}
41};
42
43using CachedRowEntry = QList<CacheEntry>;
44
45template <class Key, class Value>
46struct LRUCache
47{
48 typedef std::pair<Key, Value*> Pair;
49 std::list<Pair> cachedItems;
50 typedef typename std::list<Pair>::iterator CacheIterator;
51 std::unordered_map<Key, CacheIterator> cachedItemsMap;
52 size_t cacheSize;
53
54 explicit LRUCache()
55 {
56 bool ok;
57 cacheSize = qEnvironmentVariableIntValue(varName: "QTRO_NODES_CACHE_SIZE" , ok: &ok);
58 if (!ok)
59 cacheSize = DefaultNodesCacheSize;
60 }
61
62 ~LRUCache()
63 {
64 clear();
65 }
66
67 inline void cleanCache()
68 {
69 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
70
71 auto it = cachedItems.rbegin();
72 while (cachedItemsMap.size() > cacheSize) {
73 // Do not trash elements with children
74 // Workaround QTreeView bugs which caches the children indexes for very long time
75 while (it->second->hasChildren && it != cachedItems.rend())
76 ++it;
77
78 if (it == cachedItems.rend())
79 break;
80
81 cachedItemsMap.erase(it->first);
82 delete it->second;
83 cachedItems.erase((++it).base());
84 }
85 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
86 }
87
88 void setCacheSize(size_t rootCacheSize)
89 {
90 cacheSize = rootCacheSize;
91 cleanCache();
92 cachedItemsMap.reserve(rootCacheSize);
93 }
94
95 void changeKeys(Key key, Key delta) {
96 std::vector<std::pair<Key, CacheIterator>> changed;
97 auto it = cachedItemsMap.begin();
98 while (it != cachedItemsMap.end()) {
99 if (it->first >= key) {
100 changed.emplace_back(it->first + delta, it->second);
101 it->second->first += delta;
102 it = cachedItemsMap.erase(it);
103 } else {
104 ++it;
105 }
106 }
107 for (auto pair : changed)
108 cachedItemsMap[pair.first] = pair.second;
109 }
110
111 void insert(Key key, Value *value)
112 {
113 changeKeys(key, delta: 1);
114 ensure(key, value);
115 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
116 }
117
118 void ensure(Key key, Value *value)
119 {
120 cachedItems.emplace_front(key, value);
121 cachedItemsMap[key] = cachedItems.begin();
122 cleanCache();
123 }
124
125 void remove(Key key)
126 {
127 auto it = cachedItemsMap.find(key);
128 if (it != cachedItemsMap.end()) {
129 delete it->second->second;
130 cachedItems.erase(it->second);
131 cachedItemsMap.erase(it);
132 }
133 changeKeys(key, delta: -1);
134 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
135 }
136
137 Value *get(Key key)
138 {
139 auto it = cachedItemsMap.find(key);
140 if (it == cachedItemsMap.end())
141 return nullptr;
142
143 // Move the accessed item to front
144 cachedItems.splice(cachedItems.begin(), cachedItems, it->second);
145 Q_ASSERT(it->second->first == key);
146 return it->second->second;
147 }
148
149 Key find(Value *val)
150 {
151 for (auto it = cachedItemsMap.begin(); it != cachedItemsMap.end(); ++it) {
152 if (it->second->second == val)
153 return it->first;
154 }
155 Q_ASSERT_X(false, __FUNCTION__, "Value not found");
156 return Key{};
157 }
158
159 bool exists(Value *val)
160 {
161 for (const auto &pair : cachedItems)
162 if (pair.second == val)
163 return true;
164
165 return false;
166 }
167
168 bool exists(Key key)
169 {
170 return cachedItemsMap.find(key) != cachedItemsMap.end();
171 }
172
173 size_t size()
174 {
175 return cachedItemsMap.size();
176 }
177
178 void clear()
179 {
180 for (const auto &pair : cachedItems)
181 delete pair.second;
182 cachedItems.clear();
183 cachedItemsMap.clear();
184 }
185};
186
187class QAbstractItemModelReplicaImplementation;
188struct CacheData
189{
190 QAbstractItemModelReplicaImplementation *replicaModel;
191 CacheData *parent;
192 CachedRowEntry cachedRowEntry;
193
194 bool hasChildren;
195 LRUCache<int, CacheData> children;
196 int columnCount;
197 int rowCount;
198
199 explicit CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem = nullptr);
200
201 ~CacheData();
202
203 void ensureChildren(int start, int end)
204 {
205 for (int i = start; i <= end; ++i)
206 if (!children.exists(key: i))
207 children.ensure(key: i, value: new CacheData(replicaModel, this));
208 }
209
210 void insertChildren(int start, int end) {
211 Q_ASSERT_X(start >= 0 && start <= end, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2")).arg(start).arg(end)));
212 for (int i = start; i <= end; ++i) {
213 auto cacheData = new CacheData(replicaModel, this);
214 cacheData->columnCount = columnCount;
215 children.insert(key: i, value: cacheData);
216 ++rowCount;
217 }
218 if (rowCount)
219 hasChildren = true;
220 }
221 void removeChildren(int start, int end) {
222 Q_ASSERT_X(start >= 0 && start <= end && end < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2 < %3")).arg(start).arg(end).arg(rowCount)));
223 for (int i = end; i >= start; --i) {
224 children.remove(key: i);
225 --rowCount;
226 }
227 hasChildren = rowCount;
228 }
229 void clear() {
230 cachedRowEntry.clear();
231 children.clear();
232 hasChildren = false;
233 columnCount = 0;
234 rowCount = 0;
235 }
236};
237
238struct RequestedData
239{
240 QtPrivate::IndexList start;
241 QtPrivate::IndexList end;
242 QList<int> roles;
243};
244
245struct RequestedHeaderData
246{
247 int role;
248 int section;
249 Qt::Orientation orientation;
250};
251
252class SizeWatcher : public QRemoteObjectPendingCallWatcher
253{
254 Q_OBJECT
255public:
256 SizeWatcher(QtPrivate::IndexList _parentList, const QRemoteObjectPendingReply<QSize> &reply)
257 : QRemoteObjectPendingCallWatcher(reply),
258 parentList(_parentList) {}
259 QtPrivate::IndexList parentList;
260};
261
262class RowWatcher : public QRemoteObjectPendingCallWatcher
263{
264 Q_OBJECT
265public:
266 RowWatcher(QtPrivate::IndexList _start, QtPrivate::IndexList _end, QList<int> _roles, const QRemoteObjectPendingReply<QtPrivate::DataEntries> &reply)
267 : QRemoteObjectPendingCallWatcher(reply),
268 start(_start),
269 end(_end),
270 roles(_roles) {}
271 QtPrivate::IndexList start, end;
272 QList<int> roles;
273};
274
275class HeaderWatcher : public QRemoteObjectPendingCallWatcher
276{
277 Q_OBJECT
278public:
279 HeaderWatcher(QList<Qt::Orientation> _orientations, QList<int> _sections, QList<int> _roles, const QRemoteObjectPendingReply<QVariantList> &reply)
280 : QRemoteObjectPendingCallWatcher(reply),
281 orientations(_orientations),
282 sections(_sections),
283 roles(_roles) {}
284 QList<Qt::Orientation> orientations;
285 QList<int> sections, roles;
286};
287
288class QAbstractItemModelReplicaImplementation : public QRemoteObjectReplica
289{
290 Q_OBJECT
291 //TODO Use an input name for the model on the Replica side
292 Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "ServerModelAdapter")
293 Q_PROPERTY(QList<int> availableRoles READ availableRoles NOTIFY availableRolesChanged)
294 Q_PROPERTY(QIntHash roleNames READ roleNames)
295public:
296 QAbstractItemModelReplicaImplementation();
297 QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name);
298 ~QAbstractItemModelReplicaImplementation() override;
299 void initialize() override;
300 static void registerMetatypes();
301
302 inline const QList<int> &availableRoles() const
303 {
304 if (m_availableRoles.isEmpty())
305 m_availableRoles = propAsVariant(i: 0).value<QList<int>>();
306 return m_availableRoles;
307 }
308
309 QHash<int, QByteArray> roleNames() const
310 {
311 QIntHash roles = propAsVariant(i: 1).value<QIntHash>();
312 return roles;
313 }
314
315 void setModel(QAbstractItemModelReplica *model);
316 bool clearCache(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
317
318Q_SIGNALS:
319 void availableRolesChanged();
320 void dataChanged(QtPrivate::IndexList topLeft, QtPrivate::IndexList bottomRight, QList<int> roles);
321 void rowsInserted(QtPrivate::IndexList parent, int first, int last);
322 void rowsRemoved(QtPrivate::IndexList parent, int first, int last);
323 void rowsMoved(QtPrivate::IndexList parent, int start, int end, QtPrivate::IndexList destination, int row);
324 void currentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
325 void modelReset();
326 void headerDataChanged(Qt::Orientation,int,int);
327 void columnsInserted(QtPrivate::IndexList parent, int first, int last);
328 void layoutChanged(QtPrivate::IndexList parents, QAbstractItemModel::LayoutChangeHint hint);
329public Q_SLOTS:
330 QRemoteObjectPendingReply<QSize> replicaSizeRequest(QtPrivate::IndexList parentList)
331 {
332 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSizeRequest(QtPrivate::IndexList)");
333 QVariantList __repc_args;
334 __repc_args << QVariant::fromValue(value: parentList);
335 return QRemoteObjectPendingReply<QSize>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
336 }
337 QRemoteObjectPendingReply<QtPrivate::DataEntries> replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles)
338 {
339 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaRowRequest(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)");
340 QVariantList __repc_args;
341 __repc_args << QVariant::fromValue(value: start) << QVariant::fromValue(value: end) << QVariant::fromValue(value: roles);
342 return QRemoteObjectPendingReply<QtPrivate::DataEntries>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
343 }
344 QRemoteObjectPendingReply<QVariantList> replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles)
345 {
346 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaHeaderRequest(QList<Qt::Orientation>,QList<int>,QList<int>)");
347 QVariantList __repc_args;
348 __repc_args << QVariant::fromValue(value: orientations) << QVariant::fromValue(value: sections) << QVariant::fromValue(value: roles);
349 return QRemoteObjectPendingReply<QVariantList>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
350 }
351 void replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command)
352 {
353 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSetCurrentIndex(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)");
354 QVariantList __repc_args;
355 __repc_args << QVariant::fromValue(value: index) << QVariant::fromValue(value: command);
356 send(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args);
357 }
358 void replicaSetData(QtPrivate::IndexList index, const QVariant &value, int role)
359 {
360 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSetData(QtPrivate::IndexList,QVariant,int)");
361 QVariantList __repc_args;
362 __repc_args << QVariant::fromValue(value: index) << QVariant::fromValue(value) << QVariant::fromValue(value: role);
363 send(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args);
364 }
365 QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries> replicaCacheRequest(size_t size, QList<int> roles)
366 {
367 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaCacheRequest(size_t,QList<int>)");
368 QVariantList __repc_args;
369 __repc_args << QVariant::fromValue(value: size) << QVariant::fromValue(value: roles);
370 return QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
371 }
372 void onHeaderDataChanged(Qt::Orientation orientation, int first, int last);
373 void onDataChanged(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
374 void onRowsInserted(const QtPrivate::IndexList &parent, int start, int end);
375 void onRowsRemoved(const QtPrivate::IndexList &parent, int start, int end);
376 void onColumnsInserted(const QtPrivate::IndexList &parent, int start, int end);
377 void onRowsMoved(QtPrivate::IndexList srcParent, int srcRow, int count, QtPrivate::IndexList destParent, int destRow);
378 void onCurrentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
379 void onModelReset();
380 void requestedData(QRemoteObjectPendingCallWatcher *);
381 void requestedHeaderData(QRemoteObjectPendingCallWatcher *);
382 void init();
383 void fetchPendingData();
384 void fetchPendingHeaderData();
385 void handleInitDone(QRemoteObjectPendingCallWatcher *watcher);
386 void handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher);
387 void handleSizeDone(QRemoteObjectPendingCallWatcher *watcher);
388 void onReplicaCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
389 void fillCache(const QtPrivate::IndexValuePair &pair,const QList<int> &roles);
390 void onLayoutChanged(const QtPrivate::IndexList &parents, QAbstractItemModel::LayoutChangeHint hint);
391public:
392 QScopedPointer<QItemSelectionModel> m_selectionModel;
393 QList<CacheEntry> m_headerData[2];
394
395 CacheData m_rootItem;
396 inline CacheData* cacheData(const QModelIndex &index) const {
397 if (!index.isValid())
398 return const_cast<CacheData*>(&m_rootItem);
399 if (index.internalPointer()) {
400 auto parent = static_cast<CacheData*>(index.internalPointer());
401 if (m_activeParents.find(x: parent) != m_activeParents.end())
402 return parent->children.get(key: index.row());
403 }
404 return nullptr;
405 }
406 inline CacheData* cacheData(const QtPrivate::IndexList &index) const {
407 return cacheData(index: toQModelIndex(list: index, model: q));
408 }
409 inline CacheData* createCacheData(const QtPrivate::IndexList &index) const {
410 bool ok = false;
411 auto modelIndex = toQModelIndex(list: index, model: q, ok: &ok);
412 if (!ok)
413 return nullptr;
414 cacheData(index: modelIndex.parent())->ensureChildren(start: modelIndex.row() , end: modelIndex.row());
415 return cacheData(index: modelIndex);
416 }
417 inline CacheEntry* cacheEntry(const QModelIndex &index) const {
418 auto data = cacheData(index);
419 if (!data || index.column() < 0 || index.column() >= data->cachedRowEntry.size())
420 return nullptr;
421 CacheEntry &entry = data->cachedRowEntry[index.column()];
422 return &entry;
423 }
424 inline CacheEntry* cacheEntry(const QtPrivate::IndexList &index) const {
425 return cacheEntry(index: toQModelIndex(list: index, model: q));
426 }
427
428 QRemoteObjectPendingCallWatcher *doModelReset();
429 void initializeModelConnections();
430
431 bool m_initDone = false;
432 QList<RequestedData> m_requestedData;
433 QList<RequestedHeaderData> m_requestedHeaderData;
434 QList<QRemoteObjectPendingCallWatcher*> m_pendingRequests;
435 QAbstractItemModelReplica *q;
436 mutable QList<int> m_availableRoles;
437 std::unordered_set<CacheData*> m_activeParents;
438 QtRemoteObjects::InitialAction m_initialAction;
439 QList<int> m_initialFetchRolesHint;
440};
441
442QT_END_NAMESPACE
443
444#endif // QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
445

source code of qtremoteobjects/src/remoteobjects/qremoteobjectabstractitemmodelreplica_p.h