Skip to content
This repository has been archived by the owner on Jun 21, 2022. It is now read-only.

Commit

Permalink
Implement fastForEach variants for FastIntMap and IntMap variants
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Mar 2, 2019
1 parent b14e781 commit 1c51ff2
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 13 deletions.
3 changes: 3 additions & 0 deletions kds/src/androidMain/kotlin/com/soywiz/kds/Android.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ actual inline operator fun <T> FastIntMap<T>.contains(key: Int): Boolean = (this
actual inline fun <T> FastIntMap<T>.remove(key: Int): Unit = run { (this as IntMap<T>).remove(key) }
actual inline fun <T> FastIntMap<T>.removeRange(src: Int, dst: Int) = (this as IntMap<T>).removeRange(src, dst)
actual inline fun <T> FastIntMap<T>.clear() = (this as IntMap<T>).clear()
actual inline fun <T> FastIntMap<T>.fastKeyForEach(callback: (key: Int) -> Unit): Unit {
(this as IntMap<T>).fastKeyForEach(callback)
}

actual class FastStringMap<T>(val dummy: Boolean) {
val map = LinkedHashMap<String, T>()
Expand Down
27 changes: 25 additions & 2 deletions kds/src/commonMain/kotlin/com/soywiz/kds/FastMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ fun <T> FastIntMap<T>.values(): List<T> = this.keys().map { this[it] } as List<T
val <T> FastIntMap<T>.keys: List<Int> get() = keys()
val <T> FastIntMap<T>.values: List<T> get() = values()

expect inline fun <T> FastIntMap<T>.fastKeyForEach(callback: (key: Int) -> Unit): Unit

inline fun <T : Any?> FastIntMap<T>.fastValueForEachNullable(callback: (value: T?) -> Unit): Unit {
fastKeyForEach { callback(this[it]) }
}
inline fun <T : Any?> FastIntMap<T>.fastForEachNullable(callback: (key: Int, value: T?) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]) }
}

inline fun <T : Any> FastIntMap<T>.fastValueForEach(callback: (value: T) -> Unit): Unit {
fastKeyForEach { callback(this[it]!!) }
}
inline fun <T : Any> FastIntMap<T>.fastForEach(callback: (key: Int, value: T) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]!!) }
}

inline fun <T> FastIntMap<T>.getNull(key: Int?): T? = if (key == null) null else get(key)

inline fun <T> FastIntMap<T>.getOrPut(key: Int, callback: () -> T): T {
Expand Down Expand Up @@ -46,13 +62,20 @@ val <T> FastStringMap<T>.values: List<T> get() = values()

expect inline fun <T> FastStringMap<T>.fastKeyForEach(callback: (key: String) -> Unit): Unit

inline fun <T> FastStringMap<T>.fastValueForEach(callback: (value: T?) -> Unit): Unit {
inline fun <T : Any?> FastStringMap<T>.fastValueForEachNullable(callback: (value: T?) -> Unit): Unit {
fastKeyForEach { callback(this[it]) }
}
inline fun <T> FastStringMap<T>.fastForEach(callback: (key: String, value: T?) -> Unit): Unit {
inline fun <T : Any?> FastStringMap<T>.fastForEachNullable(callback: (key: String, value: T?) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]) }
}

inline fun <T : Any> FastStringMap<T>.fastValueForEach(callback: (value: T) -> Unit): Unit {
fastKeyForEach { callback(this[it]!!) }
}
inline fun <T : Any> FastStringMap<T>.fastForEach(callback: (key: String, value: T) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]!!) }
}

inline fun <T> FastStringMap<T>.getNull(key: String?): T? = if (key == null) null else get(key)

