1 /*
<lambda>null2  * Copyright 2020 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.runtime.snapshots
18 
19 import kotlin.contracts.ExperimentalContracts
20 import kotlin.contracts.contract
21 
22 /**
23  * Iterates through a [List] using the index and calls [action] for each item. This does not
24  * allocate an iterator like [Iterable.forEach].
25  *
26  * **Do not use for collections that come from public APIs**, since they may not support random
27  * access in an efficient way, and this method may actually be a lot slower. Only use for
28  * collections that are created by code we control and are known to support random access.
29  */
30 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
31 @OptIn(ExperimentalContracts::class)
32 internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
33     contract { callsInPlace(action) }
34     for (index in indices) {
35         val item = get(index)
36         action(item)
37     }
38 }
39 
40 /**
41  * Returns a [Set] of all elements.
42  *
43  * The returned set preserves the element iteration order of the original collection.
44  *
45  * **Do not use for collections that come from public APIs**, since they may not support random
46  * access in an efficient way, and this method may actually be a lot slower. Only use for
47  * collections that are created by code we control and are known to support random access.
48  */
fastToSetnull49 internal fun <T> List<T>.fastToSet(): Set<T> =
50     HashSet<T>(size).also { set -> fastForEach { item -> set.add(item) } }
51 
52 /**
53  * Iterates through a [List] using the index and calls [action] for each item. This does not
54  * allocate an iterator like [Iterable.forEachIndexed].
55  *
56  * **Do not use for collections that come from public APIs**, since they may not support random
57  * access in an efficient way, and this method may actually be a lot slower. Only use for
58  * collections that are created by code we control and are known to support random access.
59  */
60 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
61 @OptIn(ExperimentalContracts::class)
fastForEachIndexednull62 internal inline fun <T> List<T>.fastForEachIndexed(action: (Int, T) -> Unit) {
63     contract { callsInPlace(action) }
64     for (index in indices) {
65         val item = get(index)
66         action(index, item)
67     }
68 }
69 
70 /**
71  * Returns a list containing the results of applying the given [transform] function to each element
72  * in the original collection.
73  *
74  * **Do not use for collections that come from public APIs**, since they may not support random
75  * access in an efficient way, and this method may actually be a lot slower. Only use for
76  * collections that are created by code we control and are known to support random access.
77  */
78 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
79 @OptIn(ExperimentalContracts::class)
fastMapnull80 internal inline fun <T, R> List<T>.fastMap(transform: (T) -> R): List<R> {
81     contract { callsInPlace(transform) }
82     val target = ArrayList<R>(size)
83     fastForEach { target += transform(it) }
84     return target
85 }
86 
87 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
88 @OptIn(ExperimentalContracts::class)
fastAnynull89 internal inline fun <T> List<T>.fastAny(predicate: (T) -> Boolean): Boolean {
90     contract { callsInPlace(predicate) }
91     fastForEach { if (predicate(it)) return true }
92     return false
93 }
94 
95 /**
96  * Returns `true` if all elements match the given [predicate].
97  *
98  * **Do not use for collections that come from public APIs**, since they may not support random
99  * access in an efficient way, and this method may actually be a lot slower. Only use for
100  * collections that are created by code we control and are known to support random access.
101  */
102 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
103 @OptIn(ExperimentalContracts::class)
fastAllnull104 internal inline fun <T> List<T>.fastAll(predicate: (T) -> Boolean): Boolean {
105     contract { callsInPlace(predicate) }
106     fastForEach { if (!predicate(it)) return false }
107     return true
108 }
109 
110 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
111 @OptIn(ExperimentalContracts::class)
fastGroupBynull112 internal inline fun <T, K> List<T>.fastGroupBy(keySelector: (T) -> K): Map<K, List<T>> {
113     contract { callsInPlace(keySelector) }
114     val destination = HashMap<K, ArrayList<T>>(size)
115     fastForEach {
116         val key = keySelector(it)
117         val list = destination.getOrPut(key) { ArrayList<T>() }
118         list.add(it)
119     }
120     return destination
121 }
122 
123 /**
124  * Creates a string from all the elements separated using [separator] and using the given [prefix]
125  * and [postfix] if supplied.
126  *
127  * If the collection could be huge, you can specify a non-negative value of [limit], in which case
128  * only the first [limit] elements will be appended, followed by the [truncated] string (which
129  * defaults to "...").
130  *
131  * **Do not use for collections that come from public APIs**, since they may not support random
132  * access in an efficient way, and this method may actually be a lot slower. Only use for
133  * collections that are created by code we control and are known to support random access.
134  */
fastJoinToStringnull135 internal fun <T> List<T>.fastJoinToString(
136     separator: CharSequence = ", ",
137     prefix: CharSequence = "",
138     postfix: CharSequence = "",
139     limit: Int = -1,
140     truncated: CharSequence = "...",
141     transform: ((T) -> CharSequence)? = null
142 ): String {
143     return fastJoinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform)
144         .toString()
145 }
146 
147 /**
148  * Appends the string from all the elements separated using [separator] and using the given [prefix]
149  * and [postfix] if supplied.
150  *
151  * If the collection could be huge, you can specify a non-negative value of [limit], in which case
152  * only the first [limit] elements will be appended, followed by the [truncated] string (which
153  * defaults to "...").
154  *
155  * **Do not use for collections that come from public APIs**, since they may not support random
156  * access in an efficient way, and this method may actually be a lot slower. Only use for
157  * collections that are created by code we control and are known to support random access.
158  */
fastJoinTonull159 private fun <T, A : Appendable> List<T>.fastJoinTo(
160     buffer: A,
161     separator: CharSequence = ", ",
162     prefix: CharSequence = "",
163     postfix: CharSequence = "",
164     limit: Int = -1,
165     truncated: CharSequence = "...",
166     transform: ((T) -> CharSequence)? = null
167 ): A {
168     buffer.append(prefix)
169     var count = 0
170     for (index in indices) {
171         val element = get(index)
172         if (++count > 1) buffer.append(separator)
173         if (limit < 0 || count <= limit) {
174             buffer.appendElement(element, transform)
175         } else break
176     }
177     if (limit >= 0 && count > limit) buffer.append(truncated)
178     buffer.append(postfix)
179     return buffer
180 }
181 
182 /** Copied from Appendable.kt */
appendElementnull183 private fun <T> Appendable.appendElement(element: T, transform: ((T) -> CharSequence)?) {
184     when {
185         transform != null -> append(transform(element))
186         element is CharSequence? -> append(element)
187         element is Char -> append(element)
188         else -> append(element.toString())
189     }
190 }
191 
192 /**
193  * Returns a list containing the results of applying the given [transform] function to each element
194  * in the original collection.
195  *
196  * **Do not use for collections that come from public APIs**, since they may not support random
197  * access in an efficient way, and this method may actually be a lot slower. Only use for
198  * collections that are created by code we control and are known to support random access.
199  */
200 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
201 @OptIn(ExperimentalContracts::class)
fastMapNotNullnull202 internal inline fun <T, R> List<T>.fastMapNotNull(transform: (T) -> R?): List<R> {
203     contract { callsInPlace(transform) }
204     val target = ArrayList<R>(size)
205     fastForEach { e -> transform(e)?.let { target += it } }
206     return target
207 }
208 
209 /**
210  * Returns a list containing only elements matching the given [predicate].
211  *
212  * @param [predicate] function that takes the index of an element and the element itself and returns
213  *   the result of predicate evaluation on the element.
214  *
215  * **Do not use for collections that come from public APIs**, since they may not support random
216  * access in an efficient way, and this method may actually be a lot slower. Only use for
217  * collections that are created by code we control and are known to support random access.
218  */
219 @Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
220 @OptIn(ExperimentalContracts::class)
fastFilterIndexednull221 internal inline fun <T> List<T>.fastFilterIndexed(predicate: (index: Int, T) -> Boolean): List<T> {
222     contract { callsInPlace(predicate) }
223     val target = ArrayList<T>(size)
224     fastForEachIndexed { index, e -> if (predicate(index, e)) target += e }
225     return target
226 }
227