Экспериментальный самодельный StringInterner
This commit is contained in:
parent
22b384b2a5
commit
050dddcca7
@ -5,7 +5,9 @@ import com.github.benmanes.caffeine.cache.Caffeine
|
|||||||
import com.github.benmanes.caffeine.cache.Interner
|
import com.github.benmanes.caffeine.cache.Interner
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import com.google.gson.internal.bind.JsonTreeReader
|
import com.google.gson.internal.bind.JsonTreeReader
|
||||||
|
import it.unimi.dsi.fastutil.Hash
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.stb.STBImage
|
import org.lwjgl.stb.STBImage
|
||||||
@ -70,6 +72,7 @@ import ru.dbotthepony.kstarbound.util.WriteOnce
|
|||||||
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
||||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
import java.lang.ref.ReferenceQueue
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -805,3 +808,75 @@ class Starbound : ISBFileLocator {
|
|||||||
private val polyfill by lazy { loadInternalScript("polyfill") }
|
private val polyfill by lazy { loadInternalScript("polyfill") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class StringInterner(private val segmentBits: Int) : Interner<String>, Hash.Strategy<Any> {
|
||||||
|
class Ref(referent: String, queue: ReferenceQueue<String>) : WeakReference<String>(referent, queue) {
|
||||||
|
val hash = referent.hashCode()
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(a: Any?, b: Any?): Boolean {
|
||||||
|
if (a is String && b is Ref) return a == b.get()
|
||||||
|
if (a is Ref && b is String) return a.get() == b
|
||||||
|
return a === b
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(o: Any): Int {
|
||||||
|
return o.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val queue = ReferenceQueue<String>()
|
||||||
|
private val actualSegmentBits: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
var result = 0
|
||||||
|
|
||||||
|
for (i in 0 until segmentBits) {
|
||||||
|
result = result or (1.shl(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
actualSegmentBits = result
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cleaner = Runnable {
|
||||||
|
while (true) {
|
||||||
|
val ref = queue.remove() as Ref
|
||||||
|
val segment = segments[ref.hash and actualSegmentBits]
|
||||||
|
|
||||||
|
synchronized(segment) {
|
||||||
|
val removed = segment.remove(ref)
|
||||||
|
check(removed === ref) { "Expected to remove reference $ref from segment ${ref.hash and actualSegmentBits} (full hash: ${ref.hash}), but we removed $removed (removed hash: ${removed.hashCode()}, removed segment: ${removed.hashCode() and actualSegmentBits})" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val thread = Thread(cleaner, "String Interner Cleanup Thread")
|
||||||
|
|
||||||
|
init {
|
||||||
|
thread.priority = 2
|
||||||
|
thread.isDaemon = true
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val segments: Array<Object2ObjectOpenCustomHashMap<Any, Any>> = Array(1.shl(segmentBits)) { Object2ObjectOpenCustomHashMap(this) }
|
||||||
|
|
||||||
|
override fun intern(sample: String): String {
|
||||||
|
val hash = sample.hashCode()
|
||||||
|
val segment = segments[hash and actualSegmentBits]
|
||||||
|
|
||||||
|
synchronized(segment) {
|
||||||
|
val canonical = (segment[sample] as Ref?)?.get()
|
||||||
|
|
||||||
|
if (canonical != null) {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
val ref = Ref(sample, queue)
|
||||||
|
segment.put(ref, ref)
|
||||||
|
return sample
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user