Coverage Summary for Class: GhostSerializer (com.ghost.serialization.contract)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| GhostSerializer$DefaultImpls |
87.5%
(7/8)
|
|
95%
(38/40)
|
89.9%
(152/169)
|
| Total |
87.5%
(7/8)
|
|
95%
(38/40)
|
89.9%
(152/169)
|
package com.ghost.serialization.contract
import com.ghost.serialization.InternalGhostApi
import com.ghost.serialization.ghostInternalEncodeAndDrainTo
import com.ghost.serialization.ghostInternalEncodeWithWriter
import okio.BufferedSink
import okio.BufferedSource
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.GhostJsonWriter
/**
* Base contract for high-performance JSON serializers.
*
* Implementations are typically generated by the Ghost KSP compiler plugin
* to provide reflection-free serialization and deserialization.
*
* # Two-writer design
*
* Implementations must provide overloads of `serialize(writer, value)` for
* [GhostJsonWriter] (streaming I/O), [GhostJsonFlatWriter] (in-memory bytes),
* and [GhostJsonStringWriter] (in-memory chars).
*/
@OptIn(InternalGhostApi::class)
interface GhostSerializer<T> {
/** The unique name of the model in the Ghost registry. */
val typeName: String
/**
* Whether this serializer supports resilient parsing (skipping malformed items
* when used in collections).
*/
val isResilient: Boolean get() = false
/**
* Whether this serializer was generated for a `@GhostProtoSerialization` class, applying
* proto3 canonical JSON mapping rules (quoted int64/uint64, Base64 `bytes`, default-value
* omission on serialize) rather than plain `@GhostSerialization` rules.
*
* Framework integrations that share one dispatch surface for both flavors (e.g. Spring's
* globally-registered `HttpMessageConverter`) can check this to decide whether to route
* reads through a proto3-lenient reader — the annotation itself is `BINARY`-retained and
* not visible via runtime reflection, so this is the supported way to detect it at runtime.
*/
val isProto: Boolean get() = false
/**
* Serializes [value] into the provided [sink] using the fast in-memory
* [GhostJsonFlatWriter] and a single bulk drain. See `Ghost.serialize`
* for the full rationale.
*/
fun serialize(sink: BufferedSink, value: T) {
ghostInternalEncodeAndDrainTo(sink) { writer ->
serialize(writer, value)
}
}
/**
* Serializes [value] using the streaming [writer] (segmented [okio.Buffer]
* under the hood). Used by `Ghost.serialize(sink, value)`.
*/
fun serialize(writer: GhostJsonWriter, value: T)
/**
* Serializes [value] using the in-memory [writer] (contiguous
* [com.ghost.serialization.writer.FlatByteArrayWriter] under the hood).
* Used by `Ghost.encodeToString` / `Ghost.encodeToBytes` to bypass Okio
* segment overhead on the synchronous in-memory encode path.
*/
fun serialize(writer: GhostJsonFlatWriter, value: T)
/**
* Serializes [value] using the in-memory text [writer] (contiguous
* [com.ghost.serialization.writer.FlatCharArrayWriter] under the hood).
* Used by `Ghost.encodeToString` to write characters directly.
*/
fun serialize(writer: GhostJsonStringWriter, value: T) {
val bytes = ghostInternalEncodeWithWriter { flatWriter ->
serialize(flatWriter, value)
}
writer.rawValue(bytes)
}
/** Deserializes a new instance of [T] from the [source]. */
fun deserialize(source: BufferedSource): T {
val reader = GhostJsonFlatReader(source.readByteArray())
return deserialize(reader)
}
/** Deserializes a new instance of [T] using a specialized streaming [reader]. */
fun deserialize(reader: GhostJsonReader): T
/** Deserializes a new instance of [T] using a specialized flat in-memory [reader]. */
fun deserialize(reader: GhostJsonFlatReader): T {
val delegatedReader = GhostJsonReader(reader.rawData).also {
it.position = reader.position
it.limit = reader.limit
it.strictMode = reader.strictMode
it.coerceStringsToNumbers = reader.coerceStringsToNumbers
it.coerceBooleans = reader.coerceBooleans
it.maxDepth = reader.maxDepth
it.maxCollectionSize = reader.maxCollectionSize
}
val result = deserialize(delegatedReader)
reader.position = delegatedReader.position
reader.nextTokenByte = -1
return result
}
/** Deserializes a new instance of [T] using a specialized string [reader]. */
fun deserialize(reader: GhostJsonStringReader): T {
val bytes = reader.ensureUtf8Bytes()
val flatReader = GhostJsonFlatReader(bytes).also {
it.position = reader.charPositionToBytePosition(reader.position)
it.limit = bytes.size
it.strictMode = reader.strictMode
it.coerceStringsToNumbers = reader.coerceStringsToNumbers
it.coerceBooleans = reader.coerceBooleans
it.maxDepth = reader.maxDepth
it.maxCollectionSize = reader.maxCollectionSize
it.materializeRawJsonCaptures = true
}
val result = deserialize(flatReader)
reader.position = reader.bytePositionToCharPosition(flatReader.position)
reader.nextTokenByte = -1
return result
}
/**
* Optional warm-up cycle to trigger JIT (Just-In-Time) or ART optimization
* for critical parsing paths.
*/
fun warmUp() {}
}