Экспериментальный самодельный 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.google.gson.*
|
||||
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.Object2ObjectOpenCustomHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.stb.STBImage
|
||||
@ -70,6 +72,7 @@ import ru.dbotthepony.kstarbound.util.WriteOnce
|
||||
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import java.io.*
|
||||
import java.lang.ref.ReferenceQueue
|
||||
import java.lang.ref.WeakReference
|
||||
import java.text.DateFormat
|
||||
import java.time.Duration
|
||||
@ -805,3 +808,75 @@ class Starbound : ISBFileLocator {
|
||||
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