Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a0b3f97 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[kairos] remove unused utils + organize remaining" into main

parents 2bd466fa 2a5e24c8
Loading
Loading
Loading
Loading
+0 −129
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.kairos.internal.util

import com.android.systemui.kairos.internal.store.NoValue
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap

internal class ConcurrentNullableHashMap<K, V>
private constructor(private val inner: ConcurrentHashMap<Any, Any>) :
    ConcurrentMap<K, V>, AbstractMutableMap<K, V>() {

    constructor() : this(ConcurrentHashMap())

    constructor(capacity: Int) : this(ConcurrentHashMap(capacity))

    override fun get(key: K): V? = inner[key ?: NullValue]?.let { toNullable<V>(it) }

    fun getValue(key: K): V = toNullable(inner.getValue(key ?: NullValue))

    @Suppress("UNCHECKED_CAST")
    override fun put(key: K, value: V): V? =
        inner.put(key ?: NullValue, value ?: NullValue)?.takeIf { it !== NullValue } as V?

    operator fun set(key: K, value: V) {
        put(key, value)
    }

    fun toMap(): Map<K, V> =
        inner.asSequence().associate { (k, v) -> toNullable<K>(k) to toNullable(v) }

    override fun clear() {
        inner.clear()
    }

    override fun remove(key: K, value: V): Boolean = inner.remove(key ?: NoValue, value ?: NoValue)

    override val entries: MutableSet<MutableMap.MutableEntry<K, V>> =
        object : AbstractMutableSet<MutableMap.MutableEntry<K, V>>() {
            val wrapped = inner.entries

            override fun add(element: MutableMap.MutableEntry<K, V>): Boolean {
                val e =
                    object : MutableMap.MutableEntry<Any, Any> {
                        override val key: Any
                            get() = element.key ?: NullValue

                        override val value: Any
                            get() = element.value ?: NullValue

                        override fun setValue(newValue: Any): Any =
                            element.setValue(toNullable(newValue)) ?: NullValue
                    }
                return wrapped.add(e)
            }

            override val size: Int
                get() = wrapped.size

            override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> {
                val iter = wrapped.iterator()
                return object : MutableIterator<MutableMap.MutableEntry<K, V>> {
                    override fun hasNext(): Boolean = iter.hasNext()

                    override fun next(): MutableMap.MutableEntry<K, V> {
                        val element = iter.next()
                        return object : MutableMap.MutableEntry<K, V> {
                            override val key: K
                                get() = toNullable(element.key)

                            override val value: V
                                get() = toNullable(element.value)

                            override fun setValue(newValue: V): V =
                                toNullable(element.setValue(newValue ?: NullValue))
                        }
                    }

                    override fun remove() {
                        iter.remove()
                    }
                }
            }
        }

    override fun replace(key: K, oldValue: V, newValue: V): Boolean =
        inner.replace(key ?: NullValue, oldValue ?: NullValue, newValue ?: NullValue)

    override fun replace(key: K, value: V): V? =
        inner.replace(key ?: NullValue, value ?: NullValue)?.let { toNullable<V>(it) }

    override fun putIfAbsent(key: K, value: V): V? =
        inner.putIfAbsent(key ?: NullValue, value ?: NullValue)?.let { toNullable<V>(it) }

    @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
    private inline fun <T> toNullable(value: Any): T = value.takeIf { it !== NullValue } as T

    fun isNotEmpty(): Boolean = inner.isNotEmpty()

    @Suppress("UNCHECKED_CAST")
    override fun remove(key: K): V? =
        inner.remove(key ?: NullValue)?.takeIf { it !== NullValue } as V?

    fun asSequence(): Sequence<Pair<K, V>> =
        inner.asSequence().map { (key, value) -> toNullable<K>(key) to toNullable(value) }

    override fun isEmpty(): Boolean = inner.isEmpty()

    override fun containsKey(key: K): Boolean = inner.containsKey(key ?: NullValue)

    fun getOrPut(key: K, defaultValue: () -> V): V =
        toNullable(inner.getOrPut(key) { defaultValue() ?: NullValue })
}

private object NullValue
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,48 +16,44 @@

package com.android.systemui.kairos.internal.util

import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.Maybe.Absent

private object NULL

internal class HeteroMap private constructor(private val store: HashMap<Key<*>, Any>) {
    interface Key<A> {}

    constructor() : this(HashMap())

    constructor(capacity: Int) : this(HashMap(capacity))

    @Suppress("UNCHECKED_CAST")
    operator fun <A> get(key: Key<A>): Maybe<A> =
        store[key]?.let { Maybe.present((if (it === NULL) null else it) as A) } ?: Absent

    operator fun <A> set(key: Key<A>, value: A) {
        store[key] = value ?: NULL
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
import kotlinx.coroutines.newCoroutineContext

internal fun CoroutineScope.launchImmediate(
    start: CoroutineStart = CoroutineStart.UNDISPATCHED,
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> Unit,
): Job = launch(start = start, context = Dispatchers.Unconfined + context, block = block)

internal suspend fun awaitCancellationAndThen(block: suspend () -> Unit) {
    try {
        awaitCancellation()
    } finally {
        block()
    }

    @Suppress("UNCHECKED_CAST")
    fun <A : Any> getOrNull(key: Key<A>): A? =
        store[key]?.let { (if (it === NULL) null else it) as A }

    @Suppress("UNCHECKED_CAST")
    fun <A> getOrError(key: Key<A>, block: () -> String): A {
        store[key]?.let {
            return (if (it === NULL) null else it) as A
        } ?: error(block())
}

    operator fun contains(key: Key<*>): Boolean = store.containsKey(key)

    fun clear() {
        store.clear()
internal fun CoroutineScope.invokeOnCancel(
    context: CoroutineContext = EmptyCoroutineContext,
    block: () -> Unit,
): Job =
    launch(context = context, start = CoroutineStart.UNDISPATCHED) {
        awaitCancellationAndThen(block)
    }

    @Suppress("UNCHECKED_CAST")
    fun <A> remove(key: Key<A>): Maybe<A> =
        store.remove(key)?.let { Maybe.present((if (it === NULL) null else it) as A) } ?: Absent

    @Suppress("UNCHECKED_CAST")
    fun <A> getOrPut(key: Key<A>, defaultValue: () -> A): A =
        store.compute(key) { _, value -> value ?: defaultValue() ?: NULL } as A
@OptIn(ExperimentalCoroutinesApi::class)
internal fun CoroutineScope.childScope(
    context: CoroutineContext = EmptyCoroutineContext
): CoroutineScope {
    val newContext = newCoroutineContext(context)
    val newJob = Job(parent = newContext[Job])
    return CoroutineScope(newContext + newJob)
}
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.kairos.internal.util

internal val Any.hashString: String
    get() = Integer.toHexString(System.identityHashCode(this))
+5 −65
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -14,39 +14,25 @@
 * limitations under the License.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.kairos.internal.util

import com.android.app.tracing.traceSection
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.time.DurationUnit
import kotlin.time.measureTimedValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
import kotlinx.coroutines.newCoroutineContext

private const val LogEnabled = false
private const val LoggingEnabled = false

internal inline fun logLn(indent: Int = 0, getMessage: () -> Any?) {
    if (!LogEnabled) return
    if (!LoggingEnabled) return
    log(indent, getMessage)
    println()
}

internal inline fun log(indent: Int = 0, getMessage: () -> Any?) {
    if (!LogEnabled) return
    if (!LoggingEnabled) return
    printIndent(indent)
    print(getMessage())
}
@@ -79,7 +65,7 @@ internal inline fun <R> logDuration(
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        callsInPlace(getPrefix, InvocationKind.AT_MOST_ONCE)
    }
    return if (!LogEnabled) {
    return if (!LoggingEnabled) {
        if (trace) {
            traceSection(getPrefix) { LogIndent(0).block() }
        } else {
@@ -121,49 +107,3 @@ private inline fun printIndent(indent: Int) {
        print("  ")
    }
}

internal fun <A> CoroutineScope.asyncImmediate(
    start: CoroutineStart = CoroutineStart.UNDISPATCHED,
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> A,
): Deferred<A> = async(start = start, context = Dispatchers.Unconfined + context, block = block)

internal fun CoroutineScope.launchImmediate(
    start: CoroutineStart = CoroutineStart.UNDISPATCHED,
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> Unit,
): Job = launch(start = start, context = Dispatchers.Unconfined + context, block = block)

internal suspend fun awaitCancellationAndThen(block: suspend () -> Unit) {
    try {
        awaitCancellation()
    } finally {
        block()
    }
}

internal fun CoroutineScope.invokeOnCancel(
    context: CoroutineContext = EmptyCoroutineContext,
    block: () -> Unit,
): Job =
    launch(context = context, start = CoroutineStart.UNDISPATCHED) {
        awaitCancellationAndThen(block)
    }

internal fun CoroutineScope.childScope(
    context: CoroutineContext = EmptyCoroutineContext
): CoroutineScope {
    val newContext = newCoroutineContext(context)
    val newJob = Job(parent = newContext[Job])
    return CoroutineScope(newContext + newJob)
}

internal fun <A> Iterable<A>.associateByIndex(): Map<Int, A> = buildMap {
    forEachIndexed { index, a -> put(index, a) }
}

internal fun <A, M : MutableMap<Int, A>> Iterable<A>.associateByIndexTo(destination: M): M =
    destination.apply { forEachIndexed { index, a -> put(index, a) } }

internal val Any.hashString: String
    get() = Integer.toHexString(System.identityHashCode(this))
+0 −68
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.kairos.internal.util

import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.yield

// TODO: It's possible that this is less efficient than having each coroutine directly insert into a
//  ConcurrentHashMap, but then we would lose ordering
internal suspend inline fun <K, A, B : Any, M : MutableMap<K, B>> Map<K, A>
    .mapValuesNotNullParallelTo(
    destination: M,
    crossinline block: suspend (Map.Entry<K, A>) -> B?,
): M =
    destination.also {
        coroutineScope {
                mapValues {
                    asyncImmediate {
                        yield()
                        block(it)
                    }
                }
            }
            .mapValuesNotNullTo(it) { (_, deferred) -> deferred.await() }
    }

internal inline fun <K, A, B, M : MutableMap<K, B>> Map<K, A>.mapValuesNotNullTo(
    destination: M,
    block: (Map.Entry<K, A>) -> B?,
): M =
    destination.also {
        for (entry in this@mapValuesNotNullTo) {
            block(entry)?.let { destination.put(entry.key, it) }
        }
    }

internal inline fun <K, A, B> Map<K, A>.mapValuesNotNull(
    block: (Map.Entry<K, A>) -> B?
): Map<K, B> = mapValuesNotNullTo(mutableMapOf(), block)

internal suspend fun <A, B> Iterable<A>.mapParallel(transform: suspend (A) -> B): List<B> =
    coroutineScope {
        map { asyncImmediate { transform(it) } }.awaitAll()
    }

internal suspend fun <K, A, B, M : MutableMap<K, B>> Map<K, A>.mapValuesParallelTo(
    destination: M,
    transform: suspend (Map.Entry<K, A>) -> B,
): Map<K, B> = entries.mapParallel { it.key to transform(it) }.toMap(destination)

internal suspend fun <K, A, B> Map<K, A>.mapValuesParallel(
    transform: suspend (Map.Entry<K, A>) -> B
): Map<K, B> = mapValuesParallelTo(mutableMapOf(), transform)
Loading