diff --git a/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..2cfe00e000f65d07a859194c736fd5c98f17d460 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt @@ -0,0 +1,26 @@ +/* + * 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, +) diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..ff6758462fd83aafbf74fadf0d91daa7da8fee96 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt @@ -0,0 +1,35 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..d11a523a09cbcb2aa563ca2b01ec7c546d0bc0f6 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt new file mode 100644 index 0000000000000000000000000000000000000000..871b104c4d4c8825961a7cc4af9358307bf4ff08 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt @@ -0,0 +1,133 @@ +/* + * 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 + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..c414476e5b3eea866ba057e800d5e97bba04540e --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..334218595faf6a3a49a383715856300dccf896dc --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..e4048406798dba29fca52cd45f28f6823df55e9b --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt @@ -0,0 +1,39 @@ +/* + * 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) + ) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..2d6589765231d87943d96d3de980b42bf320b9d2 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt @@ -0,0 +1,27 @@ +/* + * 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 +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..a7cd95cb2efb3f89ad315978454160f99b5a77d7 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..a288c1ebe4bef01c75e33d77c826fa37373241bd --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..535a6cf3ca800c4fae54d47b4dbf64144ff02d27 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..188a41874123002527e285a2f6bb79d6035e70a3 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..da78b5a5e66558ed69c3e85ac5212567e602df53 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt @@ -0,0 +1,32 @@ +/* + * 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() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe3bd2b67a6ec431067a175619971bbb92e66ae9 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt @@ -0,0 +1,52 @@ +/* + * 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) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt new file mode 100644 index 0000000000000000000000000000000000000000..6bb7c3127d629519e0fda2ab855f0127dda8155a --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt @@ -0,0 +1,90 @@ +/* + * 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 + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt new file mode 100644 index 0000000000000000000000000000000000000000..c744310e31c2e08bef2162ed3e04a50d88a584e2 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt @@ -0,0 +1,65 @@ +/* + * 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) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef0b3fa3c7437c807519f3ba65f9dc8e3b7cb7d0 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt @@ -0,0 +1,71 @@ +/* + * 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 + } +} diff --git a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt index 6e55bcd4a1780d19514b210b4e31025fa5ae10f8..4ebc9b8ae91cd877f5dd8ffa27905f971635334e 100644 --- a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt +++ b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt @@ -1,8 +1,14 @@ 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.Assert.* +import java.net.InetSocketAddress +import java.nio.ByteBuffer /** * Example local unit test, which will execute on the development machine (host). @@ -10,8 +16,27 @@ import org.junit.Assert.* * See [testing documentation](http://d.android.com/tools/testing). */ class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) + @Test + fun addition_isCorrect() { + 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)) } -} \ No newline at end of file + } +} +