JS: reduce HashMap memory footprint
This commit is contained in:
@@ -37,24 +37,39 @@ import kotlin.collections.AbstractMutableMap.SimpleEntry
|
|||||||
*/
|
*/
|
||||||
internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparator) : InternalMap<K, V> {
|
internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparator) : InternalMap<K, V> {
|
||||||
|
|
||||||
private var backingMap: dynamic = js("Object.create(null)")
|
private var backingMap: dynamic = createJsMap()
|
||||||
override var size: Int = 0
|
override var size: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun put(key: K, value: V): V? {
|
override fun put(key: K, value: V): V? {
|
||||||
val hashCode = equality.getHashCode(key)
|
val hashCode = equality.getHashCode(key)
|
||||||
val chain = getChainOrNull(hashCode)
|
val chainOrEntry = getChainOrEntryOrNull(hashCode)
|
||||||
if (chain == null) {
|
if (chainOrEntry == null) {
|
||||||
// This is a new chain, put it to the map.
|
// This is a new chain, put it to the map.
|
||||||
backingMap[hashCode] = arrayOf(SimpleEntry(key, value))
|
backingMap[hashCode] = SimpleEntry(key, value)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Chain already exists, perhaps key also exists.
|
if (chainOrEntry !is Array<*>) {
|
||||||
val entry = chain.findEntryInChain(key)
|
// It is an entry
|
||||||
if (entry != null) {
|
val entry: SimpleEntry<K, V> = chainOrEntry
|
||||||
return entry.setValue(value)
|
if (equality.equals(entry.key, key)) {
|
||||||
|
return entry.setValue(value)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
backingMap[hashCode] = arrayOf(entry, SimpleEntry(key, value))
|
||||||
|
size++
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Chain already exists, perhaps key also exists.
|
||||||
|
val chain: Array<MutableEntry<K, V>> = chainOrEntry
|
||||||
|
val entry = chain.findEntryInChain(key)
|
||||||
|
if (entry != null) {
|
||||||
|
return entry.setValue(value)
|
||||||
|
}
|
||||||
|
chain.asDynamic().push(SimpleEntry(key, value))
|
||||||
}
|
}
|
||||||
chain.asDynamic().push(SimpleEntry(key, value))
|
|
||||||
}
|
}
|
||||||
size++
|
size++
|
||||||
// structureChanged(host)
|
// structureChanged(host)
|
||||||
@@ -63,29 +78,42 @@ internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparat
|
|||||||
|
|
||||||
override fun remove(key: K): V? {
|
override fun remove(key: K): V? {
|
||||||
val hashCode = equality.getHashCode(key)
|
val hashCode = equality.getHashCode(key)
|
||||||
val chain = getChainOrNull(hashCode) ?: return null
|
val chainOrEntry = getChainOrEntryOrNull(hashCode) ?: return null
|
||||||
for (index in 0..chain.size-1) {
|
if (chainOrEntry !is Array<*>) {
|
||||||
val entry = chain[index]
|
val entry: MutableEntry<K, V> = chainOrEntry
|
||||||
if (equality.equals(key, entry.key)) {
|
if (equality.equals(entry.key, key)) {
|
||||||
if (chain.size == 1) {
|
deleteProperty(backingMap, hashCode)
|
||||||
chain.asDynamic().length = 0
|
|
||||||
// remove the whole array
|
|
||||||
deleteProperty(backingMap, hashCode)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// splice out the entry we're removing
|
|
||||||
chain.asDynamic().splice(index, 1)
|
|
||||||
}
|
|
||||||
size--
|
size--
|
||||||
// structureChanged(host)
|
|
||||||
return entry.value
|
return entry.value
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val chain: Array<MutableEntry<K, V>> = chainOrEntry
|
||||||
|
for (index in 0..chain.size - 1) {
|
||||||
|
val entry = chain[index]
|
||||||
|
if (equality.equals(key, entry.key)) {
|
||||||
|
if (chain.size == 1) {
|
||||||
|
chain.asDynamic().length = 0
|
||||||
|
// remove the whole array
|
||||||
|
deleteProperty(backingMap, hashCode)
|
||||||
|
} else {
|
||||||
|
// splice out the entry we're removing
|
||||||
|
chain.asDynamic().splice(index, 1)
|
||||||
|
}
|
||||||
|
size--
|
||||||
|
// structureChanged(host)
|
||||||
|
return entry.value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clear() {
|
override fun clear() {
|
||||||
backingMap = js("Object.create(null)")
|
backingMap = createJsMap()
|
||||||
size = 0
|
size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +121,22 @@ internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparat
|
|||||||
|
|
||||||
override fun get(key: K): V? = getEntry(key)?.value
|
override fun get(key: K): V? = getEntry(key)?.value
|
||||||
|
|
||||||
private fun getEntry(key: K): MutableEntry<K, V>? =
|
private fun getEntry(key: K): MutableEntry<K, V>? {
|
||||||
getChainOrNull(equality.getHashCode(key))?.findEntryInChain(key)
|
val chainOrEntry = getChainOrEntryOrNull(equality.getHashCode(key)) ?: return null
|
||||||
|
if (chainOrEntry !is Array<*>) {
|
||||||
|
val entry: MutableEntry<K, V> = chainOrEntry
|
||||||
|
if (equality.equals(entry.key, key)) {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val chain: Array<MutableEntry<K, V>> = chainOrEntry
|
||||||
|
return chain.findEntryInChain(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun Array<MutableEntry<K, V>>.findEntryInChain(key: K): MutableEntry<K, V>? =
|
private fun Array<MutableEntry<K, V>>.findEntryInChain(key: K): MutableEntry<K, V>? =
|
||||||
firstOrNull { entry -> equality.equals(entry.key, key) }
|
firstOrNull { entry -> equality.equals(entry.key, key) }
|
||||||
@@ -107,23 +149,26 @@ internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparat
|
|||||||
val keys: Array<Int> = js("Object").keys(backingMap)
|
val keys: Array<Int> = js("Object").keys(backingMap)
|
||||||
var keyIndex = -1
|
var keyIndex = -1
|
||||||
|
|
||||||
var chain: Array<MutableEntry<K, V>>? = null
|
var chainOrEntry: dynamic = null
|
||||||
|
var isChain = false
|
||||||
var itemIndex = -1
|
var itemIndex = -1
|
||||||
var lastEntry: MutableEntry<K, V>? = null
|
var lastEntry: MutableEntry<K, V>? = null
|
||||||
|
|
||||||
private fun computeNext(): Int {
|
private fun computeNext(): Int {
|
||||||
if (chain != null) {
|
if (chainOrEntry != null && isChain) {
|
||||||
if (++itemIndex < chain!!.size)
|
val chainSize: Int = chainOrEntry.unsafeCast<Array<MutableEntry<K, V>>>().size
|
||||||
|
if (++itemIndex < chainSize)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++keyIndex < keys.size) {
|
if (++keyIndex < keys.size) {
|
||||||
chain = backingMap[keys[keyIndex]]
|
chainOrEntry = backingMap[keys[keyIndex]]
|
||||||
|
isChain = chainOrEntry is Array<*>
|
||||||
itemIndex = 0
|
itemIndex = 0
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chain = null
|
chainOrEntry = null
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +181,12 @@ internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparat
|
|||||||
|
|
||||||
override fun next(): MutableEntry<K, V> {
|
override fun next(): MutableEntry<K, V> {
|
||||||
if (!hasNext()) throw NoSuchElementException()
|
if (!hasNext()) throw NoSuchElementException()
|
||||||
val lastEntry = chain!![itemIndex]
|
val lastEntry = if (isChain) {
|
||||||
|
chainOrEntry.unsafeCast<Array<MutableEntry<K, V>>>()[itemIndex]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chainOrEntry.unsafeCast<MutableEntry<K, V>>()
|
||||||
|
}
|
||||||
this.lastEntry = lastEntry
|
this.lastEntry = lastEntry
|
||||||
state = -1
|
state = -1
|
||||||
return lastEntry
|
return lastEntry
|
||||||
@@ -152,9 +202,9 @@ internal class InternalHashCodeMap<K, V>(override val equality: EqualityComparat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getChainOrNull(hashCode: Int): Array<MutableEntry<K, V>>? {
|
private fun getChainOrEntryOrNull(hashCode: Int): dynamic {
|
||||||
val chain = backingMap[hashCode].unsafeCast<Array<MutableEntry<K, V>>?>()
|
val chainOrEntry = backingMap[hashCode]
|
||||||
return chain.takeIf { it !== undefined }
|
return if (chainOrEntry === undefined) null else chainOrEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,12 @@ internal interface InternalMap<K, V> : MutableIterable<MutableMap.MutableEntry<K
|
|||||||
fun put(key: K, value: V): V?
|
fun put(key: K, value: V): V?
|
||||||
fun remove(key: K): V?
|
fun remove(key: K): V?
|
||||||
fun clear(): Unit
|
fun clear(): Unit
|
||||||
|
|
||||||
|
fun createJsMap(): dynamic {
|
||||||
|
val result = js("Object.create(null)")
|
||||||
|
// force to switch object representation to dictionary mode
|
||||||
|
result["foo"] = 1;
|
||||||
|
deleteProperty(result, "foo")
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ import kotlin.collections.MutableMap.MutableEntry
|
|||||||
*/
|
*/
|
||||||
internal class InternalStringMap<K, V>(override val equality: EqualityComparator) : InternalMap<K, V> {
|
internal class InternalStringMap<K, V>(override val equality: EqualityComparator) : InternalMap<K, V> {
|
||||||
|
|
||||||
private var backingMap: dynamic = js("Object.create(null)")
|
private var backingMap: dynamic = createJsMap()
|
||||||
override var size: Int = 0
|
override var size: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ internal class InternalStringMap<K, V>(override val equality: EqualityComparator
|
|||||||
|
|
||||||
|
|
||||||
override fun clear() {
|
override fun clear() {
|
||||||
backingMap = js("Object.create(null)")
|
backingMap = createJsMap()
|
||||||
size = 0
|
size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user