Skip to content

Releases: juanchurtado1991/ghost-serializer

Ghost Serializer 1.3.1

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 10 Aug 03:54

Highlights

YAML compliance ~96%

Ghost YAML is now measured against the yaml-test-suite with a full offline harness (yamlComplianceMatrix).

  • Reader compliance: ~54% → 96.06% (151 → 268 of 279 valid cases with a JSON fixture — same denominator as matrix.yaml.info; 1.3.0 shipped YAML before the suite was measured)
  • Dozens of real parser bugs fixed (anchors/aliases, tags, block scalars, flow collections, comments, tabs, explicit keys, multi-line plain scalars, and silent wrong-tree cases)
  • Writer round-trip: 100% (321/321); remaining kaml-oracle gaps are oracle limitations, not Ghost bugs
  • Known remaining gaps are tracked by case ID in YamlTestSuiteDeviations.kt (no silent skips)
  • New YAML + JSON coverage-guided fuzzing (Jazzer) in the JVM CI path

Safari / Wasm Speed Test (#16)

On JavaScriptCore (Safari / iOS), Ghost.encodeToString no longer collapses on CharArray.concatToString.

  • JSC path: UTF-8 flat writer + browser TextDecoder
  • Chrome / V8: keeps the char writer (already fast)
  • Safari string Speed Test after fix: ~2.7× kotlinx.serialization
  • JVM / Android / Native unchanged

Also in 1.3.1

  • Spring + Retrofit collection bodies: top-level List / Set / Map unwrap (MVC + WebFlux, JSON + YAML); fix for List<String> / Map<String, String> falling through to Jackson
  • WKT string-channel overrides: nested Well-Known Types under textChannel = true avoid a full UTF-8 bridge + reparse
  • Hot-path allocation fixes: digit-walk HOFs no longer allocate on synthetic decode; SetSerializer resilient path avoids double allocation
  • Ghost.deserialize(bytes) { options } now uses the flat reader (same engine as plain deserialize(bytes))
  • Study manual regenerated: docs/Ghost-Serialization-Manual-1.3.1.pdf

Install

implementation("com.ghostserializer:ghost-serialization:1.3.1")

Gradle plugin: com.ghostserializer.ghost version 1.3.1

Full notes: CHANGELOG.md

👻 Ghost Serializer 1.3.0 — Unified JSON + YAML + Proto3 JSON runtime

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 28 Jul 06:35

This isn't an incremental bump from 1.2.7 — it's a rewrite of what "Ghost" ships as an artifact, plus the biggest decode-throughput jump in the project's history.

Highlights

  • 🔀 One runtime, three wire formats. JSON, YAML, and Proto3 JSON now live in a single ghost-serialization artifact. ghost-yaml, ghost-yaml-ktor, and ghost-protobuf are gone.
  • Decode throughput up to 1.2 GB/s. A rewritten decode hot path (in-order field prediction + SWAR scanning) pushed every channel well past 1.2.7 — String decode crosses 1 GB/s for the first time.
  • 🌐 Kotlin/Wasm everywhere. ghost-api, ghost-serialization, ghost-ktor, and the new Ghost Playground now ship wasmJs targets alongside JVM/Android/iOS.
  • 🕹️ New Ghost Playground — Compose Multiplatform (JVM + Wasm) site with a live JSON/YAML/Proto Studio and a browser speed test vs kotlinx.serialization and Moshi.
  • 🐛 Two silent data-corruption bugs fixed in the streaming reader and the WebFlux NDJSON decoder.

⚡ Performance: crossing 1 GB/s

Twitter macro fixture (631,514 bytes). Same 3 decode channels:

Decode 1.2.7 1.3.0 Δ 1.2.7→1.3.0 vs KSER vs Moshi
String 0.803 GB/s 1.229 GB/s · 514 µs/op +53% +71% +229%
Bytes 0.698 GB/s 1.052 GB/s · 600 µs/op +51% +148% +283%
Streaming 0.304 GB/s 0.529 GB/s · 1194 µs/op +74% +174% +24%

(1.2.7 GB/s derived from its own published ops/s × the 631,514-byte Twitter fixture — same fixture in both versions; 1.2.7 didn't track GB/s directly.)

Three profile-driven changes got us here, each A/B-verified and kept only when it won:

  1. In-order field prediction — machine-generated JSON lists fields in declaration order, so the reader checks the predicted next field first instead of hashing every key. Biggest single win.
  2. SWAR whitespace + key comparison — 8 bytes at a time (VarHandle wide load on JVM, scalar SWAR elsewhere).
  3. SWAR value-string scan with deferred pool hash — branch-free 8-byte scan for quotes/backslash/control/non-ASCII; the string-pool hash is now only computed for strings short enough to actually pool.

Encode got targeted wins too (ASCII-prefix fast path +10%, BMP Unicode fast path +13% on the string channel). Full multi-engine tables: benchmarks.md.

🔀 Unified runtime: JSON + YAML + Proto3 JSON

1.2.7 shipped three separate artifacts. 1.3.0 merges them into ghost-serialization:

// Before (1.2.x)
implementation("com.ghostserializer:ghost-yaml:1.2.x")
implementation("com.ghostserializer:ghost-yaml-ktor:1.2.x")
implementation("com.ghostserializer:ghost-protobuf:1.2.x")

// After — one runtime artifact
implementation("com.ghostserializer:ghost-serialization:1.3.0")
implementation("com.ghostserializer:ghost-ktor:1.3.0") // JSON + YAML + Proto adapters

Or apply id("com.ghostserializer.ghost") version "1.3.0" and let the Gradle plugin auto-inject adapters.

Full diff: v1.2.7...v1.3.0

Ghost Serialization v1.2.7

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 09 Jul 04:27

Production-ready, zero-allocation serialization engine for Kotlin Multiplatform. This release introduces support for Proto3 JSON mapping, brings robust HTTP integrations for Ktor, Retrofit, and Spring Boot, adds coverage-guided fuzz testing, and fixes critical edge-case serialization bugs.


📦 New Module: ghost-protobuf

We have introduced a new Kotlin Multiplatform module (com.ghostserializer:ghost-protobuf) which layers Proto3 JSON mapping rules on top of the zero-allocation JSON engine.

  • Multiplatform Targets: Android, iOS (Arm64 & Simulator), and JVM.
  • Well-Known Types (WKTs): Zero-allocation serializers for ProtoDuration, ProtoTimestamp (nanosecond precision, RFC3339 compliant), ProtoStruct, ProtoValue, ProtoAny, and scalar wrapper types (ProtoBoolValue, ProtoInt32Value, etc.).
  • Any Type Registry: Resolve ProtoAny dynamically via ProtoAnyRegistry using Ghost’s serializer registry.
  • Quotations & Unsigned Numeric Ranges: Unsigned 64-bit integer (uint64) backing via ULong with correct string quoting and boundaries support.

⚡ ProtoLab Benchmark (Sample App)

Added a new interactive UI page to the sample app to compare performance under real-world scenarios (using OpenLibrary API payloads) between:

  1. Ghost Core JSON
  2. Ghost Proto3 JSON
  3. KotlinX Serialization (Standard JSON & Proto3 JSON)

🔗 Framework Integrations

  • Spring Boot Starter: GhostHttpMessageConverter now auto-detects classes annotated with @GhostProtoSerialization and routes them dynamically through the GhostProtoJsonFlatReader.
  • Ktor Content Negotiation: Added GhostProtoContentConverter alongside bypass helpers (bodyGhostProto(), respondGhostProto()).
  • Retrofit Client: Added GhostProtoConverterFactory for directly reading Proto3 JSON payloads.

🛠️ Bug Fixes & Conformances

  • Validation against Reference Implementation: Introduced ProtoJsonConformanceTest cross-checking output against Google's protobuf-java JsonFormat.Printer to ensure 100% compliance.
  • Fixed Sign Loss on Durations: Fixed an issue where negative durations under one second (e.g., -0.5s) lost their sign.
  • Long.MIN_VALUE Edge Case: Corrected negative space overflow in boundary numeric conversions that previously corrupted Long.MIN_VALUE into "-0".
  • Fractional Seconds Widths: Duration and Timestamp now correctly pad fractional seconds to exactly 3, 6, or 9 digits as required by the Proto3 JSON specification.
  • Base64 Out-of-Bounds: Fixed potential ArrayIndexOutOfBoundsException when parsing invalid Base64 streams with non-Latin-1 characters.

💡 API & Internals Changes

  • Subclassing Support: Opened GhostJsonFlatReader for extension so that custom/protocol-specific readers (like GhostProtoJsonFlatReader) can override parsing behavior without duplicating the buffer implementation.
  • CI Stability: Resolved a CI Gradle runner GPG signing exception during JVM unit test suites on host-clean runners.

👻 Ghost Serializer — Release v1.2.4 (Hotfix)

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 01 Jul 00:08

We are releasing v1.2.4 as an urgent hotfix addressing critical compilation and runtime crashes when matching JSON field names. All users are strongly encouraged to upgrade immediately.

🚀 What's Fixed

Perfect Hash Collision Resolution in KSP & Runtime (Issue #10)

Previously in v1.2.3 and below, classes containing properties of the exact same character length that also shared overlapping prefixes/suffixes (e.g., modelCode vs modelName, dateCreated vs dateUpdated, or inviterUsername vs inviteeUsername) could trigger identical key hashes in the perfect hashing algorithm.
This resulted in:

  • Compile-time KSP failures due to the generator being unable to resolve a collision-free multiplier.
  • Runtime deserialization crashes (GhostJsonException: Expected '"') or silent data mapping corruption when fields resolved to the same slot during O(1) matching.
    How we fixed it:
    We refactored PerfectHashFinder (compiler) and JsonReaderOptions (runtime) to utilize a zero-allocation polynomial accumulation while loop (hash multiplier of 31) over all remaining bytes of the field name upon detecting a collision. This completely resolves collision overlapping, guaranteeing correct field dispatch with zero extra memory overhead.

📦 Dependency Info

[versions]
ghost = "1.2.4"
ksp   = "2.1.10-1.0.31"
[libraries]
ghost-api           = { module = "com.ghostserializer:ghost-api", version.ref = "ghost" }
ghost-serialization = { module = "com.ghostserializer:ghost-serialization", version.ref = "ghost" }
ghost-compiler      = { module = "com.ghostserializer:ghost-compiler", version.ref = "ghost" }
plugins {
    id("com.google.devtools.ksp") version "2.1.10-1.0.31"
    id("com.ghostserializer.ghost") version "1.2.4"
}

👻 Ghost Serializer v1.2.3

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 30 Jun 02:16

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")
}

Ghost Serialization v1.2.2

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 23 Jun 03:30

👻 Ghost Serialization v1.2.2 — Kotlin 2.1.10, KSP2 Support & Single-Pass Optimization

We are thrilled to release v1.2.2 of Ghost Serialization! This release brings full compatibility with Kotlin 2.1.10 and KSP2, alongside key performance optimizations that solidify Ghost as the fastest JSON engine for Kotlin Multiplatform.
With these updates, Ghost sweeps 1st place in all 6 JSON benchmark categories (Decode/Encode × String/Bytes/Streaming) on the Twitter macro dataset.

⚡ Key Highlights

  • Kotlin 2.1.10 & KSP2 Support: Complete migration to KSP 2.1.10-1.0.31 with KSP2 enabled (ksp.useKSP2=true) for faster incremental compiler builds.
  • Single-Pass String Scan (GhostJsonStringReader): Combines key boundary detection and perfect hashing in a single unrolled loop over raw memory, cutting key-matching memory reads in half.
  • Zero-Allocation String Decoding: Replaces array allocation routines with intrinsic character access (rawData[index].code), boosting String decoding performance to +32.1% faster than Kotlinx Serialization (KSer) with 69.6% less memory.
  • Ktor Server & Client Extensions: Integrated direct byte-first respondGhost (Server) and bodyGhost (Client) functions to bypass Ktor pipeline overhead entirely.

📋 Full Changelog

🚀 Performance

  • Single-Pass String Scan: Eliminated a redundant double-scan in the hot path of internalSelect. The new findClosingQuoteWithHash combines locating the closing quote and computing the 4-byte dispatch hash into a single loop, cutting key matching reads in half.

🛠️ Optimizations

  • Adapter Integration (Ktor, Retrofit, Spring Boot): Migrated Ktor, Retrofit, and Spring Boot adapters to use the new cached-serializer public APIs (Ghost.encodeToBytes and Ghost.deserialize), removing internal overhead.
  • RandomAccess List Loops: Replaced default Iterator-based loops with index-based loops in ListSerializer, IntArraySerializer, and LongArraySerializer for RandomAccess collections (like ArrayList) to avoid iterator heap allocations under heavy loops.
  • Map Entry-Set Iteration: Optimized MapSerializer to iterate entries directly instead of performing double hash lookups via key sets.
  • Fast-Path ASCII String Writer Scans: Refactored ASCII scans in GhostJsonStringWriter with hoisted local registers for native bulk copying.
  • Dynamic String Writer Heap Sizing: Reduced the default initial capacity of FlatCharArrayWriter from 8 KB to 1 KB (2 KB heap) and fine-tuned mobile buffer retention heuristics to lower memory footprint on Android and iOS.
  • Streaming Segment-Buffering: Implemented an internal 8 KB segment buffer directly in StreamingGhostSource to copy and hold active Okio segments, bypassing virtual dispatch.

✨ Added

  • Ktor Server & Client Direct Serialization: Added high-performance respondGhost (Server) and bodyGhost (Client) extensions, bypassing Ktor's ContentNegotiation pipeline.
  • Cached Serializer Overloads: Exposed new public overloads for encodeToString, encodeToBytes, deserialize, and deserializeStreaming that accept pre-resolved GhostSerializer<T> parameters.
  • Native String Reader Opt-in (ghost.textChannel): GhostJsonStringReader overloads are now opt-in via KSP configuration. When disabled (default), the string dispatch table is omitted, saving 4 KB of memory per DTO.

🧹 Refactored

  • Zero Magic Strings & Numbers: Centralized all template strings, error messages, and control identifiers into GhostEmitterConstants and GhostJsonConstants.
  • KMP Deprecations Cleanup: Replaced deprecated platform constructors with standard Kotlin Multiplatform CharArray.concatToString().

📦 Quick Start

# gradle/libs.versions.toml
[versions]
ghost = "1.2.2"
ksp   = "2.1.10-1.0.31"
[libraries]
ghost-api           = { module = "com.ghostserializer:ghost-api", version.ref = "ghost" }
ghost-serialization = { module = "com.ghostserializer:ghost-serialization", version.ref = "ghost" }
ghost-compiler      = { module = "com.ghostserializer:ghost-compiler", version.ref = "ghost" }

Ghost Serialization 1.2.1

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 01 Jun 16:43

This release resolves a critical class-loading NullPointerException during cold-start JIT prewarming, introduces detailed memory profiling (KB/op) using ThreadMXBean into the Twitter macro-dataset benchmarks, and establishes automated deep-equivalence validation guaranteeing 100% data fidelity with zero-data-loss serialization roundtrips.


🚀 What's New

🛠️ Twitter Macro-Dataset Memory Profiling

  • Twitter Benchmark Memory Metrics: Integrated JVM ThreadMXBean memory tracking to profile real-time allocated memory per operation (KB/op) in the Twitter Macro Dataset benchmark.

    💡 Highlight: Demonstrates up to 6.5x memory reduction when using direct bytes.

  • Special Features Twitter Tests: Included new integration tests verifying Ghost's advanced structural features (@GhostFlatten, @GhostWrap, and @GhostIgnore) directly on Twitter-like production payloads with flawless serialization roundtrips.

🛠️ What's Changed

🛡️ Core Stability & Class-Loading Fixes

  • Contextual Serializers Class-Loading NullPointerException: Resolved a critical NPE during class initialization of KSP-generated serializers (like ContextualModelSerializer). When Ghost.prewarm() loaded the default registry, serializers containing external types loaded their static fields early and threw NPEs if their contextual serializers weren't already registered.

    Fix: Added support for the pre-registration of manual registries before calling Ghost.prewarm().

⚙️ Twitter Benchmark Refactoring

  • Twitter Benchmark Codebase Refactoring: Refactored the benchmark suite by cleanly extracting configurations, engines, data models, and the Twitter macro benchmark into BenchmarkModels.kt and TwitterBenchmark.kt, dramatically reducing the size of GhostBenchmark.kt and removing all compiler warnings.
  • 100% Data Fidelity Guarantee: Added robust, automated deep-equivalence testing (GhostTwitterReproductionTest.kt) verifying 100% exact structural parity with Kotlinx Serialization and complete zero-data-loss serialization roundtrips over the entire Twitter macro dataset.

📚 Documentation

  • Best Practices: Updated README.md and CHANGELOG.md with complete guides on The Byte-First Philosophy (advocating direct byte-array parsing in network layers over UTF-16 String conversions to achieve up to 65% faster deserialization and 6.5x memory reduction).
  • Roadmap: Added detailed explanations of streaming decode trade-offs and outlined upcoming segment-buffering solutions.

Ghost Serialization 1.2.0 Latest

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 30 May 09:20

This release introduces native, declarative network annotations (@GhostStrict and @GhostCoerce) across all major HTTP integrations (Retrofit, Spring Boot, and Ktor), implements critical security and boundary bug fixes with zero-allocation performance preservation, and recovers maximum lenient parsing throughput by default in all network adapters.


🚀 What's New

🛠️ Declarative Network Customization

  • Native Declarative Annotations (@GhostStrict & @GhostCoerce): Shipped new dynamic annotations for elegant, declarative, and zero-allocation parsing customization:
    • Retrofit: Fully supports @GhostStrict and @GhostCoerce on API service interface methods to customize parser configurations dynamically per endpoint.
    • Spring Boot (Spring WebMvc): Integrates native thread-safe annotation scanning using RequestBodyAdvice. Supports @GhostStrict and @GhostCoerce at the controller class, endpoint method, or @RequestBody parameter levels out-of-the-box.
    • Ktor (KMP): Enhanced the GhostContentConverter in ghost-ktor with a custom configurer lambda parameter, enabling developers to dynamically tune or enforce strict/coerced settings directly in their KMP ContentNegotiation pipelines.

🛠️ What's Changed

🛡️ Core Security & Resilience Fixes

  • Scientific Notation Exponent Integer Overflow: Fixed a vulnerability in parseExponentValue for flat and streaming readers by clamping exponent values exceeding 1000 to prevent integer overflows.
  • Geometric Capacity Overflow Protection: Fixed a potential buffer overflow vulnerability in FlatByteArrayWriter.ensureCapacity by safely catching integer overflows and clamping growth boundaries to Int.MAX_VALUE.
  • Dynamic Key Hash Collision mixing: Eliminated perfect hash collision vulnerabilities in JsonReaderOptions and reader subsystems by dynamically detecting perfect key collisions at initialization and conditionally applying a branchless last-byte mix.
  • Serializer Delegation Config Preservation: Fixed a configuration propagation leak in the GhostSerializer.deserialize(GhostJsonFlatReader) default delegation method by copying all configuration attributes (e.g. strictMode, coerceStringsToNumbers) to the delegated streaming reader.
  • Negative Depth Boundary Safety: Protected reader depth decrement operations in endObject() and endArray() to stay non-negative, preventing bitmask corruption under malformed or resilient parsing.
  • Unbounded Surrogate Parser Checks: Fixed a boundary bug in GhostJsonReader and GhostJsonFlatReaderStrings where checking for trailing unicode surrogate pairs at the end of truncated strings caused IndexOutOfBoundsException instead of structured GhostJsonException.
  • Long Overflow Check: Fixed a silent overflow bug in calculateLongWithOverflowCheck where values matching Long.MIN_VALUE with additional trailing digits bypassed overflow verification.

⚙️ Parser & Writer Correctness

  • Strict Comma Validation: Enforced strict JSON comma checking in both streaming and flat readers using zero-allocation bitwise mask tracking. Bound strictly to strictMode = true to preserve maximum lenient parsing speed by default.
  • Iterative Comma Synchronization in hasNext(): Fixed a critical parser state leak in hasNext() where commaConsumedMask and needsCommaMask were not correctly cleared and tracked during iterative loops like skipValue().
  • Select Separator Comma Synchronization: Fixed internalSelect in both flat and streaming readers to correctly synchronize and clear commaConsumedMask when a separator comma is consumed, resolving unexpected comma errors in subsequent field decodes.
  • Strict Byte Checking: Resolved a project rule violation in expectByte by replacing the prohibited toChar() extension method with the direct Char(expected) constructor path.
  • Negative Zero Sign Loss: Corrected double formatting in GhostDoubleFormatter to preserve the minus sign for -0.0 by performing a zero-copy raw bits sign check.
  • Double Formatter Precision Threshold: Lowered MASSIVE_DOUBLE_THRESHOLD from 1e15 to 1e9 in GhostDoubleFormatter to guarantee standard-compliant shortest representation.
  • Leading Zero Shift Masking: Corrected a shift-masking bug in validateLeadingZero where non-digit characters in the ASCII range of 112..121 were validated as digits.
  • Flat Writer Infinite Loop: Fixed an infinite loop in ensureCapacity when FlatByteArrayWriter was initialized with zero capacity.
  • Primitive Collection Doubling: Fixed an ArrayIndexOutOfBoundsException in GhostIntList and GhostLongList when initialized with zero capacity.

⚡ Performance & Allocation Optimization

  • Zero-Allocation Stream Decoding: In StreamingGhostSource.decodeToString, eliminated a temporary Buffer allocation and segment copy. Now leverages Okio's snapshot(end).substring(start, end).utf8() directly, resulting in zero-copy range views of existing buffered segments.
  • Pool Tier Collision: Resolved a collision in GhostPools.kt where SCRATCH_BUFFER_SIZE (48 bytes) and TIER_SMALL (1024 bytes) shared the same pool slot. Added a dedicated scratch field to GhostPool to prevent buffer eviction leaks.

🐘 Compiler (KSP) & Gradle

  • Non-Nested Sealed Subclasses: Enhanced KSP generation to scan superTypes for sealed parents. This ensures that non-nested/top-level sealed subclasses correctly serialize and deserialize their type discriminator key.
  • Gradle Plugin KSP Setup Order: Refactored KSP compiler dependency injection in the Gradle plugin to be completely order-independent and reactive to KSP and KMP target application sequences.

📚 Documentation

  • Updated README.md and CHANGELOG.md with complete usage guides, annotations targets, and configuration examples for Retrofit, Spring Boot, and Ktor.

Ghost Serialization 1.1.19

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 27 May 22:33

This release brings important fixes to compiler code generation (KSP) and Gradle configurations, ensuring cleaner generated code with zero name-shadowing warnings, fixing redundant assertions, and streamlining Multiplatform Gradle setups.

🛠️ What's Changed

⚙️ Compiler & Code Generation (KSP)

  • subIndex name shadowing eliminated: In generated serializers for models using nested @GhostFlatten structures, the recursive generator (emitFlattenedGroup) now dynamically appends the recursion depth to local iteration variables (subIndex0, subIndex1, etc.). This resolves Kotlin compiler warnings about shadowed names.
  • Redundant non-null assertion (!!) removed: Removed !! from the copy template block TEMPLATE_IF_NOT_NULL_COPY (if (v0 != null) instance = instance.copy(...)), resolving redundant assertion warnings on non-null receivers when copying generated structures.
  • Cleaner compiler output: Demoted custom coder warnings from KSP (Detected custom coder for...) to informational logs (logger.info), reducing noise during standard Gradle builds.

🐘 Gradle & Multiplatform (KMP)

  • Automatic hierarchy template applied: Standardized multiplatform source-set wiring in ghost-serialization/build.gradle.kts using applyDefaultHierarchyTemplate(). This resolves the target configuration warning introduced in recent Kotlin Multiplatform updates and removes conflicting manual dependsOn structures for intermediate targets like nativeMain or iosMain.

📚 Documentation

  • Updated GHOST_MANUAL_EN.md references and regenerated the manual PDF build script settings to point to 1.1.19.
  • Bumped standard fallback version in the Gradle plugin to 1.1.19.

Ghost Serialization 1.1.18

Choose a tag to compare

@juanchurtado1991 juanchurtado1991 released this 25 May 21:01

What's Changed

Refactoring — KSP Compiler

  • Serialization pipeline decomposed into dedicated modules mirroring the deserialization split from 1.1.16:
    • BaseSerializeEmitter — shared property / collection / value-class emit logic
    • StandardSerializeEmitter — objects with < 40 properties
    • FragmentedSerializeEmitter — chunked emission for large models
  • PerfectHashFinder extracted — compile-time brute-force hash optimizer now lives in its own object, with a full runtime walkthrough in KDoc
  • FlattenOptionsGenerator extracted — recursive nested-options generation for @GhostFlatten is now isolated, removing quadratic path-lookup overhead from GhostCodeGenerator
  • Cleaner generated variable names — removed leading underscore prefix from generated locals and mask fields: _resultresult, _mask0mask0, _fieldNamefieldNameValue. Eliminates Kotlin name-shadowing lint warnings in generated code
  • Bitmask literals hoisted to private const valMASK_FIELDNAME / MASK_DEFAULTS_N companion constants replace inline 1L shl N magic numbers, improving JIT friendliness
  • validateRequiredFields helper — required-field validation extracted into a dedicated inline function in generated serializers
  • Dead constant cleanup in GhostEmitterConstants — 14 unused constants removed; new constants added for 1.1.18 emission patterns
  • TypeHelpers KDoc pass — all extension functions now have individual KDoc comments

Changed

  • Ghost.deserialize(BufferedSource) rewritten to use acquireScratchBuffer / releaseScratchBuffer instead of the removed GhostPayload helper — zero extra allocations
  • Removed deprecated aliases Ghost.serializeToBytes() and Ghost.serializeToString() — use encodeToBytes() / encodeToString() directly
  • Gradle plugin DEFAULT_VERSION bumped to 1.1.18

Documentation

  • Full KDoc added to Ghost, GhostRegistry, GhostJsonException, JsonReaderOptions, InternalGhostApi, and GhostDoubleFormatter (complete algorithm walkthrough)
  • API Reference appendix added to the English manual covering all public types and methods
  • PDF manual regeneratedGhost-Serialization-Manual-1.1.18.pdf (46 pages)
  • Build scripts added under scripts/ for reproducible PDF generation

Fixed

  • CI — iOS: test-ios job now trusts the Gradle exit code for Kotlin/Native tests, preventing false-positive failures when K/N output formatting changes

Upgrade from 1.1.17

# gradle/libs.versions.toml
ghost = "1.1.18"