Skip to content
Snippets Groups Projects
Verified Commit 24bd964e authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Experiments with coroutines

parent 408e30a0
Branches
No related tags found
1 merge request!2Draft: Jetpack compose rewrite
Pipeline #568 failed
Showing
with 825 additions and 6 deletions
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid
data class ProtocolInfo(
val flags: UByte,
val data: UShort,
val version: UByte,
)
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object BoolSerializer : Serializer<Boolean> {
override fun serialize(buffer: ChainedByteBuffer, data: Boolean) {
buffer.put(
if (data) 0x01.toByte()
else 0x00.toByte()
)
}
override fun deserialize(buffer: ByteBuffer): Boolean {
return buffer.get() != 0x00.toByte()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object ByteSerializer : Serializer<Byte> {
override fun serialize(buffer: ChainedByteBuffer, data: Byte) {
buffer.put(data)
}
override fun deserialize(buffer: ByteBuffer): Byte {
return buffer.get()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import de.kuschku.quasseldroid.util.CoroutineChannel
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.yield
import java.nio.ByteBuffer
import java.nio.channels.AsynchronousByteChannel
import java.nio.channels.WritableByteChannel
import java.util.*
class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: Boolean = false) {
private val bufferList: MutableList<ByteBuffer> = ArrayList()
var size = 0
private set
private var currentBuffer = 0
private fun allocate(size: Int) = when (direct) {
true -> ByteBuffer.allocateDirect(size)
false -> ByteBuffer.allocate(size)
}
private fun ensureSpace(size: Int) {
if (bufferList.isEmpty()) {
bufferList.add(allocate(bufferSize))
}
if (bufferList[currentBuffer].remaining() < size) {
currentBuffer += 1
}
if (currentBuffer == bufferList.size) {
bufferList.add(allocate(bufferSize))
}
this.size += size
}
fun put(value: Byte) {
ensureSpace(1)
bufferList.last().put(value)
}
fun putChar(value: Char) {
ensureSpace(2)
bufferList.last().putChar(value)
}
fun putShort(value: Short) {
ensureSpace(2)
bufferList.last().putShort(value)
}
fun putInt(value: Int) {
ensureSpace(4)
bufferList.last().putInt(value)
}
fun putLong(value: Long) {
ensureSpace(8)
bufferList.last().putLong(value)
}
fun putFloat(value: Float) {
ensureSpace(4)
bufferList.last().putFloat(value)
}
fun putDouble(value: Double) {
ensureSpace(8)
bufferList.last().putDouble(value)
}
fun put(value: ByteBuffer) {
while (value.remaining() > 8) {
putLong(value.long)
}
while (value.hasRemaining()) {
put(value.get())
}
}
fun put(value: ByteArray) {
value.forEach(this::put)
}
fun clear() {
bufferList.clear()
currentBuffer = 0
size = 0
}
fun buffers() = sequence {
for (buffer in bufferList) {
buffer.flip()
yield(buffer)
}
}
fun toBuffer(): ByteBuffer {
val byteBuffer = allocate(size)
for (buffer in bufferList) {
buffer.flip()
byteBuffer.put(buffer)
}
byteBuffer.flip()
return byteBuffer
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object IntSerializer : Serializer<Int> {
override fun serialize(buffer: ChainedByteBuffer, data: Int) {
buffer.putInt(data)
}
override fun deserialize(buffer: ByteBuffer): Int {
return buffer.getInt()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object LongSerializer : Serializer<Long> {
override fun serialize(buffer: ChainedByteBuffer, data: Long) {
buffer.putLong(data)
}
override fun deserialize(buffer: ByteBuffer): Long {
return buffer.getLong()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import de.kuschku.quasseldroid.ProtocolInfo
import java.nio.ByteBuffer
object ProtocolInfoSerializer : Serializer<ProtocolInfo> {
override fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo) {
UByteSerializer.serialize(buffer, data.flags)
UShortSerializer.serialize(buffer, data.data)
UByteSerializer.serialize(buffer, data.version)
}
override fun deserialize(buffer: ByteBuffer): ProtocolInfo {
return ProtocolInfo(
UByteSerializer.deserialize(buffer),
UShortSerializer.deserialize(buffer),
UByteSerializer.deserialize(buffer)
)
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
interface Serializer<T> {
fun serialize(buffer: ChainedByteBuffer, data: T)
fun deserialize(buffer: ByteBuffer): T
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object ShortSerializer : Serializer<Short> {
override fun serialize(buffer: ChainedByteBuffer, data: Short) {
buffer.putShort(data)
}
override fun deserialize(buffer: ByteBuffer): Short {
return buffer.getShort()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object UByteSerializer : Serializer<UByte> {
override fun serialize(buffer: ChainedByteBuffer, data: UByte) {
buffer.put(data.toByte())
}
override fun deserialize(buffer: ByteBuffer): UByte {
return buffer.get().toUByte()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object UIntSerializer : Serializer<UInt> {
override fun serialize(buffer: ChainedByteBuffer, data: UInt) {
buffer.putInt(data.toInt())
}
override fun deserialize(buffer: ByteBuffer): UInt {
return buffer.getInt().toUInt()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object ULongSerializer : Serializer<ULong> {
override fun serialize(buffer: ChainedByteBuffer, data: ULong) {
buffer.putLong(data.toLong())
}
override fun deserialize(buffer: ByteBuffer): ULong {
return buffer.getLong().toULong()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.protocol
import java.nio.ByteBuffer
object UShortSerializer : Serializer<UShort> {
override fun serialize(buffer: ChainedByteBuffer, data: UShort) {
buffer.putShort(data.toShort())
}
override fun deserialize(buffer: ByteBuffer): UShort {
return buffer.getShort().toUShort()
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util
import de.kuschku.quasseldroid.protocol.ChainedByteBuffer
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runInterruptible
import java.net.InetSocketAddress
import java.net.Socket
import java.nio.ByteBuffer
import java.util.concurrent.Executors
class CoroutineChannel {
private lateinit var channel: StreamChannel
private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
suspend fun connect(address: InetSocketAddress) = runInterruptible(writeContext) {
this.channel = StreamChannel(Socket(address.address, address.port))
}
suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) {
this.channel.read(buffer)
}
suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
this.channel.write(buffer)
}
suspend fun write(chainedBuffer: ChainedByteBuffer) {
for (buffer in chainedBuffer.buffers()) {
write(buffer)
}
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util
import android.util.Log
import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.channels.ReadableByteChannel
import java.nio.channels.spi.AbstractInterruptibleChannel
class ReadableWrappedChannel(
private var backingStream: InputStream
) : AbstractInterruptibleChannel(), ReadableByteChannel {
private val buffer = ByteBuffer.allocate(PAGE_SIZE)
private val lock = Any()
override fun read(dst: ByteBuffer): Int {
val totalData = dst.remaining()
var remainingData = totalData
// used to mark if we’ve already read any data. This is used to ensure that we only ever block
// once for reading
var hasRead = false
synchronized(lock) {
// Only read as long as we have content to read, and until we’ve blocked at most once
while (remainingData > 0 && !(hasRead && backingStream.available() == 0)) {
// Data to be read, always the minimum of available data and the page size
val toReadOnce = Math.min(remainingData, PAGE_SIZE)
var readData = 0
try {
// begin blocking operation, this handles interruption etc. properly
begin()
// prepare bufferId for reading by resetting position and limit
buffer.clear()
// read data into bufferId
readData = backingStream.read(buffer.array(), 0, toReadOnce)
// accurately set bufferId info
buffer.position(readData)
} finally {
// end blocking operation, this handles interruption etc. properly
end(readData > 0)
}
if (readData <= 0) {
Log.e("ReadableWrappedChannel", "Read: $readData")
}
// read is negative if no data was read, in that case, terminate
if (readData < 0)
break
// add read amount to total
remainingData -= readData
// mark that we’ve read data (to only block once)
hasRead = true
// flip bufferId to prepare for reading
buffer.flip()
dst.put(buffer)
}
}
return (totalData - remainingData)
}
override fun implCloseChannel() = backingStream.close()
companion object {
private const val PAGE_SIZE = 8192
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util
import java.io.Flushable
import java.io.InputStream
import java.io.OutputStream
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.channels.ByteChannel
import java.nio.channels.InterruptibleChannel
import java.util.zip.DeflaterOutputStream
import java.util.zip.InflaterInputStream
class StreamChannel constructor(
private val socket: Socket,
private val inputStream: InputStream = socket.getInputStream(),
private val outputStream: OutputStream = socket.getOutputStream(),
) : Flushable by outputStream, ByteChannel, InterruptibleChannel {
private val input = ReadableWrappedChannel(inputStream)
private val output = WritableWrappedChannel(outputStream)
fun withCompression(): StreamChannel {
return StreamChannel(
socket,
InflaterInputStream(inputStream),
DeflaterOutputStream(outputStream),
)
}
override fun close() {
input.close()
output.close()
socket.close()
}
override fun isOpen(): Boolean {
return !socket.isClosed
}
override fun read(dst: ByteBuffer): Int {
return input.read(dst)
}
override fun write(src: ByteBuffer): Int {
return output.write(src)
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util
import java.io.OutputStream
import java.nio.ByteBuffer
import java.nio.channels.WritableByteChannel
import java.nio.channels.spi.AbstractInterruptibleChannel
class WritableWrappedChannel(
private var backingStream: OutputStream
) : AbstractInterruptibleChannel(), WritableByteChannel {
private val buffer = ByteBuffer.allocate(PAGE_SIZE)
private val lock = Any()
override fun write(src: ByteBuffer): Int {
val totalData = src.remaining()
var remainingData = totalData
synchronized(lock) {
while (remainingData > 0) {
// Data to be written, always the minimum of available data and the page size
val writtenData = Math.min(remainingData, PAGE_SIZE)
// Set new bufferId info
buffer.clear()
buffer.limit(writtenData)
// Read data into bufferId
buffer.put(src)
try {
// begin blocking operation, this handles interruption etc. properly
begin()
// Write data to backing stream
backingStream.write(buffer.array(), 0, writtenData)
} finally {
// end blocking operation, this handles interruption etc. properly
end(writtenData > 0)
}
// add written amount to total
remainingData -= writtenData
}
return (totalData - remainingData)
}
}
override fun implCloseChannel() = backingStream.close()
companion object {
private const val PAGE_SIZE = 8192
}
}
package de.kuschku.quasseldroid package de.kuschku.quasseldroid
import de.kuschku.quasseldroid.protocol.ChainedByteBuffer
import de.kuschku.quasseldroid.protocol.IntSerializer
import de.kuschku.quasseldroid.protocol.UIntSerializer
import de.kuschku.quasseldroid.util.CoroutineChannel
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import java.net.InetSocketAddress
import org.junit.Assert.* import java.nio.ByteBuffer
/** /**
* Example local unit test, which will execute on the development machine (host). * Example local unit test, which will execute on the development machine (host).
...@@ -14,4 +20,23 @@ class ExampleUnitTest { ...@@ -14,4 +20,23 @@ class ExampleUnitTest {
fun addition_isCorrect() { fun addition_isCorrect() {
assertEquals(4, 2 + 2) assertEquals(4, 2 + 2)
} }
@Test
fun testNetworking() {
runBlocking {
val sizeBuffer = ByteBuffer.allocateDirect(4)
val sendBuffer = ChainedByteBuffer(direct = true)
val channel = CoroutineChannel()
channel.connect(InetSocketAddress("kuschku.de", 4242))
val readBuffer = ByteBuffer.allocateDirect(4)
UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u)
IntSerializer.serialize(sendBuffer, 2)
UIntSerializer.serialize(sendBuffer, 0x8000_0000u)
channel.write(sendBuffer)
channel.read(readBuffer)
readBuffer.flip()
println(IntSerializer.deserialize(readBuffer))
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment