SnapshotDoubleState.kt

/*
 * Copyright 2023 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.
 */

@file:JvmName("SnapshotDoubleStateKt")
@file:JvmMultifileClass
package androidx.compose.runtime

import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.runtime.snapshots.SnapshotMutableState
import androidx.compose.runtime.snapshots.StateObject
import androidx.compose.runtime.snapshots.StateRecord
import androidx.compose.runtime.snapshots.overwritable
import androidx.compose.runtime.snapshots.readable
import androidx.compose.runtime.snapshots.withCurrent
import kotlin.reflect.KProperty

/**
 * Return a new [MutableDoubleState] initialized with the passed in [value]
 *
 * The MutableDoubleState class is a single value holder whose reads and writes are observed by
 * Compose. Additionally, writes to it are transacted as part of the [Snapshot] system. On the JVM,
 * values are stored in memory as the primitive `double` type, avoiding the autoboxing that occurs
 * when using `MutableState<Double>`.
 *
 * @param value the initial value for the [MutableDoubleState]
 *
 * @see DoubleState
 * @see MutableDoubleState
 */
fun mutableStateOf(
    value: Double
): MutableDoubleState = createSnapshotMutableDoubleState(value)

/**
 * A value holder where reads to the [doubleValue] property during the execution of a [Composable]
 * function cause the current [RecomposeScope] to subscribe to changes of that value.
 *
 * @see MutableDoubleState
 * @see mutableStateOf
 */
@Stable
@JvmDefaultWithCompatibility
interface DoubleState : State<Double> {
    @AutoboxingStateValueProperty("doubleValue")
    override val value: Double
        @Suppress("AutoBoxing") get() = doubleValue

    val doubleValue: Double
}

/**
 * Permits property delegation of `val`s using `by` for [DoubleState].
 */
@Suppress("NOTHING_TO_INLINE")
inline operator fun DoubleState.getValue(
    thisObj: Any?,
    property: KProperty<*>
): Double = doubleValue

/**
 * A value holder where reads to the [doubleValue] property during the execution of a [Composable]
 * function cause the current [RecomposeScope] to subscribe to changes of that value. When the
 * [doubleValue] property is written to and changed, a recomposition of any subscribed [RecomposeScope]s
 * will be scheduled. If [doubleValue] is written to with the same value, no recompositions will be
 * scheduled.
 *
 * @see [DoubleState]
 * @see [mutableStateOf]
 */
@Stable
@JvmDefaultWithCompatibility
interface MutableDoubleState : DoubleState, MutableState<Double> {
    @AutoboxingStateValueProperty("doubleValue")
    override var value: Double
        @Suppress("AutoBoxing") get() = doubleValue
        set(value) { doubleValue = value }

    override var doubleValue: Double
}

/**
 * Permits property delegation of `var`s using `by` for [MutableDoubleState].
 */
@Suppress("NOTHING_TO_INLINE")
inline operator fun MutableDoubleState.setValue(
    thisObj: Any?,
    property: KProperty<*>,
    value: Double
) {
    this.doubleValue = value
}

internal expect fun createSnapshotMutableDoubleState(
    value: Double
): MutableDoubleState

/**
 * A single value holder whose reads and writes are observed by Compose.
 *
 * Additionally, writes to it are transacted as part of the [Snapshot] system.
 *
 * @param value the wrapped value
 *
 * @see [mutableStateOf]
 */
internal open class SnapshotMutableDoubleStateImpl(
    value: Double
) : StateObject, MutableDoubleState, SnapshotMutableState<Double> {

    private var next = DoubleStateStateRecord(value)

    override val firstStateRecord: StateRecord
        get() = next

    override var doubleValue: Double
        get() = next.readable(this).value
        set(value) = next.withCurrent {
            if (it.value != value) {
                next.overwritable(this, it) { this.value = value }
            }
        }

    // Arbitrary policies are not allowed. The underlying `==` implementation
    // for primitive types corresponds to structural equality
    override val policy: SnapshotMutationPolicy<Double>
        get() = structuralEqualityPolicy()

    override fun component1(): Double = doubleValue

    override fun component2(): (Double) -> Unit = { doubleValue = it }

    override fun prependStateRecord(value: StateRecord) {
        next = value as DoubleStateStateRecord
    }

    override fun mergeRecords(
        previous: StateRecord,
        current: StateRecord,
        applied: StateRecord
    ): StateRecord? {
        val currentRecord = current as DoubleStateStateRecord
        val appliedRecord = applied as DoubleStateStateRecord
        return if (currentRecord.value == appliedRecord.value) {
            current
        } else {
            null
        }
    }

    override fun toString(): String = next.withCurrent {
        "MutableDoubleState(value=${it.value})@${hashCode()}"
    }

    private class DoubleStateStateRecord(
        var value: Double
    ) : StateRecord() {
        override fun assign(value: StateRecord) {
            this.value = (value as DoubleStateStateRecord).value
        }

        override fun create(): StateRecord = DoubleStateStateRecord(value)
    }
}