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