We are excited to announce the release of Ghost Serializer v1.2.3! This version introduces massive memory footprint optimizations for small/medium models, native Android UI thread pooling enhancements, native ByteArray support, and resolves several compiler edge-cases and limits.
Our complete Kotlin Multiplatform verification suite has grown to 825/825 passing tests!
⚡ Performance & Memory Optimizations
- Dynamic Perfect Hash Table Sizing: Instead of allocating a fixed
1024-size dispatch table for every class, the compiler now starts searching for a collision-free perfect hash at size 128 and scales up (256, 512, 1024, etc.) only on demand. This reduces the runtime memory footprint of the dispatch table by 50% to 87.5% for small and medium-sized models.
- Android ThreadLocal UI Thread Bypass: Optimized
getLocalPool() on Android. By caching the main UI thread reference on first access and performing a fast identity comparison (===), we now completely bypass the ThreadLocal.get() map lookup on the UI thread.
- String Pool Cache Locality: Added a contiguous primitive
IntArray (stringPoolHashes) to both GhostJsonFlatReader and GhostJsonStringReader. This keeps string pool miss lookups inside the CPU's L1 data cache and avoids dereferencing cold String object references.
🛠️ Bug Fixes
- Perfect Hash Table Scaling for Large Models: Refactored
PerfectHashFinder.kt and JsonReaderOptions.kt to support dynamic table sizes up to 8192. This resolves KSP processing failures for large models (like the 100-field CollisionModel) that could not find a perfect hash at the default size.
- Android JVM Unit Test Looper Mocking: Wrapped
Looper.getMainLooper() in GhostPools.android.kt in a try-catch block to prevent Method getMainLooper in android.os.Looper not mocked crashes when executing Android unit tests in a pure JVM environment.
classDeclaration Inaccessible in GhostCodeGenerator: Declared the classDeclaration constructor parameter as a val, making it visible to annotation-reading helpers inside buildSerializerObject().
@GhostFallback Support for Enum Deserialization: Enums annotated with @GhostFallback no longer throw GhostJsonException on an unrecognized ordinal. The compiler now reads the annotation and emits an else -> branch pointing to the marked fallback constant.
- Auto-
UNKNOWN Fallback for Enums: If an enum class has a constant named UNKNOWN (any case variation), the compiler now automatically generates a fallback to it without requiring the @GhostFallback annotation.
- KSP Duplicate Property Collection on Interface/Superclass Override (Issue #4):
getAllProperties() returns both the original and the overriding declaration when a data class overrides an interface property, causing the same JSON field to be registered twice. Fixed by filtering out any property for which findOverridee() returns a non-null result before building property models.
StackOverflowError in emitFlattenedGroup on Path-Length Mismatch (Issue #5): When colliding @GhostFlatten / @WrappedKeys paths have different depths, pathIndex could exceed the shorter path's length, leading to infinite recursion. Fixed by using >= for the single-property leaf termination check and adding an explicit guard that throws a clear IllegalStateException.
➕ Added
ByteArray Field Type Support: Fields declared as ByteArray are now serialized by writing the pre-encoded bytes directly into the JSON stream via rawValue(), and deserialized by capturing the raw token span via captureRawJsonBytes(). Adds GhostJsonReaderCapture, GhostJsonFlatReaderCapture, and GhostJsonStringReaderCapture for all three reader flavors.
📦 Installation (Gradle)
plugins {
id("com.google.devtools.ksp") version "2.1.10-1.0.31"
id("com.ghostserializer.ghost") version "1.2.3"
}
dependencies {
// Core Runtime & Platform Adapters
implementation("com.ghostserializer:ghost-serialization:1.2.3")
implementation("com.ghostserializer:ghost-ktor:1.2.3")
implementation("com.ghostserializer:ghost-retrofit:1.2.3")
}