diff --git a/.idea/misc.xml b/.idea/misc.xml
index 239935e..61efd00 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,7 +4,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index fdb5b8f..9f55a08 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -17,7 +17,7 @@ repositories {
kotlin {
jvm {
- jvmToolchain(11)
+ jvmToolchain(8)
withJava()
testRuns.named("test") {
executionTask.configure {
diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.kt
new file mode 100644
index 0000000..9742015
--- /dev/null
+++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.kt
@@ -0,0 +1,37 @@
+package net.sergeych.kiloparsec.adapter
+
+import kotlinx.coroutines.channels.ReceiveChannel
+
+/**
+ * Multiplatform implementation of an internet address.
+ * Notice to implementors. It must provide correct and effective [equals] and [hashCode].
+ */
+interface NetworkAddress {
+ val host: String
+ val port: Int
+}
+
+/**
+ * Multiplatform datagram abstraction
+ */
+interface Datagram {
+ val message: UByteArray
+ val address: NetworkAddress
+ suspend fun respondWith(message: UByteArray)
+}
+interface DatagramReceiver {
+
+ val incoming: ReceiveChannel
+ suspend fun send(message: UByteArray, networkAddress: NetworkAddress)
+ @Suppress("unused")
+ suspend fun send(message: UByteArray, datagramAddress: String) {
+ send(message, networkAddressOf(datagramAddress))
+ }
+
+ suspend fun send(message: UByteArray,host: String,port: Int) =
+ send(message, NetworkAddress(host,port))
+ fun close()
+}
+
+expect fun networkAddressOf(address: String): NetworkAddress
+expect fun NetworkAddress(host: String,port: Int): NetworkAddress
diff --git a/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.js.kt b/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.js.kt
new file mode 100644
index 0000000..ff55b73
--- /dev/null
+++ b/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.js.kt
@@ -0,0 +1,5 @@
+package net.sergeych.kiloparsec.adapter
+
+actual fun networkAddressOf(address: String): NetworkAddress {
+ TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.js.kt b/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.js.kt
new file mode 100644
index 0000000..d4dc4ab
--- /dev/null
+++ b/src/jsMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.js.kt
@@ -0,0 +1,5 @@
+package net.sergeych.kiloparsec.adapter
+
+actual fun NetworkAddress(host: String, port: Int): NetworkAddress {
+ TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.jvm.kt b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.jvm.kt
new file mode 100644
index 0000000..7a8cea4
--- /dev/null
+++ b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.jvm.kt
@@ -0,0 +1,8 @@
+package net.sergeych.kiloparsec.adapter
+
+import java.net.InetAddress
+
+actual fun networkAddressOf(address: String): NetworkAddress {
+ val (host,port) = address.split(":")
+ return JvmNetworkAddress(InetAddress.getByName(host), port.toInt())
+}
\ No newline at end of file
diff --git a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.jvm.kt b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.jvm.kt
new file mode 100644
index 0000000..6de79e8
--- /dev/null
+++ b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.jvm.kt
@@ -0,0 +1,6 @@
+package net.sergeych.kiloparsec.adapter
+
+import java.net.InetAddress
+
+actual fun NetworkAddress(host: String, port: Int): NetworkAddress =
+ JvmNetworkAddress(InetAddress.getByName(host), port)
diff --git a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/UdpServer.kt b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/UdpServer.kt
new file mode 100644
index 0000000..4766d0f
--- /dev/null
+++ b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/UdpServer.kt
@@ -0,0 +1,135 @@
+package net.sergeych.kiloparsec.adapter
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import net.sergeych.mp_logger.LogTag
+import net.sergeych.mp_logger.exception
+import net.sergeych.mp_logger.info
+import net.sergeych.mp_logger.warning
+import java.net.DatagramPacket
+import java.net.DatagramSocket
+import java.net.InetAddress
+import java.util.concurrent.atomic.AtomicInteger
+
+private val counter = AtomicInteger(0)
+
+class JvmNetworkAddress(val inetAddress: InetAddress, override val port: Int) : NetworkAddress {
+ override val host: String by lazy { inetAddress.hostName }
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is JvmNetworkAddress) return false
+
+ if (inetAddress != other.inetAddress) return false
+ if (port != other.port) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = inetAddress.hashCode()
+ result = 31 * result + port
+ return result
+ }
+
+}
+
+class UdpDatagram(override val message: UByteArray, val inetAddress: InetAddress, val port: Int) : Datagram {
+
+ override val address: NetworkAddress by lazy {
+ JvmNetworkAddress(inetAddress, port)
+ }
+
+ private val access = Mutex()
+
+ private var socket: DatagramSocket? = null
+ override suspend fun respondWith(message: UByteArray) {
+ withContext(Dispatchers.IO) {
+ access.withLock {
+ if (socket == null) socket = DatagramSocket()
+ val packet = DatagramPacket(
+ message.toByteArray(),
+ message.size,
+ inetAddress,
+ port
+ )
+ socket!!.send(packet)
+ }
+ }
+ }
+}
+
+@OptIn(DelicateCoroutinesApi::class)
+class UdpServer(val port: Int) :
+ DatagramReceiver, LogTag("UDPS:${counter.incrementAndGet()}") {
+ private var isClosed = false
+
+
+ private val deferredSocket = CompletableDeferred()
+ private var job: Job? = null
+
+ private suspend fun start() = try {
+ coroutineScope {
+ val socket = DatagramSocket(port)
+ val buffer = ByteArray(16384)
+ val packet = DatagramPacket(buffer, buffer.size)
+ deferredSocket.complete(socket)
+ while (isActive && !isClosed) {
+ try {
+ socket.receive(packet)
+ val data = packet.data.sliceArray(0..(2048, BufferOverflow.DROP_OLDEST)
+ override val incoming = channel
+
+ override suspend fun send(message: UByteArray, networkAddress: NetworkAddress) {
+ networkAddress as JvmNetworkAddress
+ withContext(Dispatchers.IO) {
+ val packet = DatagramPacket(
+ message.toByteArray(), message.size,
+ networkAddress.inetAddress, networkAddress.port
+ )
+ deferredSocket.await().send(packet)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/jvmTest/kotlin/net/sergeych/kiloparsec/adapters/UServerTest.kt b/src/jvmTest/kotlin/net/sergeych/kiloparsec/adapters/UServerTest.kt
new file mode 100644
index 0000000..102b58b
--- /dev/null
+++ b/src/jvmTest/kotlin/net/sergeych/kiloparsec/adapters/UServerTest.kt
@@ -0,0 +1,26 @@
+package net.sergeych.kiloparsec.adapters
+
+import com.ionspin.kotlin.crypto.util.encodeToUByteArray
+import kotlinx.coroutines.test.runTest
+import net.sergeych.kiloparsec.adapter.UdpServer
+import net.sergeych.mp_logger.Log
+import org.junit.jupiter.api.Assertions.assertEquals
+import kotlin.test.Test
+
+class UServerTest {
+
+ @Test
+ fun udpProvider() = runTest {
+ Log.connectConsole(Log.Level.DEBUG)
+ val s1 = UdpServer(17120)
+ val s2 = UdpServer(17121)
+ s1.send("Hello".encodeToUByteArray(), "localhost",17121)
+ val d1 = s2.incoming.receive()
+ assertEquals(d1.address.port, 17120)
+ assertEquals("Hello", d1.message.toByteArray().decodeToString())
+ d1.respondWith("world".encodeToUByteArray())
+ assertEquals("world", s1.incoming.receive().message.toByteArray().decodeToString())
+ // println("s1: ${s1.bindAddress()}")
+
+ }
+}
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.native.kt b/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.native.kt
new file mode 100644
index 0000000..ff55b73
--- /dev/null
+++ b/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/DatagramProvider.native.kt
@@ -0,0 +1,5 @@
+package net.sergeych.kiloparsec.adapter
+
+actual fun networkAddressOf(address: String): NetworkAddress {
+ TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.native.kt b/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.native.kt
new file mode 100644
index 0000000..d4dc4ab
--- /dev/null
+++ b/src/nativeMain/kotlin/net/sergeych/kiloparsec/adapter/NetworkProvider.native.kt
@@ -0,0 +1,5 @@
+package net.sergeych.kiloparsec.adapter
+
+actual fun NetworkAddress(host: String, port: Int): NetworkAddress {
+ TODO("Not yet implemented")
+}
\ No newline at end of file