Skip to content

Commit 2a1e668

Browse files
committed
feat: implement FixedInt annotation for fixed-length encoding (when changing default settings)
1 parent f5df7fd commit 2a1e668

3 files changed

Lines changed: 71 additions & 11 deletions

File tree

src/main/kotlin/com/eignex/kencode/Annotations.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,28 @@ annotation class VarInt
6262
@Retention(AnnotationRetention.RUNTIME)
6363
annotation class VarUInt
6464

65+
/**
66+
* Marks an `Int` or `Long` property to be encoded using **fixed-length** encoding.
67+
*
68+
* Behavior:
69+
* - Serialized densely using fixed bytes (4 bytes for Int, 8 bytes for Long).
70+
* - **Overrides** any global `PackedConfiguration` defaults (`defaultVarInt` or `defaultZigZag`).
71+
* - Applies only to `Int` and `Long` fields inside `PackedFormat` structures.
72+
*
73+
* Usage:
74+
* ```
75+
* @Serializable
76+
* data class Example(
77+
* @FixedInt val hash: Int, // always 4 bytes, even if defaultVarInt = true
78+
* @FixedInt val mask: Long // always 8 bytes
79+
* )
80+
* ```
81+
*/
82+
@SerialInfo
83+
@Target(AnnotationTarget.PROPERTY)
84+
@Retention(AnnotationRetention.RUNTIME)
85+
annotation class FixedInt
86+
6587
fun List<Annotation>.hasVarInt(): Boolean = any { it is VarInt }
6688
fun List<Annotation>.hasVarUInt(): Boolean = any { it is VarUInt }
89+
fun List<Annotation>.hasFixedInt(): Boolean = any { it is FixedInt }

src/main/kotlin/com/eignex/kencode/PackedDecoder.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,21 @@ class PackedDecoder(
200200

201201
override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int {
202202
val anns = descriptor.getElementAnnotations(index)
203+
val hasFixedInt = anns.hasFixedInt()
203204
val hasVarInt = anns.hasVarInt()
204205
val hasVarUInt = anns.hasVarUInt()
205206

206-
val zigZag = hasVarInt || (config.defaultZigZag && !hasVarUInt)
207-
val isVar = hasVarUInt || zigZag || config.defaultVarInt
207+
val isVar = when {
208+
hasFixedInt -> false
209+
hasVarInt || hasVarUInt -> true
210+
else -> config.defaultVarInt || config.defaultZigZag
211+
}
212+
213+
val zigZag = when {
214+
hasVarInt -> true
215+
hasVarUInt || hasFixedInt -> false
216+
else -> config.defaultZigZag
217+
}
208218

209219
return if (isVar) {
210220
val (raw, bytesRead) = PackedUtils.decodeVarInt(input, position)
@@ -217,11 +227,21 @@ class PackedDecoder(
217227

218228
override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long {
219229
val anns = descriptor.getElementAnnotations(index)
230+
val hasFixedInt = anns.hasFixedInt()
220231
val hasVarInt = anns.hasVarInt()
221232
val hasVarUInt = anns.hasVarUInt()
222233

223-
val zigZag = hasVarInt || (config.defaultZigZag && !hasVarUInt)
224-
val isVar = hasVarUInt || zigZag || config.defaultVarInt
234+
val isVar = when {
235+
hasFixedInt -> false
236+
hasVarInt || hasVarUInt -> true
237+
else -> config.defaultVarInt || config.defaultZigZag
238+
}
239+
240+
val zigZag = when {
241+
hasVarInt -> true
242+
hasVarUInt || hasFixedInt -> false
243+
else -> config.defaultZigZag
244+
}
225245

226246
return if (isVar) {
227247
val (raw, bytesRead) = PackedUtils.decodeVarLong(input, position)

src/main/kotlin/com/eignex/kencode/PackedEncoder.kt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ class PackedEncoder(
159159
@ExperimentalSerializationApi
160160
override fun encodeNotNullMark() {
161161
if (inStructure && isCollection) {
162-
// Inline not-null marker for collections (0 = present)
163162
PackedUtils.writeVarLong(0L, dataBuffer)
164163
} else if (!inStructure) {
165164
PackedUtils.writeVarLong(0L, output)
@@ -204,8 +203,6 @@ class PackedEncoder(
204203
nullableIndices = intArrayOf()
205204
}
206205

207-
// --- Element Encoding Delegates ---
208-
209206
private fun booleanPos(index: Int): Int {
210207
for (i in booleanIndices.indices) if (booleanIndices[i] == index) return i
211208
return -1
@@ -224,11 +221,21 @@ class PackedEncoder(
224221

225222
override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) {
226223
val anns = descriptor.getElementAnnotations(index)
224+
val hasFixedInt = anns.hasFixedInt()
227225
val hasVarInt = anns.hasVarInt()
228226
val hasVarUInt = anns.hasVarUInt()
229227

230-
val zigZag = hasVarInt || (config.defaultZigZag && !hasVarUInt)
231-
val isVar = hasVarUInt || zigZag || config.defaultVarInt
228+
val isVar = when {
229+
hasFixedInt -> false
230+
hasVarInt || hasVarUInt -> true
231+
else -> config.defaultVarInt || config.defaultZigZag
232+
}
233+
234+
val zigZag = when {
235+
hasVarInt -> true
236+
hasVarUInt || hasFixedInt -> false
237+
else -> config.defaultZigZag
238+
}
232239

233240
if (isVar) {
234241
val v = if (zigZag) PackedUtils.zigZagEncodeInt(value) else value
@@ -240,11 +247,21 @@ class PackedEncoder(
240247

241248
override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) {
242249
val anns = descriptor.getElementAnnotations(index)
250+
val hasFixedInt = anns.hasFixedInt()
243251
val hasVarInt = anns.hasVarInt()
244252
val hasVarUInt = anns.hasVarUInt()
245253

246-
val zigZag = hasVarInt || (config.defaultZigZag && !hasVarUInt)
247-
val isVar = hasVarUInt || zigZag || config.defaultVarInt
254+
val isVar = when {
255+
hasFixedInt -> false
256+
hasVarInt || hasVarUInt -> true
257+
else -> config.defaultVarInt || config.defaultZigZag
258+
}
259+
260+
val zigZag = when {
261+
hasVarInt -> true
262+
hasVarUInt || hasFixedInt -> false
263+
else -> config.defaultZigZag
264+
}
248265

249266
if (isVar) {
250267
val v = if (zigZag) PackedUtils.zigZagEncodeLong(value) else value

0 commit comments

Comments
 (0)