From b774cf17ce9ca82630e2c9028befa979620eace2 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:23:07 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf(serializer):=20us?= =?UTF-8?q?e=20VarHandle=20in=20JavaUUIDSerializer=20and=20validate=20inpu?= =?UTF-8?q?t=20length?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace ByteBuffer.wrap with a big-endian byteArrayViewVarHandle for packing and unpacking the UUID halves; the bytes written are unchanged - reject decoded byte arrays that are not exactly 16 bytes long with an IllegalArgumentException instead of a BufferUnderflowException or silent truncation - give the descriptor its own serial name "surf.api.UUID" instead of reusing the ByteArray descriptor --- .../java/uuid/JavaUUIDSerializer.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer.kt index 17ab9a7af..729d0e0be 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer.kt @@ -3,32 +3,39 @@ package dev.slne.surf.api.core.serializer.java.uuid import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ByteArraySerializer +import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import java.nio.ByteBuffer +import java.lang.invoke.MethodHandles +import java.nio.ByteOrder import java.util.* typealias SerializableUUID = @Serializable(with = JavaUUIDSerializer::class) UUID object JavaUUIDSerializer : KSerializer { private val byteArraySerializer = ByteArraySerializer() - override val descriptor = byteArraySerializer.descriptor + private val longBigEndian = MethodHandles.byteArrayViewVarHandle( + LongArray::class.java, + ByteOrder.BIG_ENDIAN, + ) + + override val descriptor = SerialDescriptor("surf.api.UUID", byteArraySerializer.descriptor) override fun serialize(encoder: Encoder, value: UUID) { val bytes = ByteArray(16) - val buffer = ByteBuffer.wrap(bytes) - buffer.putLong(value.mostSignificantBits) - buffer.putLong(value.leastSignificantBits) + longBigEndian.set(bytes, 0, value.mostSignificantBits) + longBigEndian.set(bytes, 8, value.leastSignificantBits) encoder.encodeSerializableValue(byteArraySerializer, bytes) } override fun deserialize(decoder: Decoder): UUID { val bytes = decoder.decodeSerializableValue(byteArraySerializer) - val buffer = ByteBuffer.wrap(bytes) - val mostSigBits = buffer.long - val leastSigBits = buffer.long + require(bytes.size == 16) { "UUID must be 16 bytes, but was ${bytes.size}" } + + val mostSigBits = longBigEndian.get(bytes, 0) as Long + val leastSigBits = longBigEndian.get(bytes, 8) as Long return UUID(mostSigBits, leastSigBits) } -} \ No newline at end of file +} From e1f3f4b8ff5a6913aa6dd93994dd21b879b8ba86 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:23:42 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat(serializer):=20add=20JavaU?= =?UTF-8?q?UIDLongSerializer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - serialize UUID as a structure of two long elements "most" and "least" - add SerializableLongUUID typealias - decode elements in any order and fail with SerializationException when one is missing - update ABI dump for surf-api-core --- .../surf-api-core/api/surf-api-core.api | 9 +++ .../java/uuid/JavaUUIDLongSerializer.kt | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer.kt diff --git a/surf-api-core/surf-api-core/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index 2265baa8d..9c6cb0a6b 100644 --- a/surf-api-core/surf-api-core/api/surf-api-core.api +++ b/surf-api-core/surf-api-core/api/surf-api-core.api @@ -10457,6 +10457,15 @@ public final class dev/slne/surf/api/core/serializer/java/url/URLSerializer : ko public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/net/URL;)V } +public final class dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Ldev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/util/UUID; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/util/UUID;)V +} + public final class dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Ldev/slne/surf/api/core/serializer/java/uuid/JavaUUIDSerializer; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer.kt new file mode 100644 index 000000000..3474dc730 --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/serializer/java/uuid/JavaUUIDLongSerializer.kt @@ -0,0 +1,60 @@ +package dev.slne.surf.api.core.serializer.java.uuid + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.element +import kotlinx.serialization.encoding.* +import java.util.* + +typealias SerializableLongUUID = @Serializable(with = JavaUUIDLongSerializer::class) UUID + +object JavaUUIDLongSerializer : KSerializer { + override val descriptor = buildClassSerialDescriptor("surf.api.LongUuid") { + element("most") + element("least") + } + + override fun serialize(encoder: Encoder, value: UUID) { + encoder.encodeStructure(descriptor) { + encodeLongElement(descriptor, 0, value.mostSignificantBits) + encodeLongElement(descriptor, 1, value.leastSignificantBits) + } + } + + @OptIn(ExperimentalSerializationApi::class) + override fun deserialize(decoder: Decoder): UUID = decoder.decodeStructure(descriptor) { + if (decodeSequentially()) { + return@decodeStructure UUID( + decodeLongElement(descriptor, 0), + decodeLongElement(descriptor, 1), + ) + } + + var most = 0L + var least = 0L + var seen = 0 + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> { + most = decodeLongElement(descriptor, 0) + seen = seen or 1 + } + + 1 -> { + least = decodeLongElement(descriptor, 1) + seen = seen or 2 + } + + CompositeDecoder.DECODE_DONE -> break + else -> throw SerializationException("Unexpected element index $index") + } + } + + if (seen != 3) throw SerializationException("UUID requires both 'most' and 'least'") + + UUID(most, least) + } +} From 35c772cfa670806a229dd815ce3129a450314615 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:23:59 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=A7=20chore:=20bump=20version=20to?= =?UTF-8?q?=203.47.0=20in=20gradle.properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5ea82d3fa..e576c5245 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.3 group=dev.slne.surf.api -version=3.46.2 +version=3.47.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false