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