1 /*
<lambda>null2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.foundation.lazy.layout
18 
19 import androidx.collection.MutableObjectIntMap
20 import androidx.collection.ObjectIntMap
21 import androidx.collection.emptyObjectIntMap
22 import androidx.compose.foundation.internal.checkPrecondition
23 
24 /**
25  * A key-index mapping used inside the [LazyLayoutItemProvider]. It might not contain all items in
26  * the lazy layout as optimization, but it must cover items the provider is requesting during layout
27  * pass. See [NearestRangeKeyIndexMap] as sample implementation that samples items near current
28  * viewport.
29  */
30 internal interface LazyLayoutKeyIndexMap {
31     /** @return current index for given [key] or `-1` if not found. */
32     fun getIndex(key: Any): Int
33 
34     /** @return key for a given [index] if it is known, or null otherwise. */
35     fun getKey(index: Int): Any?
36 
37     /** Empty map implementation, always returning `-1` for any key. */
38     companion object Empty : LazyLayoutKeyIndexMap {
39         @Suppress("AutoBoxing") override fun getIndex(key: Any): Int = -1
40 
41         override fun getKey(index: Int) = null
42     }
43 }
44 
45 /**
46  * Implementation of [LazyLayoutKeyIndexMap] indexing over given [IntRange] of items. Items outside
47  * of given range are considered unknown, with null returned as the index.
48  */
49 internal class NearestRangeKeyIndexMap(
50     nearestRange: IntRange,
51     intervalContent: LazyLayoutIntervalContent<*>
52 ) : LazyLayoutKeyIndexMap {
53     private val map: ObjectIntMap<Any>
54     private val keys: Array<Any?>
55     private val keysStartIndex: Int
56 
57     init {
58         // Traverses the interval [list] in order to create a mapping from the key to the index for
59         // all the indexes in the passed [range].
60         val list = intervalContent.intervals
61         val first = nearestRange.first
<lambda>null62         checkPrecondition(first >= 0) { "negative nearestRange.first" }
63         val last = minOf(nearestRange.last, list.size - 1)
64         if (last < first) {
65             map = emptyObjectIntMap()
66             keys = emptyArray()
67             keysStartIndex = 0
68         } else {
69             val size = last - first + 1
70             keys = arrayOfNulls<Any?>(size)
71             keysStartIndex = first
72             map =
mapnull73                 MutableObjectIntMap<Any>(size).also { map ->
74                     list.forEach(
75                         fromIndex = first,
76                         toIndex = last,
77                     ) {
78                         val keyFactory = it.value.key
79                         val start = maxOf(first, it.startIndex)
80                         val end = minOf(last, it.startIndex + it.size - 1)
81                         for (i in start..end) {
82                             val key =
83                                 keyFactory?.invoke(i - it.startIndex) ?: getDefaultLazyLayoutKey(i)
84                             map[key] = i
85                             keys[i - keysStartIndex] = key
86                         }
87                     }
88                 }
89         }
90     }
91 
<lambda>null92     override fun getIndex(key: Any): Int = map.getOrElse(key) { -1 }
93 
<lambda>null94     override fun getKey(index: Int) = keys.getOrElse(index - keysStartIndex) { null }
95 }
96