Coverage Summary for Class: Ghost_jvmKt (com.ghost.serialization)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| Ghost_jvmKt |
94.4%
(17/18)
|
68.8%
(22/32)
|
84.7%
(100/118)
|
98%
(295/301)
|
| Ghost_jvmKt$discoverRegistries$$inlined$Iterable$1 |
0%
(0/2)
|
|
| Ghost_jvmKt$discoverRegistries$1$1 |
100%
(3/3)
|
63.9%
(23/36)
|
86%
(37/43)
|
93.3%
(168/180)
|
| Total |
87%
(20/23)
|
66.2%
(45/68)
|
85.1%
(137/161)
|
96.3%
(463/481)
|
@file:OptIn(InternalGhostApi::class)
package com.ghost.serialization
import com.ghost.serialization.contract.GhostRegistry
import com.ghost.serialization.parser.GhostJsonReader
import com.ghost.serialization.parser.GhostJsonFlatReader
import com.ghost.serialization.parser.GhostJsonStringReader
import com.ghost.serialization.writer.GhostJsonFlatWriter
import com.ghost.serialization.writer.GhostJsonStringWriter
import com.ghost.serialization.writer.WriterSinkPair
import com.ghost.serialization.writer.FlatCharArrayWriter
import okio.BufferedSource
import java.util.ServiceLoader
import java.util.concurrent.ConcurrentHashMap
private val readerPool = ThreadLocal<GhostJsonReader>()
private val flatReaderPool = ThreadLocal<GhostJsonFlatReader>()
private val stringReaderPool = ThreadLocal<GhostJsonStringReader>()
private val sourceReaderPool = ThreadLocal<GhostJsonReader>()
@PublishedApi
internal val writerPool = ThreadLocal<WriterSinkPair>()
actual fun <T> runSynchronized(lock: Any, block: () -> T): T = synchronized(lock, block)
actual fun <K, V> createAtomicMap(): MutableMap<K, V> = ConcurrentHashMap()
/**
* Acquires the per-thread [WriterSinkPair], resets it for a fresh encode,
* and returns it. The pair survives across calls so the underlying
* [com.ghost.serialization.writer.FlatByteArrayWriter] grows once and stays warm.
*/
@PublishedApi
internal fun acquireFlatWriterPair(): WriterSinkPair {
val pair = writerPool.get()
?: WriterSinkPair()
.also { writerPool.set(it) }
pair.writer.reset()
pair.byteWriter.reset()
return pair
}
@PublishedApi
internal class WriterStringPair {
val charWriter = FlatCharArrayWriter()
val writer = GhostJsonStringWriter(charWriter)
}
@PublishedApi
internal val stringWriterPool = ThreadLocal<WriterStringPair>()
@PublishedApi
internal fun acquireStringWriterPair(): WriterStringPair {
val pair = stringWriterPool.get()
?: WriterStringPair().also { stringWriterPool.set(it) }
pair.writer.reset()
pair.charWriter.reset()
return pair
}
@InternalGhostApi
actual inline fun ghostInternalEncodeToString(
crossinline block: (GhostJsonStringWriter) -> Unit
): String {
val pair = acquireStringWriterPair()
block(pair.writer)
val result = String(
pair.charWriter.array,
0,
pair.charWriter.size
)
pair.charWriter.reset()
return result
}
@InternalGhostApi
actual inline fun ghostInternalEncodeWithWriter(
crossinline block: (GhostJsonFlatWriter) -> Unit
): ByteArray {
val pair = acquireFlatWriterPair()
block(pair.writer)
val result = pair.byteWriter.toByteArray()
pair.byteWriter.reset()
return result
}
@InternalGhostApi
actual inline fun ghostInternalEncodeAndDiscard(
crossinline block: (GhostJsonFlatWriter) -> Unit
) {
val pair = acquireFlatWriterPair()
block(pair.writer)
pair.byteWriter.reset()
}
@InternalGhostApi
actual inline fun ghostInternalEncodeAndDrainTo(
sink: okio.BufferedSink,
crossinline block: (GhostJsonFlatWriter) -> Unit
) {
val pair = acquireFlatWriterPair()
block(pair.writer)
sink.write(
pair.byteWriter.array,
0,
pair.byteWriter.size
)
pair.byteWriter.reset()
}
actual fun discoverRegistries(): Iterable<GhostRegistry> = Iterable {
object : Iterator<GhostRegistry> {
private var fastRegistries: MutableList<GhostRegistry>? = null
private var fastIterator: Iterator<GhostRegistry>? = null
private var fastChecked = false
private var slow: Iterator<GhostRegistry>? = null
override fun hasNext(): Boolean {
if (!fastChecked) {
fastChecked = true
val names = listOf(
Ghost.DEFAULT_REGISTRY_NAME,
Ghost.TEST_REGISTRY_NAME
)
fastRegistries = names.mapNotNull { name ->
runCatching {
Class.forName(name)
.getField(Ghost.INSTANCE_FIELD)
.get(null) as GhostRegistry
}.getOrNull()
}.toMutableList()
fastIterator = fastRegistries?.iterator()
}
if (fastIterator?.hasNext() == true) return true
if (slow == null) {
slow = runCatching {
ServiceLoader
.load(GhostRegistry::class.java)
.iterator()
}
.getOrDefault(
emptyList<GhostRegistry>()
.iterator()
)
}
return slow!!.hasNext()
}
override fun next(): GhostRegistry {
if (!hasNext()) throw NoSuchElementException()
if (fastIterator?.hasNext() == true) return fastIterator!!.next()
return slow!!.next()
}
}
}
actual fun <T> ghostInternalUseReader(
bytes: ByteArray,
block: (GhostJsonReader) -> T
): T {
val reader = readerPool.get()
?: GhostJsonReader(bytes)
.also { readerPool.set(it) }
reader.reset(bytes)
return block(reader)
}
actual fun <T> ghostInternalUseFlatReader(
bytes: ByteArray,
limit: Int,
block: (GhostJsonFlatReader) -> T
): T {
val reader = flatReaderPool.get()
?: GhostJsonFlatReader(bytes)
.also { flatReaderPool.set(it) }
reader.reset(bytes, limit)
return block(reader)
}
actual fun <T> ghostInternalUseSource(
source: BufferedSource,
block: (GhostJsonReader) -> T
): T {
// Separate pool from readerPool to prevent re-entrance corruption if the
// same thread nests a ByteArray read inside a streaming read.
val reader = sourceReaderPool.get()
?: GhostJsonReader(source)
.also { sourceReaderPool.set(it) }
// data in 8 KB segments on demand instead of loading the entire payload.
reader.reset(source)
return block(reader)
}
actual fun <T> ghostInternalUseStringReader(
json: String,
block: (GhostJsonStringReader) -> T
): T {
val reader = stringReaderPool.get()
?: GhostJsonStringReader(json)
.also { stringReaderPool.set(it) }
reader.reset(json)
return block(reader)
}