• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
18 
19 #include <cstdint>
20 #include <functional>
21 #include <map>
22 #include <optional>
23 #include <set>
24 #include <string>
25 #include <unordered_map>
26 #include <unordered_set>
27 
28 #include "base/memory/referenced.h"
29 #include "core/components_ng/base/ui_node.h"
30 
31 namespace OHOS::Ace::NG {
32 
33 // custom sorting for std::set only works with struct
34 // with operator() inside
35 class RepeatVirtualScrollCaches;
36 struct CacheItem {
37     bool isValid = false;
38     RefPtr<UINode> item;
39 };
40 
41 class RepeatVirtualScrollCaches {
42 public:
43     RepeatVirtualScrollCaches(const std::map<std::string, std::pair<bool, uint32_t>>& cacheCountL24ttype,
44         const std::function<void(uint32_t)>& onCreateNode,
45         const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
46         const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
47         const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
48         bool reusable = true);
49 
50     /** scenario:
51      *         Repeat gets updated due to data change.
52      *         1. TS calls RepeatVirtualScrollNode,
53      *            then calls this function.
54      * 2. RepeatVirtualScrollNode requests layout to rebuild the UI
55      * 3. layout sends RepeatVirtualScrollNode::GetFrameChild calls
56      * 4. how to service GetFrameChild  call:
57      *   - first check for L1 keys (same template type) if any can be updated.
58      *     These UINodes remain in the render tree.
59      *   - if no L1 item, the look for L2 keys (same template type)
60      */
61     void InvalidateKeyAndTTypeCaches();
62 
63     /**
64      * scenario: scroll, try to update an existing UINode
65      *
66      * find an key / UINode in L2 and update the UINode to
67      *   render the data source item 'forIndex'.
68      */
69     RefPtr<UINode> UpdateFromL2(uint32_t forIndex);
70 
71     void UpdateSameKeyItem(const std::string& key, uint32_t index);
72 
73     /**
74      * request TS to create a new node for given index / key/
75      */
76     RefPtr<UINode> CreateNewNode(uint32_t forIndex);
77 
78     // iterate over L1 keys, not allowed to modify L1
79     void ForEachL1IndexUINode(std::map<int32_t, RefPtr<UINode>>& children);
80 
81     void RecycleItemsByIndex(int32_t index);
82 
83     /**
84      * for given index get key
85      * fetch from TS if not in cache (if allowFetch == true)
86      * return false if index out of range
87      */
88     std::optional<std::string> GetKey4Index(uint32_t index, bool allowFetch);
89 
90     /**
91      * iterate over all entries of L1 and call function for each entry
92      * if function returns true, entry is added to rebuild L1, otherwise it is moved to L2
93      * cbFunc return true, [index, key] pair stays in L1 (index remains unchanged)
94      * cbFunc returns false, enqueue key in L2
95      */
96     bool RebuildL1(const std::function<bool(int32_t index, const RefPtr<UINode>& node)>& cbFunc);
97 
98     /**
99      * dito with only key as cb function parameter
100      */
101     bool RebuildL1WithKey(const std::function<bool(const std::string& key)>& cbFunc);
102 
103     /*
104         drop L1 entry with given index from L1
105         keep it in L2
106         return the affected UINode
107         caller responsibility to detach this UINode from the UI tree!
108     */
109     RefPtr<UINode> DropFromL1(const std::string& key);
110 
111     int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& frameNode) const;
112 
113     /**
114      * scenario: in idle process , following GetChildren()
115      *
116      * enforce L2 cacheCount for each ttype
117      * by deleting UINodes, delete their entry from
118      * node4key4ttype_ and node4key_
119      * any other processing steps needed before UINode
120      * tree can be deleted
121      */
122     bool Purge();
123 
124     /**
125      * return the cached UINode for given index
126      *
127      * resolve index -> key -> UINode
128      *
129      */
130     RefPtr<UINode> GetCachedNode4Index(uint32_t forIndex);
131 
132     void AddKeyToL1(const std::string& key, bool shouldTriggerReuse = true);
133 
134     void AddKeyToL1WithNodeUpdate(const std::string& key, uint32_t index, bool shouldTriggerRecycle);
135 
136     void RemoveKeyFromL1(const std::string& key, bool shouldTriggerRecycle = true);
137 
138     bool CheckTTypeChanged(uint32_t index);
139 
IsInL1Cache(const std::string & key)140     bool IsInL1Cache(const std::string& key) const
141     {
142         return activeNodeKeysInL1_.find(key) != activeNodeKeysInL1_.end();
143     }
144 
GetAllNodes()145     const std::unordered_map<std::string, CacheItem>& GetAllNodes() const
146     {
147         return node4key_;
148     }
149 
150     /**
151      * memorize last active range(s)
152      */
153     void SetLastActiveRange(uint32_t from, uint32_t to);
GetLastActiveRange()154     std::pair<uint32_t, uint32_t> GetLastActiveRange() { return lastActiveRanges_[0]; };
155 
156     // formatting internal structures to string for debug output
157     // and possibly in modified form for DFX in the future
158     std::string DumpL1() const;
159     std::string DumpL2() const;
160     std::string DumpKey4Index() const;
161     std::string DumpTType4Index() const;
162     std::string DumpUINode4Key4TType() const;
163     std::string DumpUINode4Key() const;
164 
165     std::string DumpUINodeWithKey(const std::string& key) const;
166     std::string DumpUINode(const RefPtr<UINode>& node) const;
167 
168 private:
169     /**
170      * intended scenario: scroll
171      * servicing GetFrameChild, search for key that can be updated.
172      *
173      * return a key whose UINode can be updated
174      * the key must not be in L1, i.e. activeNodeKeysInL1_
175      * the given ttype must match the template type the UINode for this key
176      * has been rendered for (this info is available from node4key4ttype_)
177      */
178     std::optional<std::string> GetL2KeyToUpdate(const std::optional<std::string>& ttype) const;
179 
180     /**
181      * scenario: UI rebuild following key invalidation by TS side
182      * L1 includes keys that are no longer used, the linked UINodes
183      * should be updated.
184      *
185      * This function checks all L1 keys (of active UINodes) if the key
186      * can still be found from
187      * (previously updated following invalidation) key -> index map and
188      *
189      */
190     std::optional<std::string> GetL1KeyToUpdate(const std::string& ttype) const;
191 
192     /**
193      * scenario: UINode of fromKey has been updated to render data for 'forKey'
194      *     the template type (ttype) remains unchanged
195      *     update node4key4ttype_ and node4key_ entries to use new key point to same UINode
196      */
197     RefPtr<UINode> UINodeHasBeenUpdated(
198         const std::string& ttype, const std::string& fromKey, const std::string& forKey);
199 
200     /** scenario: keys cache has been updated
201      *
202      * find which keys in key -> UINode map are no longer used
203      * returned set entries are pairs:
204      *   pair.first: is this key a L1 item,
205      *   pair.second: key
206      */
207     void FindUnusedKeys(std::set<std::pair<bool, std::string>>& result) const;
208 
209     /**
210      * given key return the index position (reverse lookup)
211      * invalidated keys (after Repeat rerender/ data change)
212      * are keys for which no index exists anymore,
213      * method returns int max value for these.
214      * int max value causes that distance from active range is max
215      * these keys will be selected for update first.
216      */
217     uint32_t GetIndex4Key(const std::string& key) const;
218 
219     /**
220      * find UINode for given key, irrespective of ttype in ttype4index_
221      */
222     std::optional<std::string> GetTType4Index(uint32_t index);
223 
224     /** find in node4key_ */
225     std::optional<CacheItem> GetCachedNode4Key(const std::optional<std::string>& key);
226 
227     /**
228      *  if key and ttype given, search for UINode of given key and ttype
229      * in caches, i.e. in node4key4ttype
230     * return nullptr in all other cases
231     */
232     RefPtr<UINode> GetCachedNode4Key4Ttype(
233         const std::optional<std::string>& key, const std::optional<std::string>& ttype);
234 
235     /**
236      *  for given index return distance from active range,
237      *  or 0 if within active range
238      *  distance is int max for invalidated keys
239      */
240     uint32_t GetDistanceFromRange(uint32_t index) const;
241     /**
242      * scenario: find L1 key that should be updated
243      * choose the key whose index is the furthest away from active range
244      * given two keys compare their distFromRange
245      */
246     bool CompareKeyByIndexDistance(const std::string& key1, const std::string& key2) const;
247 
248     std::set<std::string> GetL2KeysForTType(
249         const std::unordered_map<std::string, RefPtr<UINode>>& uiNode4Key) const;
250 
251     /**
252      * does given range overlap the last active range?
253      */
254     bool HasOverlapWithLastActiveRange(uint32_t from, uint32_t to);
255 
256     /**
257      * get more index -> key and index -> ttype from TS side
258      * may request additional keys if allowFetchMore is true
259      */
260     bool FetchMoreKeysTTypes(uint32_t from, uint32_t to, bool allowFetchMore = true);
261 
262     // Map ttype -> cacheSize. Each ttype incl default has own L2 size
263     std::map<std::string, std::pair<bool, uint32_t>> cacheCountL24ttype_;
264 
265     // request TS to create new sub-tree for given index or update existing
266     // update subtree cached for (old) index
267     // API might need to change to tell which old item to update
268     std::function<void(uint32_t)> onCreateNode_;
269     std::function<void(const std::string&, uint32_t)> onUpdateNode_;
270 
271     // get index -> key for given range
272     // resulting list starts with 'from' but might end before 'to' if Array shorter
273     std::function<std::list<std::string>(uint32_t, uint32_t)> onGetTypes4Range_;
274 
275     // get index -> ttype for given range
276     // resulting list starts with 'from' but might end before 'to' if Array shorter
277     std::function<std::list<std::string>(uint32_t, uint32_t)> onGetKeys4Range_;
278 
279     // memorize active ranges of past 2 (last, prev)
280     // from SetActiveChildRange calls and use to calc scroll direction
281     // active range:= visible range + pre-render items above and below
282     // number of pre-render items defined by Gird/List.cacheCount and informed
283     // as cacheStart and cacheEnd in SetActiveChildRange
284     std::pair<uint32_t, uint32_t> lastActiveRanges_[2] = { { 0, 0 }, { 0, 0 } };
285 
286     // keys of active nodes, UINodes must be on the UI tree,
287     // this list is also known as L1
288     // all keys not in this set are in "L2"
289     std::unordered_set<std::string> activeNodeKeysInL1_;
290 
291     // L1
292     // index -> key and reverse
293     // lazy request from TS side can be invalidated
294     std::unordered_map<uint32_t, std::string> key4index_;
295     std::unordered_map<std::string, uint32_t> index4Key_;
296 
297     // index -> ttype
298     // lazy request from TS side can be invalidated
299     std::unordered_map<uint32_t, std::string> ttype4index_;
300     std::unordered_map<std::string, uint32_t> index4ttype_;
301 
302     // Map ttype -> Map key -> UINode
303     std::unordered_map<std::string, std::unordered_map<std::string, RefPtr<UINode>>> node4key4ttype_;
304 
305     // Map Map key -> UINode
306     std::unordered_map<std::string, CacheItem> node4key_;
307 
308     // for tracking reused/recycled nodes
309     std::unordered_set<int32_t> reusedNodeIds_;
310 
311     // used to record whether a PostIdleTask is requeired after RebuildL1WithKey
312     bool isModified_ = false;
313 
314     // reuse node in L2 cache or not
315     bool reusable_ = true;
316 }; // class NodeCache
317 
318 } // namespace OHOS::Ace::NG
319 
320 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
321