KEncode produces short, predictable text payloads for environments with strict character or length limits such as URLs, file names, Kubernetes labels, and log keys.
KEncode provides three standalone entry points:
- ByteEncoding text codecs: Base62, Base36, Base64, and Base85 encoders for raw binary data.
- PackedFormat: A binary format serializer that supports nested objects, lists, and maps. It uses bitsets for booleans and nullability to minimize overhead.
- EncodedFormat: A string format serializer that wraps the above to produce small deterministic string identifiers.
dependencies {
implementation("com.eignex:kencode:1.2.0")
}For PackedFormat and EncodedFormat you also need to load the
kotlinx.serialization plugin and core library.
Minimal example using the default EncodedFormat (Base62 + PackedFormat):
@Serializable
data class Payload(
@PackedType(PackedIntegerType.DEFAULT) val id: ULong, // low numbers are compacted
@PackedType(PackedIntegerType.SIGNED) val delta: Int, // zigzagged to compact small negatives
val urgent: Boolean, // Packed into bitset
val handled: Instant?, // Nullability tracked via bitset
val type: PayloadType
)
enum class PayloadType { TYPE1, TYPE2, TYPE3 }
val payload = Payload(123u, -2, true, null, PayloadType.TYPE1)
val encoded = EncodedFormat.encodeToString(payload)
// > 0fiXYI (that's it, this specific payload fits in 4 raw bytes)
val decoded = EncodedFormat.decodeFromString<Payload>(encoded)PackedFormat is a BinaryFormat designed to produce the smallest feasible payloads for Kotlin classes by moving structural metadata into a compact header.
- Bit-Packing: Booleans and nullability markers are stored in a single bit-header (about 1 bit per field).
- VarInts: Int/Long fields can be optimized using
@PackedType(PackedIntegerType.DEFAULT)(unsigned varint) or@PackedType(PackedIntegerType.SIGNED)(ZigZag) annotations. If you already usekotlinx-serialization-protobuf,@ProtoTypeannotations are recognized automatically as a fallback. - Full Graph Support: Handles nested objects, lists, maps, and polymorphism recursively. While this is supported it will not produce as compact representations as flat structures that can pack all metadata into the same header.
val compactFormat = PackedFormat {
defaultEncoding = PackedIntegerType.SIGNED
serializersModule = myCustomModule
}
val bytes = compactFormat.encodeToByteArray(payload)EncodedFormat provides a StringFormat API that produces short tokens by composing three layers:
- Binary Layer: PackedFormat (default) or ProtoBuf (recommended for cross-language compatibility).
- Transform Layer: Optional
PayloadTransformapplied after serialization. UseCompactZerosto strip leading zero bytes,Checksum.asTransform()for integrity checks, or supply your own for encryption or error-correcting codes. Chain multiple transforms withPayloadTransform.then. - Text Layer: Base62 (default), Base36, Base64, or Base85.
val customFormat = EncodedFormat {
codec = Base36 // Use Base36 instead of Base62 (for lowercase)
checksum = Crc16 // Convenience shorthand for transform = Crc16.asTransform()
binaryFormat = ProtoBuf // Use ProtoBuf instead of PackedFormat
}
val token = customFormat.encodeToString(payload)
// Chain transforms: strip leading zeros, then append checksum
val withBoth = EncodedFormat {
transform = CompactZeros.then(Crc16.asTransform())
}KEncode includes standalone codecs for byte-to-text conversion. All implementations support custom alphabets.
- Base62 / Base36: Uses fixed-block encoding for predictable lengths without padding. Main use is to have 100% alpha-numeric output, with or without upper-case.
- Base85: High-density encoding (4 bytes to 5 characters).
- Base64 / Base64UrlSafe: RFC 4648 compatible.
Wrap a cipher as a PayloadTransform and pass it to EncodedFormat:
@Serializable
data class SecretPayload(val id: Long)
val encryptingTransform = object : PayloadTransform {
override fun encode(data: ByteArray): ByteArray = cipher.encrypt(data)
override fun decode(data: ByteArray): ByteArray = cipher.decrypt(data)
}
val secureFormat = EncodedFormat {
transform = encryptingTransform
}
val token = secureFormat.encodeToString(SecretPayload.serializer(), payload)
val decoded = secureFormat.decodeFromString(SecretPayload.serializer(), token)See Examples for a BouncyCastle demo.