Compare commits

..

15 Commits

10 changed files with 107 additions and 14 deletions

View File

@@ -1,5 +1,23 @@
# Changelog # Changelog
## 0.17.3
* `Common`:
* Add `fixed` extensions for `Float` and `Double`
* New function `emptyDiff`
* Now you may pass custom `comparisonFun` to all `diff` functions
## 0.17.2
* `FSM`:
* `DefaultStatesManager.onUpdateContextsConflictResolver` and `DefaultStatesManager.onStartContextsConflictResolver` now return `false` by default
## 0.17.1
* **Hotfix** for absence of jvm dependencies in android modules
* `Versions`:
* `Ktor`: `2.2.3` -> `2.2.4`
## 0.17.0 ## 0.17.0
* `Versions`: * `Versions`:

View File

@@ -34,7 +34,11 @@ data class Diff<T> internal constructor(
*/ */
val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>, val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>,
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>> val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
) ) {
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
}
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())
private inline fun <T> performChanges( private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>, potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
@@ -43,14 +47,14 @@ private inline fun <T> performChanges(
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>, changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>, removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>, addedList: MutableList<IndexedValue<T>>,
strictComparison: Boolean comparisonFun: (T?, T?) -> Boolean
) { ) {
var i = -1 var i = -1
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) { for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
i++ i++
val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison) val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value)
val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison) val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value)
if (oldOneEqualToNewObject || newOneEqualToOldObject) { if (oldOneEqualToNewObject || newOneEqualToOldObject) {
changedList.addAll( changedList.addAll(
potentialChanges.take(i).mapNotNull { potentialChanges.take(i).mapNotNull {
@@ -104,7 +108,7 @@ private inline fun <T> performChanges(
*/ */
fun <T> Iterable<T>.calculateDiff( fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>, other: Iterable<T>,
strictComparison: Boolean = false comparisonFun: (T?, T?) -> Boolean
): Diff<T> { ): Diff<T> {
var i = -1 var i = -1
var j = -1 var j = -1
@@ -132,7 +136,7 @@ fun <T> Iterable<T>.calculateDiff(
} }
when { when {
oldObject === newObject || (oldObject == newObject && !strictComparison) -> { comparisonFun(oldObject, newObject) -> {
changedObjects.addAll(potentiallyChangedObjects.map { changedObjects.addAll(potentiallyChangedObjects.map {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
it as Pair<IndexedValue<T>, IndexedValue<T>> it as Pair<IndexedValue<T>, IndexedValue<T>>
@@ -143,23 +147,49 @@ fun <T> Iterable<T>.calculateDiff(
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) }) potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
val previousOldsAdditionsSize = additionalInOld.size val previousOldsAdditionsSize = additionalInOld.size
val previousNewsAdditionsSize = additionalInNew.size val previousNewsAdditionsSize = additionalInNew.size
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
i -= (additionalInOld.size - previousOldsAdditionsSize) i -= (additionalInOld.size - previousOldsAdditionsSize)
j -= (additionalInNew.size - previousNewsAdditionsSize) j -= (additionalInNew.size - previousNewsAdditionsSize)
} }
} }
} }
potentiallyChangedObjects.add(null to null) potentiallyChangedObjects.add(null to null)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList()) return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
} }
/**
* Calculating [Diff] object
*
* @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different
* objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(
other,
comparisonFun = if (strictComparison) {
{ t1, t2 ->
t1 === t2
}
} else {
{ t1, t2 ->
t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially
}
}
)
inline fun <T> Iterable<T>.diff( inline fun <T> Iterable<T>.diff(
other: Iterable<T>, other: Iterable<T>,
strictComparison: Boolean = false strictComparison: Boolean = false
): Diff<T> = calculateDiff(other, strictComparison) ): Diff<T> = calculateDiff(other, strictComparison)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
noinline comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(other, comparisonFun)
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new) inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, strictComparison = false)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true) inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
/** /**
@@ -187,3 +217,22 @@ fun <T> MutableList<T>.applyDiff(
set(new.index, new.value) set(new.index, new.value)
} }
} }
/**
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
* mutable list
*/
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(source, comparisonFun).also {
for (i in it.removed.indices.sortedDescending()) {
removeAt(it.removed[i].index)
}
it.added.forEach { (i, t) ->
add(i, t)
}
it.replaced.forEach { (_, new) ->
set(new.index, new.value)
}
}

View File

@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.common
val FixedSignsRange = 0 .. 100
expect fun Float.fixed(signs: Int): Float
expect fun Double.fixed(signs: Int): Double

View File

@@ -0,0 +1,4 @@
package dev.inmo.micro_utils.common
actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat()
actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble()

View File

@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import java.math.BigDecimal
import java.math.RoundingMode
actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble())
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toFloat();
actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this)
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toDouble();

View File

@@ -48,8 +48,8 @@ interface DefaultStatesManagerRepo<T : State> {
*/ */
open class DefaultStatesManager<T : State>( open class DefaultStatesManager<T : State>(
protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(), protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> true }, protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> false },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> false }
) : StatesManager<T> { ) : StatesManager<T> {
protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0) protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow() override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()

View File

@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.17.0 version=0.17.3
android_code_version=182 android_code_version=185

View File

@@ -13,7 +13,7 @@ jb-dokka = "1.7.20"
klock = "3.4.0" klock = "3.4.0"
uuid = "0.7.0" uuid = "0.7.0"
ktor = "2.2.3" ktor = "2.2.4"
gh-release = "2.4.1" gh-release = "2.4.1"

View File

@@ -50,6 +50,8 @@ kotlin {
implementation libs.android.espresso implementation libs.android.espresso
} }
} }
androidMain.dependsOn jvmMain
} }
} }

View File

@@ -61,6 +61,8 @@ kotlin {
implementation libs.android.espresso implementation libs.android.espresso
} }
} }
androidMain.dependsOn jvmMain
} }
} }