inline fun <T> FastStringMap<T>.getOrPut(key: String, callback: () -> T): T {
Expand Down
102 changes: 91 additions & 11 deletions kds/src/commonMain/kotlin/com/soywiz/kds/IntMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ class IntMap<T> private constructor(private var nbits: Int, private val loadFact
constructor(loadFactor: Double = 0.75) : this(4, loadFactor)

companion object {
private const val EOF = Int.MAX_VALUE - 1
private const val ZERO_INDEX = Int.MAX_VALUE
private const val EMPTY = 0
@PublishedApi
internal const val EOF = Int.MAX_VALUE - 1
@PublishedApi
internal const val ZERO_INDEX = Int.MAX_VALUE
@PublishedApi
internal const val EMPTY = 0
}

private var capacity = 1 shl nbits
private var hasZero = false
@PublishedApi
internal var hasZero = false
private var zeroValue: T? = null
private var mask = capacity - 1
private var stashSize = 1 + ilog2(capacity)
private var _keys = IntArray(capacity + stashSize)
@PublishedApi
internal var _keys = IntArray(capacity + stashSize)
private var _values = arrayOfNulls<Any>(capacity + stashSize) as Array<T?>
private val stashStart get() = _keys.size - stashSize
private var growSize: Int = (capacity * loadFactor).toInt()
Expand Down Expand Up @@ -216,16 +221,50 @@ class IntMap<T> private constructor(private var nbits: Int, private val loadFact
if (index != EOF) index = nextNonEmptyIndex(_keys, if (index == ZERO_INDEX) 0 else (index + 1))
}
}

@PublishedApi
internal fun nextNonEmptyIndex(keys: IntArray, offset: Int): Int {
for (n in offset until keys.size) if (keys[n] != EMPTY) return n
return EOF
}

inline fun fastKeyForEach(callback: (key: Int) -> Unit) {
var index: Int = if (hasZero) ZERO_INDEX else nextNonEmptyIndex(_keys, 0)
while (index != EOF) {
callback(
when (index) {
ZERO_INDEX, EOF -> 0
else -> _keys[index]
}
)
index = nextNonEmptyIndex(_keys, if (index == ZERO_INDEX) 0 else (index + 1))
}
}
inline fun fastValueForEachNullable(callback: (value: T?) -> Unit): Unit {
fastKeyForEach { callback(this[it]) }
}
inline fun fastForEachNullable(callback: (key: Int, value: T?) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]) }
}

inline fun fastValueForEach(callback: (value: T) -> Unit): Unit {
fastKeyForEach { callback(this[it]!!) }
}
inline fun fastForEach(callback: (key: Int, value: T) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]!!) }
}
}


fun <T> Map<Int, T>.toIntMap(): IntMap<T> {
val out = IntMap<T>()
for ((k, v) in this) out[k] = v
return out
}

class IntFloatMap {
private val i = IntIntMap()
@PublishedApi
internal val i = IntIntMap()

val size: Int get() = i.size
fun clear() = i.clear()
Expand Down Expand Up @@ -254,23 +293,38 @@ class IntFloatMap {
operator fun contains(key: Int): Boolean = key in i
operator fun get(key: Int): Float = Float.fromBits(i[key])
operator fun set(key: Int, value: Float): Float = Float.fromBits(i.set(key, value.toRawBits()))

inline fun fastKeyForEach(callback: (key: Int) -> Unit) {
i.fastKeyForEach(callback)
}

inline fun fastValueForEach(callback: (value: Float) -> Unit): Unit {
fastKeyForEach { callback(this[it]) }
}
inline fun fastForEach(callback: (key: Int, value: Float) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]) }
}
}

class IntIntMap private constructor(private var nbits: Int, private val loadFactor: Double) {
constructor(loadFactor: Double = 0.75) : this(4, loadFactor)

companion object {
private const val EOF = Int.MAX_VALUE - 1
private const val ZERO_INDEX = Int.MAX_VALUE
private const val EMPTY = 0
@PublishedApi
internal const val EOF = Int.MAX_VALUE - 1
@PublishedApi
internal const val ZERO_INDEX = Int.MAX_VALUE
@PublishedApi
internal const val EMPTY = 0
}

private var capacity = 1 shl nbits
private var hasZero = false
@PublishedApi
internal var hasZero = false
private var zeroValue: Int = 0
private var mask = capacity - 1
private var stashSize = 1 + ilog2(capacity)
private var _keys = IntArray(capacity + stashSize)
@PublishedApi internal var _keys = IntArray(capacity + stashSize)
private var _values = IntArray(capacity + stashSize)
private val stashStart get() = _keys.size - stashSize
private var growSize: Int = (capacity * loadFactor).toInt()
Expand Down Expand Up @@ -448,4 +502,30 @@ class IntIntMap private constructor(private var nbits: Int, private val loadFact
if (index != EOF) index = nextNonEmptyIndex(_keys, if (index == ZERO_INDEX) 0 else (index + 1))
}
}

