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

Further cleanup

parent e6e10d12
No related branches found
No related tags found
1 merge request!2Draft: Jetpack compose rewrite
Pipeline #571 failed
Showing
with 585 additions and 317 deletions
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.kuschku.libquassel.protocol.io package de.kuschku.quasseldroid.protocol.io
import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
import de.kuschku.libquassel.protocol.io.print
import de.kuschku.quasseldroid.util.TlsInfo import de.kuschku.quasseldroid.util.TlsInfo
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
...@@ -65,12 +67,17 @@ class CoroutineChannel { ...@@ -65,12 +67,17 @@ class CoroutineChannel {
} }
suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) { suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
buffer.print()
this.channel.write(buffer) this.channel.write(buffer)
} }
suspend fun write(chainedBuffer: de.kuschku.libquassel.protocol.io.ChainedByteBuffer) { suspend fun write(chainedBuffer: ChainedByteBuffer) {
for (buffer in chainedBuffer.buffers()) { for (buffer in chainedBuffer.buffers()) {
write(buffer) write(buffer)
} }
} }
suspend fun flush() = runInterruptible(writeContext) {
this.channel.flush()
}
} }
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.kuschku.libquassel.protocol.io package de.kuschku.quasseldroid.protocol.io
import java.io.OutputStream import java.io.OutputStream
import java.util.zip.DeflaterOutputStream import java.util.zip.DeflaterOutputStream
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.kuschku.libquassel.protocol.io package de.kuschku.quasseldroid.protocol.io
import android.util.Log import android.util.Log
import java.io.InputStream import java.io.InputStream
...@@ -43,7 +43,7 @@ class ReadableWrappedChannel( ...@@ -43,7 +43,7 @@ class ReadableWrappedChannel(
// Only read as long as we have content to read, and until we’ve blocked at most once // 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)) { while (remainingData > 0 && !(hasRead && backingStream.available() == 0)) {
// Data to be read, always the minimum of available data and the page size // Data to be read, always the minimum of available data and the page size
val toReadOnce = Math.min(remainingData, PAGE_SIZE) val toReadOnce = remainingData.coerceAtMost(PAGE_SIZE)
var readData = 0 var readData = 0
try { try {
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.kuschku.libquassel.protocol.io package de.kuschku.quasseldroid.protocol.io
import de.kuschku.quasseldroid.util.TlsInfo import de.kuschku.quasseldroid.util.TlsInfo
import java.io.Flushable import java.io.Flushable
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.kuschku.libquassel.protocol.io package de.kuschku.quasseldroid.protocol.io
import java.io.OutputStream import java.io.OutputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
...@@ -37,7 +37,7 @@ class WritableWrappedChannel( ...@@ -37,7 +37,7 @@ class WritableWrappedChannel(
synchronized(lock) { synchronized(lock) {
while (remainingData > 0) { while (remainingData > 0) {
// Data to be written, always the minimum of available data and the page size // Data to be written, always the minimum of available data and the page size
val writtenData = Math.min(remainingData, PAGE_SIZE) val writtenData = remainingData.coerceAtMost(PAGE_SIZE)
// Set new bufferId info // Set new bufferId info
buffer.clear() buffer.clear()
......
package de.kuschku.quasseldroid package de.kuschku.quasseldroid
import de.kuschku.bitflags.flags
import de.kuschku.libquassel.protocol.connection.ProtocolInfoSerializer
import de.kuschku.libquassel.protocol.features.FeatureSet
import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
import de.kuschku.libquassel.protocol.io.print
import de.kuschku.libquassel.protocol.messages.handshake.ClientInit
import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitAckSerializer
import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitRejectSerializer
import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitSerializer
import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer
import de.kuschku.libquassel.protocol.serializers.primitive.IntSerializer import de.kuschku.libquassel.protocol.serializers.primitive.IntSerializer
import de.kuschku.libquassel.protocol.serializers.primitive.ProtocolInfoSerializer
import de.kuschku.libquassel.protocol.serializers.primitive.UIntSerializer import de.kuschku.libquassel.protocol.serializers.primitive.UIntSerializer
import de.kuschku.libquassel.protocol.io.CoroutineChannel import de.kuschku.libquassel.protocol.variant.into
import de.kuschku.quasseldroid.protocol.io.CoroutineChannel
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
...@@ -14,16 +23,7 @@ import java.security.cert.X509Certificate ...@@ -14,16 +23,7 @@ import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext import javax.net.ssl.SSLContext
import javax.net.ssl.X509TrustManager import javax.net.ssl.X509TrustManager
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest { class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
@Test @Test
fun testNetworking() { fun testNetworking() {
...@@ -44,23 +44,82 @@ class ExampleUnitTest { ...@@ -44,23 +44,82 @@ class ExampleUnitTest {
}), null) }), null)
runBlocking { runBlocking {
val connectionFeatureSet = FeatureSet.build()
val sizeBuffer = ByteBuffer.allocateDirect(4) val sizeBuffer = ByteBuffer.allocateDirect(4)
val sendBuffer = de.kuschku.libquassel.protocol.io.ChainedByteBuffer(direct = true) val sendBuffer = ChainedByteBuffer(direct = true)
val channel = CoroutineChannel() val channel = CoroutineChannel()
channel.connect(InetSocketAddress("kuschku.de", 4242)) channel.connect(InetSocketAddress("kuschku.de", 4242))
val readBuffer = ByteBuffer.allocateDirect(4)
UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u or 0x03u) suspend fun readAmount(amount: Int? = null): Int {
IntSerializer.serialize(sendBuffer, 2) if (amount != null) return amount
UIntSerializer.serialize(sendBuffer, 0x8000_0000u)
sizeBuffer.clear()
channel.read(sizeBuffer)
sizeBuffer.flip()
val size = IntSerializer.deserialize(sizeBuffer, connectionFeatureSet)
sizeBuffer.clear()
return size
}
suspend fun write(sizePrefix: Boolean = true, f: suspend (ChainedByteBuffer) -> Unit) {
f(sendBuffer)
if (sizePrefix) {
sizeBuffer.clear()
sizeBuffer.putInt(sendBuffer.size)
sizeBuffer.flip()
channel.write(sizeBuffer)
sizeBuffer.clear()
}
channel.write(sendBuffer) channel.write(sendBuffer)
channel.read(readBuffer) channel.flush()
readBuffer.flip() sendBuffer.clear()
println(ProtocolInfoSerializer.deserialize(readBuffer)) }
suspend fun <T> read(amount: Int? = null, f: suspend (ByteBuffer) -> T): T {
val amount1 = readAmount(amount)
val messageBuffer = ByteBuffer.allocateDirect(minOf(amount1, 65 * 1024 * 1024))
channel.read(messageBuffer)
messageBuffer.flip()
return f(messageBuffer)
}
println("Writing protocol")
write(sizePrefix = false) {
UIntSerializer.serialize(it, 0x42b3_3f00u or 0x03u, connectionFeatureSet)
IntSerializer.serialize(it, 2, connectionFeatureSet)
UIntSerializer.serialize(it, 0x8000_0000u, connectionFeatureSet)
}
println("Reading protocol")
read(4) {
println(ProtocolInfoSerializer.deserialize(it, connectionFeatureSet))
println(channel.tlsInfo.value) println(channel.tlsInfo.value)
channel.enableTLS(context) channel.enableTLS(context)
println(channel.tlsInfo.value) println(channel.tlsInfo.value)
channel.enableCompression() channel.enableCompression()
} }
println("Writing clientInit")
write {
HandshakeMapSerializer.serialize(
it,
ClientInitSerializer.serialize(ClientInit(
clientVersion = "Quasseldroid test",
buildDate = "Never",
clientFeatures = flags(),
featureList = emptyList()
)),
connectionFeatureSet
)
}
read {
val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
println(data)
when (data["MsgType"].into<String>()) {
"ClientInitAck" -> println(ClientInitAckSerializer.deserialize(data))
"ClientInitReject" -> println(ClientInitRejectSerializer.deserialize(data))
}
}
}
} }
} }
...@@ -23,3 +23,6 @@ interface Flags<T, U : Flag<T>> { ...@@ -23,3 +23,6 @@ interface Flags<T, U : Flag<T>> {
operator fun get(value: T): U? operator fun get(value: T): U?
fun all(): Collection<U> fun all(): Collection<U>
} }
inline fun <reified T> flags(vararg values: T) where T : Flag<*>, T : Enum<T> = setOf(*values)
inline fun <reified T> flags(values: Collection<T>) where T : Flag<*>, T : Enum<T> = values.toSet()
/*
* 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.libquassel.protocol.connection
import de.kuschku.libquassel.protocol.features.FeatureSet
import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
import de.kuschku.libquassel.protocol.serializers.primitive.UByteSerializer
import de.kuschku.libquassel.protocol.serializers.primitive.UShortSerializer
import java.nio.ByteBuffer
object ProtocolInfoSerializer {
fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo, featureSet: FeatureSet) {
UByteSerializer.serialize(buffer, data.flags, featureSet)
UShortSerializer.serialize(buffer, data.data, featureSet)
UByteSerializer.serialize(buffer, data.version, featureSet)
}
fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): ProtocolInfo {
return ProtocolInfo(
UByteSerializer.deserialize(buffer, featureSet),
UShortSerializer.deserialize(buffer, featureSet),
UByteSerializer.deserialize(buffer, featureSet)
)
}
}
/*
* 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.libquassel.protocol.features
import de.kuschku.bitflags.flags
class FeatureSet internal constructor(
private val features: Set<QuasselFeature>,
private val additional: Set<QuasselFeatureName> = emptySet()
) {
fun enabled(feature: QuasselFeature) = features.contains(feature)
fun featureList(): List<QuasselFeatureName> =
features.map(QuasselFeature::feature) + additional
fun legacyFeatures(): LegacyFeatures =
flags(features.mapNotNull(LegacyFeature.Companion::get))
companion object {
fun parse(
legacy: LegacyFeatures,
features: Collection<QuasselFeatureName>
) = FeatureSet(
features = parseFeatures(legacy) + parseFeatures(features),
additional = unknownFeatures(features)
)
fun build(vararg features: QuasselFeature) = FeatureSet(features.toSet())
fun build(features: Set<QuasselFeature>) = FeatureSet(features)
fun all() = build(*QuasselFeature.values())
fun empty() = build()
private fun parseFeatures(features: LegacyFeatures) =
features.map(LegacyFeature::feature).toSet()
private fun parseFeatures(features: Collection<QuasselFeatureName>) =
features.mapNotNull(QuasselFeature.Companion::valueOf).toSet()
private fun unknownFeatures(features: Collection<QuasselFeatureName>) =
features.filter { QuasselFeature.valueOf(it) == null }.toSet()
}
}
...@@ -31,35 +31,41 @@ import de.kuschku.bitflags.Flags ...@@ -31,35 +31,41 @@ import de.kuschku.bitflags.Flags
* *
* This list should be cleaned up after every protocol break, as we can assume them to be present then. * This list should be cleaned up after every protocol break, as we can assume them to be present then.
*/ */
enum class LegacyFeature(override val value: UInt): Flag<UInt> { enum class LegacyFeature(
SynchronizedMarkerLine(0x0001u), override val value: UInt,
SaslAuthentication(0x0002u), val feature: QuasselFeature,
SaslExternal(0x0004u), ): Flag<UInt> {
HideInactiveNetworks(0x0008u), SynchronizedMarkerLine(0x0001u, QuasselFeature.SynchronizedMarkerLine),
PasswordChange(0x0010u), SaslAuthentication(0x0002u, QuasselFeature.SaslAuthentication),
SaslExternal(0x0004u, QuasselFeature.SaslExternal),
HideInactiveNetworks(0x0008u, QuasselFeature.HideInactiveNetworks),
PasswordChange(0x0010u, QuasselFeature.PasswordChange),
/** IRCv3 capability negotiation, account tracking */ /** IRCv3 capability negotiation, account tracking */
CapNegotiation(0x0020u), CapNegotiation(0x0020u, QuasselFeature.CapNegotiation),
/** IRC server SSL validation */ /** IRC server SSL validation */
VerifyServerSSL(0x0040u), VerifyServerSSL(0x0040u, QuasselFeature.VerifyServerSSL),
/** IRC server custom message rate limits */ /** IRC server custom message rate limits */
CustomRateLimits(0x0080u), CustomRateLimits(0x0080u, QuasselFeature.CustomRateLimits),
DccFileTransfer(0x0100u), DccFileTransfer(0x0100u, QuasselFeature.DccFileTransfer),
/** Timestamp formatting in away (e.g. %%hh:mm%%) */ /** Timestamp formatting in away (e.g. %%hh:mm%%) */
AwayFormatTimestamp(0x0200u), AwayFormatTimestamp(0x0200u, QuasselFeature.AwayFormatTimestamp),
/** Whether or not the core supports auth backends. */ /** Whether or not the core supports auth backends. */
Authenticators(0x0400u), Authenticators(0x0400u, QuasselFeature.Authenticators),
/** Sync buffer activity status */ /** Sync buffer activity status */
BufferActivitySync(0x0800u), BufferActivitySync(0x0800u, QuasselFeature.BufferActivitySync),
/** Core-Side highlight configuration and matching */ /** Core-Side highlight configuration and matching */
CoreSideHighlights(0x1000u), CoreSideHighlights(0x1000u, QuasselFeature.CoreSideHighlights),
/** Show prefixes for senders in backlog */ /** Show prefixes for senders in backlog */
SenderPrefixes(0x2000u), SenderPrefixes(0x2000u, QuasselFeature.SenderPrefixes),
/** Supports RPC call disconnectFromCore to remotely disconnect a client */ /** Supports RPC call disconnectFromCore to remotely disconnect a client */
RemoteDisconnect(0x4000u), RemoteDisconnect(0x4000u, QuasselFeature.RemoteDisconnect),
/** Transmit features as list of strings */ /** Transmit features as list of strings */
ExtendedFeatures(0x8000u); ExtendedFeatures(0x8000u, QuasselFeature.ExtendedFeatures);
companion object : Flags<UInt, LegacyFeature> { companion object : Flags<UInt, LegacyFeature> {
private val features = values().associateBy(LegacyFeature::feature)
fun get(feature: QuasselFeature) = features[feature]
private val values = values().associateBy(LegacyFeature::value) private val values = values().associateBy(LegacyFeature::value)
override fun get(value: UInt) = values[value] override fun get(value: UInt) = values[value]
override fun all() = values.values override fun all() = values.values
......
...@@ -19,11 +19,11 @@ ...@@ -19,11 +19,11 @@
package de.kuschku.libquassel.protocol.features package de.kuschku.libquassel.protocol.features
inline class ExtendedFeatureName( inline class QuasselFeatureName(
val name: String, val name: String,
) )
enum class ExtendedFeature { enum class QuasselFeature {
SynchronizedMarkerLine, SynchronizedMarkerLine,
SaslAuthentication, SaslAuthentication,
SaslExternal, SaslExternal,
...@@ -64,5 +64,11 @@ enum class ExtendedFeature { ...@@ -64,5 +64,11 @@ enum class ExtendedFeature {
/** CoreInfo dynamically updated using signals */ /** CoreInfo dynamically updated using signals */
SyncedCoreInfo; SyncedCoreInfo;
fun feature(): ExtendedFeatureName = ExtendedFeatureName(name) val feature = QuasselFeatureName(name)
companion object {
private val values = values().associateBy(QuasselFeature::feature)
@JvmStatic
fun valueOf(name: QuasselFeatureName): QuasselFeature? = values[name]
}
} }
/*
* 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.libquassel.protocol.io
import java.nio.ByteBuffer
fun copyData(from: ByteBuffer, to: ByteBuffer, amount: Int = -1) {
val actualAmount =
if (amount >= 0) minOf(from.remaining(), to.remaining(), amount)
else minOf(from.remaining(), to.remaining())
for (i in 0 until actualAmount) {
to.put(from.get())
}/*
if (actualAmount > 0) {
val fromLimit = from.limit()
val toLimit = to.limit()
from.limit(from.position() + actualAmount)
to.limit(to.position() + actualAmount)
to.put(from)
from.limit(fromLimit)
to.limit(toLimit)
}
*/
}
val alphabet = charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
)
fun ByteBuffer.contentToString(): String {
mark()
var result = ""
while (remaining() > 0) {
val byte = get()
val upperNibble = byte.toInt() shr 4
val lowerNibble = byte.toInt() % 16
result += alphabet[(upperNibble + 16) % 16]
result += alphabet[(lowerNibble + 16) % 16]
}
reset()
return result
}
fun ByteBuffer.print() = println(contentToString())
fun copyData(from: ByteBuffer, amount: Int) = ByteBuffer.allocateDirect(amount).also {
if (amount > 0) {
copyData(from, it, amount)
it.clear()
}
}
...@@ -48,9 +48,14 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: ...@@ -48,9 +48,14 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
this.size += size this.size += size
} }
fun nextBuffer(length: Int = 1): ByteBuffer { fun <T> withBuffer(length: Int = 0, f: (ByteBuffer) -> T) : T{
ensureSpace(length) ensureSpace(length)
return bufferList.last() val buffer = bufferList.last()
val positionBefore = buffer.position()
val result = f(buffer)
val positionAfter = buffer.position()
size += (positionAfter - positionBefore)
return result
} }
fun put(value: Byte) { fun put(value: Byte) {
...@@ -99,15 +104,7 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: ...@@ -99,15 +104,7 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
ensureSpace(value.remaining()) ensureSpace(value.remaining())
while (value.remaining() > 0) { while (value.remaining() > 0) {
val buffer = bufferList.last() copyData(value, bufferList.last())
if (buffer.remaining() >= value.remaining()) {
buffer.put(value)
} else {
val oldLimit = value.limit()
value.limit(value.position() + buffer.remaining())
buffer.put(value)
value.limit(oldLimit)
}
} }
} }
...@@ -124,12 +121,16 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: ...@@ -124,12 +121,16 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
fun buffers() = sequence { fun buffers() = sequence {
for (buffer in bufferList) { for (buffer in bufferList) {
buffer.flip() buffer.flip()
val position = buffer.position()
val limit = buffer.limit()
yield(buffer) yield(buffer)
buffer.position(position)
buffer.limit(limit)
} }
} }
fun toBuffer(): ByteBuffer { fun toBuffer(): ByteBuffer {
val byteBuffer = allocate(size) val byteBuffer = allocate(bufferSize * bufferList.size)
for (buffer in bufferList) { for (buffer in bufferList) {
buffer.flip() buffer.flip()
byteBuffer.put(buffer) byteBuffer.put(buffer)
......
...@@ -30,59 +30,75 @@ class StringEncoder(charset: Charset) { ...@@ -30,59 +30,75 @@ class StringEncoder(charset: Charset) {
private val charBuffer = CharBuffer.allocate(1024) private val charBuffer = CharBuffer.allocate(1024)
private fun charBuffer(length: Int): CharBuffer = private fun charBuffer(length: Int): CharBuffer =
if (length < 1024) charBuffer if (length < 1024) charBuffer.clear()
else CharBuffer.allocate(length) else CharBuffer.allocate(length)
private fun encodingLength(length: Int, nullLimited: Boolean) = fun encode(data: String?, target: ChainedByteBuffer) {
if (nullLimited) length + 1
else length
private fun decodingLength(length: Int, nullLimited: Boolean) =
if (nullLimited) length - 1
else length
fun encode(data: String?, target: ChainedByteBuffer, nullLimited: Boolean = false) {
if (data == null) return if (data == null) return
val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) val charBuffer = charBuffer(data.length)
charBuffer.put(data) charBuffer.put(data)
if (nullLimited) charBuffer.put(0.toChar())
charBuffer.flip() charBuffer.flip()
encoder.reset() encoder.reset()
var result: CoderResult var result: CoderResult
do { do {
result = encoder.encode(charBuffer, target.nextBuffer(data.length), true) result = target.withBuffer(charBuffer.remaining()) {
encoder.encode(charBuffer, it, true)
}
} while (result == CoderResult.OVERFLOW) } while (result == CoderResult.OVERFLOW)
} }
fun encode(data: String?, nullLimited: Boolean = false): ByteBuffer { fun encode(data: String?): ByteBuffer {
if (data == null) return ByteBuffer.allocate(0) if (data == null) {
return ByteBuffer.allocateDirect(0)
}
val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) val charBuffer = charBuffer(data.length)
charBuffer.put(data) charBuffer.put(data)
if (nullLimited) charBuffer.put(0.toChar())
charBuffer.flip() charBuffer.flip()
encoder.reset() encoder.reset()
return encoder.encode(charBuffer) return encoder.encode(charBuffer)
} }
fun decode(source: ByteBuffer, length: Int, nullLimited: Boolean = false): String { fun encodeChar(data: Char?, target: ChainedByteBuffer) {
val charBuffer = charBuffer(decodingLength(length, nullLimited)) if (data == null) {
target.putShort(0)
} else {
target.putChar(data)
}
}
fun decode(source: ByteBuffer, length: Int): String {
val charBuffer = charBuffer(length)
val oldlimit = source.limit() val oldlimit = source.limit()
source.limit(decodingLength(source.position() + length, nullLimited)) source.limit(source.position() + length)
decoder.reset() decoder.reset()
decoder.decode(source, charBuffer, true) decoder.decode(source, charBuffer, true).also {
if (it.isError) {
source.position(source.position() + it.length())
}
}
source.limit(oldlimit) source.limit(oldlimit)
charBuffer.flip() charBuffer.flip()
return charBuffer.toString() return charBuffer.toString()
} }
fun decode(source: ByteBuffer, nullLimited: Boolean = false): String { fun decode(source: ByteBuffer): String {
val charBuffer = charBuffer(decodingLength(source.remaining(), nullLimited)) println("Called to decode ${source.contentToString()}")
source.limit(decodingLength(source.capacity(), nullLimited)) val charBuffer = charBuffer(source.remaining())
decoder.reset() decoder.reset()
decoder.decode(source, charBuffer, true) decoder.decode(source, charBuffer, true).also {
if (it.isError) {
println("Encountered error: $it")
source.position(source.position() + it.length())
}
}
charBuffer.flip() charBuffer.flip()
println("Result: $charBuffer")
return charBuffer.toString() return charBuffer.toString()
} }
fun decodeChar(source: ByteBuffer): Char {
return source.getChar()
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment