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