@PublishedApi
internal fun nextNonEmptyIndex(keys: IntArray, offset: Int): Int {
for (n in offset until keys.size) if (keys[n] != EMPTY) return n
return EOF
}

inline fun fastKeyForEach(callback: (key: Int) -> Unit) {
var index: Int = if (hasZero) IntMap.ZERO_INDEX else nextNonEmptyIndex(_keys, 0)
while (index != IntMap.EOF) {
callback(
when (index) {
IntMap.ZERO_INDEX, IntMap.EOF -> 0
else -> _keys[index]
}
)
index = nextNonEmptyIndex(_keys, if (index == IntMap.ZERO_INDEX) 0 else (index + 1))
}
}

inline fun fastValueForEach(callback: (value: Int) -> Unit): Unit {
fastKeyForEach { callback(this[it]) }
}
inline fun fastForEach(callback: (key: Int, value: Int) -> Unit): Unit {
fastKeyForEach { callback(it, this[it]) }
}
}
16 changes: 16 additions & 0 deletions kds/src/commonTest/kotlin/com/soywiz/kds/FastMapTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,20 @@ class FastMapTest {
val other2 = other.entries.sortedBy { it.key }.toList().associate { it.key to it.value }
assertEquals(mapOf("a" to 1, "b" to 2), other2)
}

@Test
fun testIntForeach() {
val map = FastIntMap<String>().apply {
this[1] = "a"
this[2] = "b"
}
val other = HashMap<Int, String>()
//println("testStringForeach:")
map.fastForEach { key, value ->
//println("testStringForeach: key=$key, value=$value")
other[key] = value!!
}
val other2 = other.entries.sortedBy { it.key }.toList().associate { it.key to it.value }
assertEquals(mapOf(1 to "a", 2 to "b"), other2)
}
}
13 changes: 13 additions & 0 deletions kds/src/jsMain/kotlin/com/soywiz/kds/Js.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ actual inline fun <T> FastIntMap<T>.clear() {
(this.asDynamic()).clear()
}

@Suppress("UnsafeCastFromDynamic")
actual inline fun <T> FastIntMap<T>.fastKeyForEach(callback: (key: Int) -> Unit): Unit {
//println("FastStringMap<T>.fastKeyForEach")
val mapIterator = this.asDynamic().keys()
//console.log(mapIterator)
while (true) {
val v = mapIterator.next()
//console.log(v)
if (v.done) break
callback(v.value)
}
}

actual class FastStringMap<T>(dummy: Boolean)
//actual typealias FastStringMap<T> = Any<T>

Expand Down
4 changes: 4 additions & 0 deletions kds/src/jvmMain/kotlin/com/soywiz/kds/Jvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ actual inline fun <T> FastIntMap<T>.remove(key: Int): Unit = run { (this as IntM
actual inline fun <T> FastIntMap<T>.removeRange(src: Int, dst: Int) = (this as IntMap<T>).removeRange(src, dst)
actual inline fun <T> FastIntMap<T>.clear() = (this as IntMap<T>).clear()

actual inline fun <T> FastIntMap<T>.fastKeyForEach(callback: (key: Int) -> Unit): Unit {
(this as IntMap<T>).fastKeyForEach(callback)
}

actual class FastStringMap<T>(val dummy: Boolean) {
val map = LinkedHashMap<String, T>()
}
Expand Down
3 changes: 3 additions & 0 deletions kds/src/nativeCommonMain/kotlin/com/soywiz/kds/Native.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ actual inline operator fun <T> FastIntMap<T>.contains(key: Int): Boolean = (this
actual inline fun <T> FastIntMap<T>.remove(key: Int): Unit = run { (this as IntMap<T>).remove(key) }
actual inline fun <T> FastIntMap<T>.removeRange(src: Int, dst: Int) = (this as IntMap<T>).removeRange(src, dst)
actual inline fun <T> FastIntMap<T>.clear() = (this as IntMap<T>).clear()
actual inline fun <T> FastIntMap<T>.fastKeyForEach(callback: (key: Int) -> Unit): Unit {
(this as IntMap<T>).fastKeyForEach(callback)
}

actual class FastStringMap<T>(val dummy: Boolean) {
val map = LinkedHashMap<String, T>()
Expand Down

0 comments on commit 1c51ff2

Please sign in to comment.