1 /*
<lambda>null2  * Copyright 2023 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 @file:JvmName("SnapshotLongStateKt")
18 @file:JvmMultifileClass
19 
20 package androidx.compose.runtime
21 
22 import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
23 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
24 import androidx.compose.runtime.snapshots.GlobalSnapshot
25 import androidx.compose.runtime.snapshots.Snapshot
26 import androidx.compose.runtime.snapshots.SnapshotId
27 import androidx.compose.runtime.snapshots.SnapshotMutableState
28 import androidx.compose.runtime.snapshots.StateFactoryMarker
29 import androidx.compose.runtime.snapshots.StateObjectImpl
30 import androidx.compose.runtime.snapshots.StateRecord
31 import androidx.compose.runtime.snapshots.currentSnapshot
32 import androidx.compose.runtime.snapshots.overwritable
33 import androidx.compose.runtime.snapshots.readable
34 import androidx.compose.runtime.snapshots.toSnapshotId
35 import androidx.compose.runtime.snapshots.withCurrent
36 import kotlin.jvm.JvmMultifileClass
37 import kotlin.jvm.JvmName
38 import kotlin.reflect.KProperty
39 
40 /**
41  * Return a new [MutableLongState] initialized with the passed in [value]
42  *
43  * The MutableLongState class is a single value holder whose reads and writes are observed by
44  * Compose. Additionally, writes to it are transacted as part of the [Snapshot] system. On the JVM,
45  * values are stored in memory as the primitive `long` type, avoiding the autoboxing that occurs
46  * when using `MutableState<Long>`.
47  *
48  * @param value the initial value for the [MutableLongState]
49  * @see LongState
50  * @see MutableLongState
51  * @see mutableStateOf
52  * @see mutableIntStateOf
53  * @see mutableFloatStateOf
54  * @see mutableDoubleStateOf
55  */
56 @StateFactoryMarker
57 fun mutableLongStateOf(value: Long): MutableLongState = createSnapshotMutableLongState(value)
58 
59 /**
60  * A value holder where reads to the [longValue] property during the execution of a [Composable]
61  * function cause the current [RecomposeScope] to subscribe to changes of that value.
62  *
63  * @see MutableLongState
64  * @see mutableDoubleStateOf
65  */
66 @Stable
67 @JvmDefaultWithCompatibility
68 interface LongState : State<Long> {
69     @get:AutoboxingStateValueProperty("longValue")
70     override val value: Long
71         @Suppress("AutoBoxing") get() = longValue
72 
73     val longValue: Long
74 }
75 
76 /** Permits property delegation of `val`s using `by` for [LongState]. */
77 @Suppress("NOTHING_TO_INLINE")
getValuenull78 inline operator fun LongState.getValue(thisObj: Any?, property: KProperty<*>): Long = longValue
79 
80 /**
81  * A value holder where reads to the [longValue] property during the execution of a [Composable]
82  * function cause the current [RecomposeScope] to subscribe to changes of that value. When the
83  * [longValue] property is written to and changed, a recomposition of any subscribed
84  * [RecomposeScope]s will be scheduled. If [longValue] is written to with the same value, no
85  * recompositions will be scheduled.
86  *
87  * @see [LongState]
88  * @see [mutableDoubleStateOf]
89  */
90 @Stable
91 @JvmDefaultWithCompatibility
92 interface MutableLongState : LongState, MutableState<Long> {
93     @get:AutoboxingStateValueProperty("longValue")
94     @set:AutoboxingStateValueProperty("longValue")
95     override var value: Long
96         @Suppress("AutoBoxing") get() = longValue
97         set(value) {
98             longValue = value
99         }
100 
101     override var longValue: Long
102 }
103 
104 /** Permits property delegation of `var`s using `by` for [MutableLongState]. */
105 @Suppress("NOTHING_TO_INLINE")
setValuenull106 inline operator fun MutableLongState.setValue(thisObj: Any?, property: KProperty<*>, value: Long) {
107     longValue = value
108 }
109 
createSnapshotMutableLongStatenull110 internal expect fun createSnapshotMutableLongState(value: Long): MutableLongState
111 
112 /**
113  * A single value holder whose reads and writes are observed by Compose.
114  *
115  * Additionally, writes to it are transacted as part of the [Snapshot] system.
116  *
117  * @param value the wrapped value
118  * @see [mutableDoubleStateOf]
119  */
120 internal open class SnapshotMutableLongStateImpl(value: Long) :
121     StateObjectImpl(), MutableLongState, SnapshotMutableState<Long> {
122 
123     private var next =
124         currentSnapshot().let { snapshot ->
125             LongStateStateRecord(snapshot.snapshotId, value).also {
126                 if (snapshot !is GlobalSnapshot) {
127                     it.next =
128                         LongStateStateRecord(Snapshot.PreexistingSnapshotId.toSnapshotId(), value)
129                 }
130             }
131         }
132 
133     override val firstStateRecord: StateRecord
134         get() = next
135 
136     override var longValue: Long
137         get() = next.readable(this).value
138         set(value) =
139             next.withCurrent {
140                 if (it.value != value) {
141                     next.overwritable(this, it) { this.value = value }
142                 }
143             }
144 
145     // Arbitrary policies are not allowed. The underlying `==` implementation
146     // for primitive types corresponds to structural equality
147     override val policy: SnapshotMutationPolicy<Long>
148         get() = structuralEqualityPolicy()
149 
150     override fun component1(): Long = longValue
151 
152     override fun component2(): (Long) -> Unit = { longValue = it }
153 
154     override fun prependStateRecord(value: StateRecord) {
155         next = value as LongStateStateRecord
156     }
157 
158     override fun mergeRecords(
159         previous: StateRecord,
160         current: StateRecord,
161         applied: StateRecord
162     ): StateRecord? {
163         val currentRecord = current as LongStateStateRecord
164         val appliedRecord = applied as LongStateStateRecord
165         return if (currentRecord.value == appliedRecord.value) {
166             current
167         } else {
168             null
169         }
170     }
171 
172     override fun toString(): String =
173         next.withCurrent { "MutableLongState(value=${it.value})@${hashCode()}" }
174 
175     private class LongStateStateRecord(snapshotId: SnapshotId, var value: Long) :
176         StateRecord(snapshotId) {
177         override fun assign(value: StateRecord) {
178             this.value = (value as LongStateStateRecord).value
179         }
180 
181         override fun create(): StateRecord = create(currentSnapshot().snapshotId)
182 
183         override fun create(snapshotId: SnapshotId): StateRecord =
184             LongStateStateRecord(snapshotId, value)
185     }
186 }
187