From 224f85323f05607fee1dfa12f58a574abd444c20 Mon Sep 17 00:00:00 2001 From: Gabriel Tofvesson Date: Wed, 25 Mar 2020 21:21:43 +0100 Subject: [PATCH] Almost completely rewrite serialization system --- SyncedVar.iml | 1 + src/net/tofvesson/data/DiffTrackedArray.kt | 2 +- src/net/tofvesson/data/RBuffer.kt | 123 +++++++++ src/net/tofvesson/data/ReadBuffer.kt | 130 --------- src/net/tofvesson/data/Serializer.kt | 28 +- src/net/tofvesson/data/SyncHandler.kt | 28 +- src/net/tofvesson/data/WBuffer.kt | 249 ++++++++++++++++++ src/net/tofvesson/data/WriteBuffer.kt | 154 ----------- src/net/tofvesson/data/WriteState.kt | 5 +- src/net/tofvesson/math/Arithmetic.kt | 29 +- src/net/tofvesson/math/Extensions.kt | 5 +- src/net/tofvesson/networking/ReliableUDP.kt | 64 ----- src/net/tofvesson/reflect/Accessible.kt | 2 +- src/net/tofvesson/reflect/Field.kt | 15 +- .../serializers/DiffTrackedSerializer.kt | 72 +++-- .../tofvesson/serializers/MathSerializer.kt | 43 +-- .../serializers/PrimitiveArraySerializer.kt | 35 ++- .../serializers/PrimitiveSerializers.kt | 11 +- {src => test}/Main.java | 9 +- 19 files changed, 558 insertions(+), 447 deletions(-) create mode 100644 src/net/tofvesson/data/RBuffer.kt delete mode 100644 src/net/tofvesson/data/ReadBuffer.kt create mode 100644 src/net/tofvesson/data/WBuffer.kt delete mode 100644 src/net/tofvesson/data/WriteBuffer.kt delete mode 100644 src/net/tofvesson/networking/ReliableUDP.kt rename {src => test}/Main.java (94%) diff --git a/SyncedVar.iml b/SyncedVar.iml index 245d342..3114cdf 100644 --- a/SyncedVar.iml +++ b/SyncedVar.iml @@ -4,6 +4,7 @@ + diff --git a/src/net/tofvesson/data/DiffTrackedArray.kt b/src/net/tofvesson/data/DiffTrackedArray.kt index e89fbd1..6e9f46e 100644 --- a/src/net/tofvesson/data/DiffTrackedArray.kt +++ b/src/net/tofvesson/data/DiffTrackedArray.kt @@ -3,7 +3,7 @@ package net.tofvesson.data class DiffTrackedArray(val elementType: Class, val size: Int, gen: (Int) -> T) { val values: Array = java.lang.reflect.Array.newInstance(elementType, size) as Array - val changeMap = Array(size) {false} + val changeMap = BooleanArray(size) {false} init{ for(index in 0 until size) diff --git a/src/net/tofvesson/data/RBuffer.kt b/src/net/tofvesson/data/RBuffer.kt new file mode 100644 index 0000000..d4811ac --- /dev/null +++ b/src/net/tofvesson/data/RBuffer.kt @@ -0,0 +1,123 @@ +package net.tofvesson.data + +import net.tofvesson.math.* +import java.nio.ByteBuffer + +class RBuffer(val buffer: ByteBuffer, bitOffset: Long) +{ + /* + * Backing fields for indices + */ + private var bits = 0L + private var bytes = 0 + + init { + bytes = (readPackedMisaligned((bitOffset ushr 3).toInt(), (bitOffset and 7).toInt()).toInt() + bitOffset.bitIndexToBytes()).toInt() + bits += (varIntSize(bytes.toLong()) * 8) + bitOffset + } + + fun readBit() = readBitAt(bits++) + + fun readByte() = buffer.get(bytes++) + + fun readShort(): Short { + val result = buffer.getShort(bytes) + bytes += 2 + + return result + } + + fun readInt(): Int { + val result = buffer.getInt(bytes) + bytes += 4 + + return result + } + + fun readLong(): Long { + val result = buffer.getLong(bytes) + bytes += 8 + + return result + } + + fun readFloat(): Float { + val result = buffer.getFloat(bytes) + bytes += 4 + + return result + } + + fun readDouble(): Double { + val result = buffer.getDouble(bytes) + bytes += 8 + + return result + } + + + fun readPackedShort(noZigZag: Boolean = false) = readPackedLong(noZigZag).toShort() + fun readPackedInt(noZigZag: Boolean = false) = readPackedLong(noZigZag).toInt() + fun readPackedLong(noZigZag: Boolean = false): Long { + //doBoundaryCheck(1) + val header: Long = buffer[bytes++].toLong() and 0xFF + if (header <= 240L) return if(noZigZag) header else zigZagDecode(header) + if (header <= 248L){ + //doBoundaryCheck(2) + val res = 240L + ((header - 241L).shl(8)) + (buffer[bytes++].toLong() and 0xFF) + return if(noZigZag) res else zigZagDecode(res) + } + if (header == 249L){ + //doBoundaryCheck(3) + val res = 2288 + ((buffer[bytes++].toLong() and 0xFF).shl(8)) + (buffer[bytes++].toLong() and 0xFF) + return if(noZigZag) res else zigZagDecode(res) + } + val hdr = header - 247 + //doBoundaryCheck(hdr.toInt()) + var res = (buffer[bytes++].toLong() and 0xFF).or(((buffer[bytes++].toLong() and 0xFF).shl(8)).or((buffer[bytes++].toLong() and 0xFF).shl(16))) + var cmp = 2 + while (hdr > ++cmp) + res = res.or((buffer[bytes++].toLong() and 0xFF).shl(cmp.shl(3))) + + return if(noZigZag) res else zigZagDecode(res) + } + + fun readPackedFloat(noSwapEndian: Boolean = false): Float { + val readVal = readPackedInt(true) + return if(noSwapEndian) intToFloat(readVal) else intToFloat(swapEndian(readVal)) + } + + fun readPackedDouble(noSwapEndian: Boolean = false): Double { + val readVal = readPackedLong(true) + return if(noSwapEndian) longToDouble(readVal) else longToDouble(swapEndian(readVal)) + } + + + private fun readMisaligned(index: Int, shift: Int) = + ((buffer[index].toInt() and 0xFF ushr shift) or (buffer[index + 1].toInt() and 0xFF shl (8 - shift))).toByte() + + private fun readPackedMisaligned(index: Int, shift: Int): Long { + var idx = index + val header: Long = readMisaligned(idx++, shift).toLong() and 0xFF + if (header <= 240L) return header + if (header <= 248L){ + return 240L + ((header - 241L).shl(8)) + (readMisaligned(idx, shift).toLong() and 0xFF) + } + if (header == 249L){ + return 2288 + ((readMisaligned(idx++, shift).toLong() and 0xFF).shl(8)) + (readMisaligned(idx, shift).toLong() and 0xFF) + } + val hdr = header - 247 + //doBoundaryCheck(hdr.toInt()) + var res = (readMisaligned(idx++, shift).toLong() and 0xFF).or(((readMisaligned(idx++, shift).toLong() and 0xFF).shl(8)).or((readMisaligned(idx++, shift).toLong() and 0xFF).shl(16))) + var cmp = 2 + while (hdr > ++cmp) + res = res.or((readMisaligned(idx++, shift).toLong() and 0xFF).shl(cmp.shl(3))) + + return res + } + + private fun readBitAt(bitIndex: Long) = + (buffer[(bitIndex ushr 3).toInt()].toInt() ushr (bitIndex and 7).toInt()) and 1 != 0 + + private fun Long.bitIndexToBytes() = (this ushr 3) + ((this or (this ushr 1) or (this ushr 2)) and 1) +} \ No newline at end of file diff --git a/src/net/tofvesson/data/ReadBuffer.kt b/src/net/tofvesson/data/ReadBuffer.kt deleted file mode 100644 index 5d40a41..0000000 --- a/src/net/tofvesson/data/ReadBuffer.kt +++ /dev/null @@ -1,130 +0,0 @@ -package net.tofvesson.data - -import net.tofvesson.exception.InsufficientCapacityException -import net.tofvesson.math.* -import java.nio.ByteBuffer - -@Suppress("MemberVisibilityCanBePrivate", "unused") -class ReadBuffer(state: WriteState, private val _buffer: ByteBuffer, bufferBitOffset: Int = 0) { - - /* - Buffer layout: - {BufferBitOffset}[Header][Bits]{BitOffset}[Bytes] - BufferBitOffset is optional and usually set to 0 - BitOffset is between 0 and 7 bits long (to realign data to the next free byte) - */ - private var headerIndex = bufferBitOffset - private var bitOffset = bufferBitOffset + state.header - private var byteIndex = state.computeBitFieldOffset(bufferBitOffset) - - // Maximum size for the above values - private val maxHeaderSize = bufferBitOffset + state.header - private val maxBitOffset: Int - private val maxByteOffset = _buffer.capacity() - - val buffer: ByteBuffer - get() = this._buffer - - init { - if(_buffer.capacity() - (bufferBitOffset ushr 3) - bufferBitOffset.collapseLowerByte() < state.computeBitFieldOffset()) - throw InsufficientCapacityException() - byteIndex = readPackedInt(true) - maxBitOffset = bufferBitOffset + byteIndex*8 - } - - - private fun readBit(head: Boolean): Boolean { - if(head && headerIndex >= maxHeaderSize) - throw IndexOutOfBoundsException("Attempt to read more headers than available space permits!") - - val index = (if(head) headerIndex else bitOffset) ushr 3 - val shift = (if(head) headerIndex else bitOffset) and 7 - if(head) ++headerIndex - else ++bitOffset - return (_buffer[index].toInt() and (1 shl shift)) != 0 - } - - fun readHeader() = readBit(true) - fun readBit() = readBit(false) - - fun readByte(): Byte { - //doBoundaryCheck(1) - return _buffer.get(byteIndex++) - } - - fun readShort(): Short { - //doBoundaryCheck(2) - val res = _buffer.getShort(byteIndex) - byteIndex += 2 - return res - } - - fun readInt(): Int { - //doBoundaryCheck(4) - val res = _buffer.getInt(byteIndex) - byteIndex += 4 - return res - } - - fun readLong(): Long { - //doBoundaryCheck(8) - val res = _buffer.getLong(byteIndex) - byteIndex += 8 - return res - } - - fun readFloat(): Float { - //doBoundaryCheck(4) - val res = _buffer.getFloat(byteIndex) - byteIndex += 4 - return res - } - - fun readDouble(): Double { - //doBoundaryCheck(8) - val res = _buffer.getDouble(byteIndex) - byteIndex += 8 - return res - } - - fun readPackedShort(noZigZag: Boolean = false) = readPackedLong(noZigZag).toShort() - fun readPackedInt(noZigZag: Boolean = false) = readPackedLong(noZigZag).toInt() - fun readPackedLong(noZigZag: Boolean = false): Long { - //doBoundaryCheck(1) - val header: Long = buffer[byteIndex++].toLong() and 0xFF - if (header <= 240L) return if(noZigZag) header else zigZagDecode(header) - if (header <= 248L){ - //doBoundaryCheck(2) - val res = 240L + ((header - 241L).shl(8)) + (buffer[byteIndex++].toLong() and 0xFF) - return if(noZigZag) res else zigZagDecode(res) - } - if (header == 249L){ - //doBoundaryCheck(3) - val res = 2288 + ((buffer[byteIndex++].toLong() and 0xFF).shl(8)) + (buffer[byteIndex++].toLong() and 0xFF) - return if(noZigZag) res else zigZagDecode(res) - } - val hdr = header - 247 - //doBoundaryCheck(hdr.toInt()) - var res = (buffer[byteIndex++].toLong() and 0xFF).or(((buffer[byteIndex++].toLong() and 0xFF).shl(8)).or((buffer[byteIndex++].toLong() and 0xFF).shl(16))) - var cmp = 2 - while (hdr > ++cmp) - res = res.or((buffer[byteIndex++].toLong() and 0xFF).shl(cmp.shl(3))) - - return if(noZigZag) res else zigZagDecode(res) - } - - fun readPackedFloat(noSwapEndian: Boolean = false): Float { - val readVal = readPackedInt(true) - return if(noSwapEndian) intToFloat(readVal) else intToFloat(swapEndian(readVal)) - } - - fun readPackedDouble(noSwapEndian: Boolean = false): Double { - val readVal = readPackedLong(true) - return if(noSwapEndian) longToDouble(readVal) else longToDouble(swapEndian(readVal)) - } - - private fun doBoundaryCheck(byteCount: Int) { - if(byteIndex + byteCount > maxByteOffset) - throw IndexOutOfBoundsException("Attempt to read value past maximum range!") - } -} \ No newline at end of file diff --git a/src/net/tofvesson/data/Serializer.kt b/src/net/tofvesson/data/Serializer.kt index e92d42d..effa0f3 100644 --- a/src/net/tofvesson/data/Serializer.kt +++ b/src/net/tofvesson/data/Serializer.kt @@ -2,6 +2,7 @@ package net.tofvesson.data import net.tofvesson.annotation.SyncFlag import net.tofvesson.exception.UnsupportedTypeException +import net.tofvesson.reflect.access import java.lang.reflect.Field import java.util.* @@ -16,7 +17,9 @@ abstract class Serializer(registeredTypes: Array>) { flags: Array, owner: Any?, state: WriteState - ) = computeSizeExplicit(field, flags, owner, state, field.type) + ) = if (canSerialize(field.get(owner), flags, field.type)) + computeSizeExplicit(field, flags, owner, state, field.type) + else Unit abstract fun computeSizeExplicit( field: Field, @@ -34,14 +37,16 @@ abstract class Serializer(registeredTypes: Array>) { field: Field, flags: Array, owner: Any?, - writeBuffer: WriteBuffer - ) = serializeExplicit(field, flags, owner, writeBuffer, field.type) + writeBuffer: WBuffer + ) = if (canSerialize(field.get(owner), flags, field.type)) + serializeExplicit(field.access(), flags, owner, writeBuffer, field.type) + else Unit abstract fun serializeExplicit( field: Field, flags: Array, owner: Any?, - writeBuffer: WriteBuffer, + writeBuffer: WBuffer, fieldType: Class<*> ) @@ -53,20 +58,25 @@ abstract class Serializer(registeredTypes: Array>) { field: Field, flags: Array, owner: Any?, - readBuffer: ReadBuffer - ) = deserializeExplicit(field, flags, owner, readBuffer, field.type) + readBuffer: RBuffer + ) = if (canDeserialize(field.get(owner), flags, field.type)) + deserializeExplicit(field.access(), flags, owner, readBuffer, field.type) + else Unit abstract fun deserializeExplicit( field: Field, flags: Array, owner: Any?, - readBuffer: ReadBuffer, + readBuffer: RBuffer, fieldType: Class<*> ) + abstract fun canSerialize(obj: Any?, flags: Array, type: Class<*>): Boolean + open fun canDeserialize(obj: Any?, flags: Array, type: Class<*>) = canSerialize(obj, flags, type) + fun getRegisteredTypes(): Array> = Arrays.copyOf(registeredTypes, registeredTypes.size) - fun canSerialize(field: Field): Boolean = registeredTypes.contains(field.type) - fun canSerialize(type: Class<*>): Boolean = registeredTypes.contains(type) + fun canSerialize(field: Field) = canSerialize(field.type) + open fun canSerialize(type: Class<*>): Boolean = registeredTypes.contains(type) protected fun throwInvalidType(type: Class<*>): Nothing = throw UnsupportedTypeException("Type ${this.javaClass} cannot serialize $type") } \ No newline at end of file diff --git a/src/net/tofvesson/data/SyncHandler.kt b/src/net/tofvesson/data/SyncHandler.kt index e5f9579..0fbd969 100644 --- a/src/net/tofvesson/data/SyncHandler.kt +++ b/src/net/tofvesson/data/SyncHandler.kt @@ -41,12 +41,12 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) { } fun clearSerializers() = serializers.clear() - fun getRegisteredSerializers() = serializers.toArray() - fun getCompatibleSerializer(type: Class<*>): Serializer { + fun getRegisteredSerializers() = serializers.toTypedArray() + fun getCompatibleSerializer(type: Class<*>): Serializer? { for(serializer in serializers) if(serializer.canSerialize(type)) return serializer - throw UnsupportedTypeException("Cannot find a compatible serializer for $type") + return null } @@ -78,7 +78,7 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) { for(entry in toSync) if(entry is Class<*>) computeClassSize(entry, writeState) else computeObjectSize(entry, writeState) - val writeBuffer = WriteBuffer(writeState) + val writeBuffer = WBuffer(null, 0, writeState) for(entry in toSync) if(entry is Class<*>) readClass(entry, writeBuffer) else readObject(entry, writeBuffer) @@ -91,7 +91,7 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) { for(entry in toSync) if(entry is Class<*>) computeClassSize(entry, writeState) else computeObjectSize(entry, writeState) - val readBuffer = ReadBuffer(writeState, ByteBuffer.wrap(syncData), bitOffset) + val readBuffer = RBuffer(ByteBuffer.wrap(syncData), bitOffset.toLong()) for(entry in toSync) if(entry is Class<*>) writeClass(entry, readBuffer) else writeObject(entry, readBuffer) @@ -132,22 +132,22 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) { private fun computeTypeSize(type: Class<*>, value: Any?, writeState: WriteState) { for(field in collectSyncable(type, value == null)) getCompatibleSerializer(field.access().type) - .computeSize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeState) + ?.computeSize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeState) } - private fun readObject(value: Any, writeBuffer: WriteBuffer) = readType(value.javaClass, value, writeBuffer) - private fun readClass(value: Class<*>, writeBuffer: WriteBuffer) = readType(value, null, writeBuffer) - private fun readType(type: Class<*>, value: Any?, writeBuffer: WriteBuffer) { + private fun readObject(value: Any, writeBuffer: WBuffer) = readType(value.javaClass, value, writeBuffer) + private fun readClass(value: Class<*>, writeBuffer: WBuffer) = readType(value, null, writeBuffer) + private fun readType(type: Class<*>, value: Any?, writeBuffer: WBuffer) { for(field in collectSyncable(type, value == null)) getCompatibleSerializer(field.type) - .serialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeBuffer) + ?.serialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeBuffer) } - private fun writeObject(value: Any, readBuffer: ReadBuffer) = writeType(value.javaClass, value, readBuffer) - private fun writeClass(value: Class<*>, readBuffer: ReadBuffer) = writeType(value, null, readBuffer) - private fun writeType(type: Class<*>, value: Any?, readBuffer: ReadBuffer) { + private fun writeObject(value: Any, readBuffer: RBuffer) = writeType(value.javaClass, value, readBuffer) + private fun writeClass(value: Class<*>, readBuffer: RBuffer) = writeType(value, null, readBuffer) + private fun writeType(type: Class<*>, value: Any?, readBuffer: RBuffer) { for(field in collectSyncable(type, value == null)) getCompatibleSerializer(field.type) - .deserialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, readBuffer) + ?.deserialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, readBuffer) } } \ No newline at end of file diff --git a/src/net/tofvesson/data/WBuffer.kt b/src/net/tofvesson/data/WBuffer.kt new file mode 100644 index 0000000..6a99dc1 --- /dev/null +++ b/src/net/tofvesson/data/WBuffer.kt @@ -0,0 +1,249 @@ +package net.tofvesson.data + +import net.tofvesson.math.* +import java.nio.ByteBuffer +import kotlin.experimental.and +import kotlin.experimental.inv +import kotlin.experimental.or + +class WBuffer(_buffer: ByteBuffer?, bufferBitOffset: Long, private val maxima: WriteState) +{ + /* + * Base indices + * Header base comes immediately after a special (not necessarily byte-aligned) byteBase index header + */ + /** + * Base bit index for writing metadata + */ + val metaBase = bufferBitOffset + val bitsBase = metaBase + (varIntSize((maxima.bits).toLong().bitIndexToBytes()) * 8) + val bytesBase = (bitsBase + maxima.bits).bitIndexToBytes() + + /* + * Backing fields for indices + */ + private var bits = 0L + private var bytes = 0 + + /* + * Indices + */ + var bitIndex + get() = bitsBase + bits + set(value) { + if (value < bitsBase || value > (bitsBase + maxima.bits)) + throw IndexOutOfBoundsException("Attempt to index bits beyond acceptable range") + + bits = value - bitsBase + } + + var byteIndex + get() = (bytesBase + bytes).toInt() + set(value) { + if (value < bytesBase || value > maxima.bytes + bytesBase + 1) + throw IndexOutOfBoundsException("Attempt to index bytes beyond acceptable range") + + bytes = (value - bytesBase).toInt() + } + + + val buffer: ByteBuffer + + init { + /* + * If given buffer is null, allocate a new buffer + * If given buffer is too small, allocate a new buffer and copy given buffer to the new buffer + * If given buffer is large enough, simply assign buffer field to said buffer + */ + when { + _buffer == null -> buffer = ByteBuffer.allocate((bytesBase + maxima.bytes).toInt()) + _buffer.capacity() < bytesBase + maxima.bytes -> { + buffer = ByteBuffer.allocate((bytesBase + maxima.bytes).toInt()) + buffer.put(_buffer) + } + else -> buffer = _buffer + } + + writePackedMisaligned((bufferBitOffset ushr 3).toInt(), (bufferBitOffset and 7).toInt(), bytesBase) + } + + fun writeBit(value: Boolean): WBuffer { + ensureBitBounds() + + writeBitAt(bitIndex, value) + ++bits + + return this + } + + fun writeByte(value: Byte): WBuffer { + ensureByteBounds(1) + + buffer.put(byteIndex, value) + ++bytes + + return this + } + + fun writeShort(value: Short): WBuffer { + ensureByteBounds(2) + + buffer.putShort(byteIndex, value) + bytes += 2 + + return this + } + + fun writeInt(value: Int): WBuffer { + ensureByteBounds(4) + + buffer.putInt(byteIndex, value) + bytes += 4 + + return this + } + + fun writeLong(value: Long): WBuffer { + ensureByteBounds(8) + + buffer.putLong(byteIndex, value) + bytes += 8 + + return this + } + + fun writeFloat(value: Float): WBuffer { + ensureByteBounds(4) + + buffer.putFloat(byteIndex, value) + bytes += 4 + + return this + } + + fun writeDouble(value: Double): WBuffer { + ensureByteBounds(8) + + buffer.putDouble(byteIndex, value) + bytes += 8 + + return this + } + + fun writePackedShort(value: Short, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag) + fun writePackedInt(value: Int, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag) + fun writePackedLong(value: Long, noZigZag: Boolean = false): WBuffer { + val toWrite = if(noZigZag) value else zigZagEncode(value) + val size = varIntSize(toWrite) + + ensureByteBounds(size.toLong()) + + when(toWrite) { + in 0..240 -> buffer.put(byteIndex, toWrite.toByte()) + in 241..2287 -> { + buffer.put(byteIndex, (((toWrite - 240) ushr 8) + 241).toByte()) + buffer.put(byteIndex + 1, (toWrite - 240).toByte()) + } + in 2288..67823 -> { + buffer.put(byteIndex, 249.toByte()) + buffer.put(byteIndex + 1, ((toWrite - 2288) ushr 8).toByte()) + buffer.put(byteIndex + 2, (toWrite - 2288).toByte()) + } + else -> { + var header = 255 + var match = 0x00FF_FFFF_FFFF_FFFFL + while (toWrite in 67824..match) { + --header + match = match ushr 8 + } + buffer.put(byteIndex, header.toByte()) + val max = header - 247 + for (i in 0 until max) + buffer.put(byteIndex + 1 + i, (toWrite ushr (i shl 3)).toByte()) + } + } + + bytes += size + + return this + } + + fun writePackedFloat(value: Float, noSwapEndian: Boolean = false): WBuffer { + val write = + if(noSwapEndian) bitConvert(floatToInt(value)) + else bitConvert(swapEndian(floatToInt(value))) + return writePackedLong(write, true) + } + + fun writePackedDouble(value: Double, noSwapEndian: Boolean = false): WBuffer { + val write = + if(noSwapEndian) doubleToLong(value) + else swapEndian(doubleToLong(value)) + return writePackedLong(write, true) + } + + private fun writeMisaligned(index: Int, shift: Int, value: Byte) { + if(shift == 0) + { + buffer.put(index, value) + return + } + + val byte = buffer[index] and (-1 shr (8 - shift)).toByte().inv() + val nextByte = buffer[index + 1] and (-1 shl shift).toByte() + + buffer.put(index, byte or (value.toInt() shl (8 - shift) and 0xFF).toByte()) + buffer.put(index + 1, nextByte or (value.toInt() and 0xFF ushr shift).toByte()) + } + + private fun writePackedMisaligned(index: Int, shift: Int, value: Long) { + when (value) { + in 0..240 -> writeMisaligned(index, shift, value.toByte()) + in 241..2287 -> { + writeMisaligned(index, shift, (((value - 240) ushr 8) + 241).toByte()) + writeMisaligned(index + 1, shift, (value - 240).toByte()) + } + in 2288..67823 -> { + writeMisaligned(index, shift, 249.toByte()) + writeMisaligned(index + 1, shift, ((value - 2288) ushr 8).toByte()) + writeMisaligned(index + 2, shift, (value - 2288).toByte()) + } + else -> { + var header = 255 + var match = 0x00FF_FFFF_FFFF_FFFFL + while (value in 67824..match) { + --header + match = match ushr 8 + } + writeMisaligned(index, shift, header.toByte()) + val max = header - 247 + for (i in 0 until max) + writeMisaligned(index + 1 + i, shift, (value ushr (i shl 3)).toByte()) + } + } + } + + + private fun writeBitAt(bitIndex: Long, value: Boolean) + { + val byteIndex = (bitIndex ushr 3).toInt() + val byteShift = (bitIndex and 7).toInt() + + val byte = buffer[byteIndex] + + if (value) + buffer.put(byteIndex, byte or (1 shl byteShift).toByte()) + else + buffer.put(byteIndex, byte and (1 shl byteShift).toByte().inv()) + } + + private fun ensureBitBounds() = ensureBounds(bits, 1, maxima.bits.toLong()) + private fun ensureByteBounds(count: Long) = ensureBounds(bytes.toLong(), count, maxima.bytes.toLong()) + private fun ensureBounds(index: Long, increment: Long, maximum: Long) + { + if (index + increment > maximum) + throw IndexOutOfBoundsException("Attempt to write past boundary!") + } + + private fun Long.bitIndexToBytes() = (this ushr 3) + ((this or (this ushr 1) or (this ushr 2)) and 1) +} \ No newline at end of file diff --git a/src/net/tofvesson/data/WriteBuffer.kt b/src/net/tofvesson/data/WriteBuffer.kt deleted file mode 100644 index 224a991..0000000 --- a/src/net/tofvesson/data/WriteBuffer.kt +++ /dev/null @@ -1,154 +0,0 @@ -package net.tofvesson.data - -import net.tofvesson.exception.InsufficientCapacityException -import net.tofvesson.math.* -import java.nio.ByteBuffer -import kotlin.experimental.and -import kotlin.experimental.or - -@Suppress("MemberVisibilityCanBePrivate", "unused") -class WriteBuffer(state: WriteState, _buffer: ByteBuffer? = null, bufferBitOffset: Int = 0) { - /* - Buffer layout: - {BufferBitOffset}[Header][Bits]{BitOffset}[Bytes] - BufferBitOffset is optional and usually set to 0 - BitOffset is between 0 and 7 bits long (to realign data to the next free byte) - */ - private var headerIndex = bufferBitOffset - private var bitOffset = bufferBitOffset + state.header - private var byteIndex = state.computeBitFieldOffset(bufferBitOffset) - - // Maximum size for the above values - private val maxHeaderSize = bufferBitOffset + state.header - private val maxBitOffset = bufferBitOffset + state.header + state.bits - private val maxByteOffset = state.computeRequiredBytes(0, bufferBitOffset) - - val buffer: ByteBuffer - - init{ - buffer = _buffer ?: ByteBuffer.allocate(state.computeRequiredBytes() + varIntSize(byteIndex.toLong())) - if(buffer.capacity() + (bufferBitOffset ushr 3) + bufferBitOffset.collapseLowerByte() + varIntSize(byteIndex.toLong()) < state.computeRequiredBytes()) - throw InsufficientCapacityException() - writePackedInt(byteIndex + varIntSize(byteIndex.toLong()), true) - } - - private fun writeBit(bit: Boolean, head: Boolean){ - if((head && headerIndex >= maxHeaderSize) || (!head && bitOffset >= maxBitOffset)) - throw IndexOutOfBoundsException("Attempt to write more ${if(head)"headers" else "bits"} than available space permits!") - - val index = (if(head) headerIndex else bitOffset) ushr 3 - val shift = (if(head) headerIndex else bitOffset) and 7 - buffer.put(index, (buffer[index] and (1 shl shift).inv().toByte()) or (bit.toNumber() shl shift).toByte()) - if(head) ++headerIndex - else ++bitOffset - } - - fun writeHeader(bit: Boolean): WriteBuffer { - writeBit(bit, true) - return this - } - - fun writeBit(bit: Boolean): WriteBuffer { - writeBit(bit, false) - return this - } - - fun writeByte(value: Byte): WriteBuffer { - doBoundaryCheck(1) - buffer.put(byteIndex, value) - ++byteIndex - return this - } - - fun writeShort(value: Short): WriteBuffer { - doBoundaryCheck(2) - buffer.putShort(byteIndex, value) - byteIndex += 2 - return this - } - - fun writeInt(value: Int): WriteBuffer { - doBoundaryCheck(4) - buffer.putInt(byteIndex, value) - byteIndex += 4 - return this - } - - fun writeLong(value: Long): WriteBuffer { - doBoundaryCheck(8) - buffer.putLong(byteIndex, value) - byteIndex += 8 - return this - } - - fun writeFloat(value: Float): WriteBuffer { - doBoundaryCheck(4) - buffer.putFloat(byteIndex, value) - byteIndex += 4 - return this - } - - fun writeDouble(value: Double): WriteBuffer { - doBoundaryCheck(8) - buffer.putDouble(byteIndex, value) - byteIndex += 8 - return this - } - - fun writePackedShort(value: Short, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag) - fun writePackedInt(value: Int, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag) - fun writePackedLong(value: Long, noZigZag: Boolean = false): WriteBuffer { - val toWrite = if(noZigZag) value else zigZagEncode(value) - val size = varIntSize(toWrite) - - doBoundaryCheck(size) - - when(toWrite) { - in 0..240 -> buffer.put(byteIndex, toWrite.toByte()) - in 241..2287 -> { - buffer.put(byteIndex, (((toWrite - 240) shr 8) + 241).toByte()) - buffer.put(byteIndex + 1, (toWrite - 240).toByte()) - } - in 2288..67823 -> { - buffer.put(byteIndex, 249.toByte()) - buffer.put(byteIndex + 1, ((toWrite - 2288) shr 8).toByte()) - buffer.put(byteIndex + 2, (toWrite - 2288).toByte()) - } - else -> { - var header = 255 - var match = 0x00FF_FFFF_FFFF_FFFFL - while (toWrite in 67824..match) { - --header - match = match shr 8 - } - buffer.put(byteIndex, header.toByte()) - val max = header - 247 - for (i in 0 until max) - buffer.put(byteIndex + 1 + i, (toWrite shr (i shl 3)).toByte()) - } - } - - byteIndex += size - - return this - } - - fun writePackedFloat(value: Float, noSwapEndian: Boolean = false): WriteBuffer { - val write = - if(noSwapEndian) bitConvert(floatToInt(value)) - else bitConvert(swapEndian(floatToInt(value))) - return writePackedLong(write, true) - } - - fun writePackedDouble(value: Double, noSwapEndian: Boolean = false): WriteBuffer { - val write = - if(noSwapEndian) doubleToLong(value) - else swapEndian(doubleToLong(value)) - return writePackedLong(write, true) - } - - private fun doBoundaryCheck(byteCount: Int) { - if(byteIndex + byteCount > maxByteOffset) - throw IndexOutOfBoundsException("Attempt to write value past maximum range!") - } -} \ No newline at end of file diff --git a/src/net/tofvesson/data/WriteState.kt b/src/net/tofvesson/data/WriteState.kt index 63a19fc..a5f9b7a 100644 --- a/src/net/tofvesson/data/WriteState.kt +++ b/src/net/tofvesson/data/WriteState.kt @@ -7,16 +7,13 @@ data class WriteState(private var _bytes: Int, private var _bits: Int, private v private set var bits: Int = _bits private set - var header: Int = _header - private set fun registerBytes(bytes: Int): WriteState { this.bytes += bytes; return this } fun registerBits(bits: Int) : WriteState { this.bits += bits; return this } - fun registerHeader(header: Int): WriteState { this.header += header; return this } fun computeRequiredBytes(additionalBytes: Int = 0, additionalBits: Int = 0) = bytes + additionalBytes + computeBitFieldOffset(additionalBits) - fun computeBitFieldOffset(additionalBits: Int = 0) = roundUpBitsToBytes(bits + header + additionalBits) + fun computeBitFieldOffset(additionalBits: Int = 0) = roundUpBitsToBytes(bits + additionalBits) private fun roundUpBitsToBytes(bits: Int) = (bits ushr 3) + bits.collapseLowerByte() } \ No newline at end of file diff --git a/src/net/tofvesson/math/Arithmetic.kt b/src/net/tofvesson/math/Arithmetic.kt index 46912d0..2ac0603 100644 --- a/src/net/tofvesson/math/Arithmetic.kt +++ b/src/net/tofvesson/math/Arithmetic.kt @@ -6,6 +6,7 @@ import kotlin.math.roundToInt fun varIntSize(value: Long): Int = when { + value < 0 -> 9 value <= 240 -> 1 value <= 2287 -> 2 value <= 67823 -> 3 @@ -64,19 +65,19 @@ fun writeVarInt(buffer: ByteBuffer, offset: Int, value: Long){ } buffer.put(offset, header.toByte()) val max = header - 247 - for (i in 0..(max-1)) buffer.put(offset+i+1, (value shr (i shl 3)).toByte()) + for (i in 0..(max-1)) buffer.put(offset+i+1, (value ushr (i shl 3)).toByte()) } } } fun writeInt(buffer: ByteArray, offset: Int, value: Long, bytes: Int){ for(i in 0..(bytes-1)) - buffer[offset+i] = (value shr (8*i)).toByte() + buffer[offset+i] = (value ushr (8*i)).toByte() } fun writeInt(buffer: ByteBuffer, offset: Int, value: Long, bytes: Int){ for(i in 0..(bytes-1)) - buffer.put(offset+i, (value shr (8*i)).toByte()) + buffer.put(offset+i, (value ushr (8*i)).toByte()) } fun readVarInt(buffer: ByteArray, offset: Int): Long { @@ -106,13 +107,13 @@ fun readVarInt(buffer: ByteBuffer, offset: Int): Long { } fun writeBit(bit: Boolean, buffer: ByteArray, index: Int){ - buffer[index shr 3] = buffer[index shr 3].or(((if(bit) 1 else 0) shl (index and 7)).toByte()) + buffer[index ushr 3] = buffer[index ushr 3].or(((if(bit) 1 else 0) shl (index and 7)).toByte()) } -fun readBit(buffer: ByteArray, index: Int): Boolean = buffer[index shr 3].toInt() and (1 shl (index and 7)) != 0 +fun readBit(buffer: ByteArray, index: Int): Boolean = buffer[index ushr 3].toInt() and (1 shl (index and 7)) != 0 fun writeBit(bit: Boolean, buffer: ByteBuffer, index: Int){ - buffer.put(index shr 3, buffer[index shr 3] or (((if(bit) 1 else 0) shl (index and 7)).toByte())) + buffer.put(index ushr 3, buffer[index ushr 3] or (((if(bit) 1 else 0) shl (index and 7)).toByte())) } -fun readBit(buffer: ByteBuffer, index: Int): Boolean = buffer[index shr 3].toInt() and (1 shl (index and 7)) != 0 +fun readBit(buffer: ByteBuffer, index: Int): Boolean = buffer[index ushr 3].toInt() and (1 shl (index and 7)) != 0 private val converter = ByteBuffer.allocateDirect(8) @@ -140,17 +141,17 @@ fun longToDouble(value: Long): Double = return@synchronized converter.getDouble(0) } -fun swapEndian(value: Short) = ((value.toInt() shl 8) or ((value.toInt() shr 8) and 255)).toShort() +fun swapEndian(value: Short) = ((value.toInt() shl 8) or ((value.toInt() ushr 8) and 255)).toShort() fun swapEndian(value: Int) = - ((value shr 24) and 0xFF) or - ((value shr 8) and 0xFF00) or + ((value ushr 24) and 0xFF) or + ((value ushr 8) and 0xFF00) or ((value shl 24) and -16777216) or ((value shl 8) and 0xFF0000) fun swapEndian(value: Long) = ((value shr 56) and 0xFFL) or - ((value shr 40) and 0xFF00L) or - ((value shr 24) and 0xFF0000L) or - ((value shr 8) and 0xFF000000L) or + ((value ushr 40) and 0xFF00L) or + ((value ushr 24) and 0xFF0000L) or + ((value ushr 8) and 0xFF000000L) or ((value shl 56) and -72057594037927936L) or ((value shl 40) and 0xFF000000000000L) or ((value shl 24) and 0xFF0000000000L) or @@ -159,7 +160,7 @@ fun swapEndian(value: Long) = fun bitConvert(value: Int): Long = value.toLong() and 0xFFFFFFFFL fun zigZagEncode(value: Long): Long = (value shl 1) xor (value shr 63) -fun zigZagDecode(value: Long): Long = (value shr 1) xor ((value shl 63) shr 63) +fun zigZagDecode(value: Long): Long = (value ushr 1) xor ((value shl 63) shr 63) fun Float.encodeRotation(byteCount: Int): Int { if(this < 0 || this > 360) throw RotationOutOfBoundsException() diff --git a/src/net/tofvesson/math/Extensions.kt b/src/net/tofvesson/math/Extensions.kt index ebe1bf3..ab189bd 100644 --- a/src/net/tofvesson/math/Extensions.kt +++ b/src/net/tofvesson/math/Extensions.kt @@ -1,7 +1,7 @@ package net.tofvesson.math fun Boolean.toNumber() = if(this) 1 else 0 -fun Number.toBoolean() = this!=0 +fun Number.toBoolean() = this != 0 fun Int.collapseLowerByte(): Int = ((this ushr 7) or @@ -10,4 +10,5 @@ fun Int.collapseLowerByte(): Int = (this ushr 4) or (this ushr 3) or (this ushr 2) or - (this ushr 1)) and 1 \ No newline at end of file + (this ushr 1) or + this) and 1 \ No newline at end of file diff --git a/src/net/tofvesson/networking/ReliableUDP.kt b/src/net/tofvesson/networking/ReliableUDP.kt deleted file mode 100644 index 51c5dbb..0000000 --- a/src/net/tofvesson/networking/ReliableUDP.kt +++ /dev/null @@ -1,64 +0,0 @@ -package net.tofvesson.networking - -import net.tofvesson.reflect.access -import java.net.DatagramPacket -import java.net.DatagramSocket -import java.net.InetAddress -import java.util.concurrent.atomic.AtomicBoolean - -class ReliableUDP(address: InetAddress, port: Short, private val onAccept: (ByteArray, Int, Int) -> Unit, private val automaticAccept: Boolean, acceptTimeout: Long = 10L) { - - private enum class PacketType(val bits: Int = (javaClass.getDeclaredField("\$VALUES").access().get(null) as Array<*>).indexOf(this)) { - DATA, ACK, FIN; - companion object { - val fieldSize = (Math.log((javaClass.getDeclaredField("\$VALUES").access().get(null) as Array<*>).size.toDouble()) / Math.log(2.0)).toInt() + 1 - } - } - - private var socket = DatagramSocket() - private val sync: Thread? = if(automaticAccept) Thread { acceptLoop() } else null - private val stop = AtomicBoolean(false) - private var finInitiated = false - private val packet = DatagramPacket(ByteArray(socket.receiveBufferSize), socket.receiveBufferSize) - - init { - socket.connect(address, port.toInt()) - socket.soTimeout = 0 - sync?.start() - } - - private fun acceptLoop(){ - while(synchronized(stop){!stop.get()}){ - accept() - if(packet.length==0) continue // Drop empty packets - val packetType = PacketType.values()[packet.data[0].toInt()] - when(packetType){ - PacketType.DATA -> { - - } - - PacketType.ACK -> { - - } - - PacketType.FIN -> { - - } - } - } - } - - fun accept(){ - if(automaticAccept && Thread.currentThread() != sync) - throw IllegalThreadStateException("Current thread isn't setup to accept datagram packets") - socket.receive(packet) - } - - fun send(){ - if(finInitiated) throw IllegalStateException("Cannot send message after connection is closed!") - } - - fun _send(){ - - } -} \ No newline at end of file diff --git a/src/net/tofvesson/reflect/Accessible.kt b/src/net/tofvesson/reflect/Accessible.kt index 7a5dde6..2827765 100644 --- a/src/net/tofvesson/reflect/Accessible.kt +++ b/src/net/tofvesson/reflect/Accessible.kt @@ -19,6 +19,6 @@ fun T.access(): T where T: AccessibleObject { fun getUnsafe(): Unsafe{ val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe") - theUnsafe.trySetAccessible() + theUnsafe.isAccessible = true return theUnsafe.get(null) as Unsafe } \ No newline at end of file diff --git a/src/net/tofvesson/reflect/Field.kt b/src/net/tofvesson/reflect/Field.kt index 2e54b42..6b065b8 100644 --- a/src/net/tofvesson/reflect/Field.kt +++ b/src/net/tofvesson/reflect/Field.kt @@ -3,8 +3,19 @@ package net.tofvesson.reflect import java.lang.reflect.Field fun Field.setStaticFinalValue(value: Any?){ - val factory = Class.forName("jdk.internal.reflect.UnsafeFieldAccessorFactory").getDeclaredMethod("newFieldAccessor", Field::class.java, Boolean::class.java) - val isReadonly = Class.forName("jdk.internal.reflect.UnsafeQualifiedStaticFieldAccessorImpl").getDeclaredField("isReadOnly") + var pkg = "jdk.internal.relfect" + try + { + Class.forName("$pkg.UnsafeFieldAccessorFactory") + } + catch(e: ClassNotFoundException) + { + pkg = "sun.reflect" + } + + + val factory = Class.forName("$pkg.UnsafeFieldAccessorFactory").getDeclaredMethod("newFieldAccessor", Field::class.java, Boolean::class.java) + val isReadonly = Class.forName("$pkg.UnsafeQualifiedStaticFieldAccessorImpl").getDeclaredField("isReadOnly") isReadonly.forceAccessible = true factory.forceAccessible = true val overrideAccessor = Field::class.java.getDeclaredField("overrideFieldAccessor") diff --git a/src/net/tofvesson/serializers/DiffTrackedSerializer.kt b/src/net/tofvesson/serializers/DiffTrackedSerializer.kt index 7669282..80e9ba3 100644 --- a/src/net/tofvesson/serializers/DiffTrackedSerializer.kt +++ b/src/net/tofvesson/serializers/DiffTrackedSerializer.kt @@ -2,6 +2,7 @@ package net.tofvesson.serializers import net.tofvesson.annotation.SyncFlag import net.tofvesson.data.* +import net.tofvesson.reflect.access import java.lang.reflect.Field class DiffTrackedSerializer private constructor(): Serializer(arrayOf( @@ -11,23 +12,39 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf( companion object { private val trackedField = DiffTracked::class.java.getDeclaredField("_value") val singleton = DiffTrackedSerializer() + + /** + * Checks if a given object/class needs to ve serialized by checking if any DiffTracked/DiffTrackedArray + * instances in the given object/class are dirty + */ + fun objectNeedsSerialization(obj: Any?) = + obj == null || + ((obj as? Class<*> ?: obj.javaClass).fields.firstOrNull { + if (it.type is DiffTracked<*> || it.type is DiffTrackedArray<*>){ + it.access() + val diff = it.get(if(obj is Class<*>) null else obj) + return@firstOrNull diff.javaClass.getDeclaredMethod("hasChanged").invoke(diff) as Boolean + } + + return@firstOrNull false + } != null) } override fun computeSizeExplicit(field: Field, flags: Array, owner: Any?, state: WriteState, fieldType: Class<*>) { when (fieldType) { DiffTracked::class.java -> { val tracker = field.get(owner) as DiffTracked<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) - state.registerHeader(1) + val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) ?: return + state.registerBits(1) if (tracker.hasChanged()) serializer.computeSizeExplicit(trackedField, flags, tracker, state, tracker.valueType) } DiffTrackedArray::class.java -> { val tracker = field.get(owner) as DiffTrackedArray<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) - state.registerHeader(1) + val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) ?: return + state.registerBits(1) if (tracker.hasChanged()) { val holder = Holder(null) - state.registerHeader(tracker.size) + state.registerBits(tracker.size) for (index in tracker.changeMap.indices) if (tracker.changeMap[index]) { holder.value = tracker[index] @@ -39,12 +56,12 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf( } } - override fun serializeExplicit(field: Field, flags: Array, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) { + override fun serializeExplicit(field: Field, flags: Array, owner: Any?, writeBuffer: WBuffer, fieldType: Class<*>) { when (fieldType) { DiffTracked::class.java -> { val tracker = field.get(owner) as DiffTracked<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) - writeBuffer.writeHeader(tracker.hasChanged()) + val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) ?: return + writeBuffer.writeBit(tracker.hasChanged()) if (tracker.hasChanged()) { serializer.serializeExplicit(trackedField, flags, tracker, writeBuffer, tracker.valueType) tracker.clearChangeState() @@ -52,13 +69,13 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf( } DiffTrackedArray::class.java -> { val tracker = field.get(owner) as DiffTrackedArray<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) - writeBuffer.writeHeader(tracker.hasChanged()) + val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) ?: return + writeBuffer.writeBit(tracker.hasChanged()) if (tracker.hasChanged()) { val holder = Holder(null) for (index in tracker.changeMap.indices) { - writeBuffer.writeHeader(tracker.changeMap[index]) + writeBuffer.writeBit(tracker.changeMap[index]) if (tracker.changeMap[index]) { holder.value = tracker[index] serializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, tracker.elementType) @@ -71,26 +88,26 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf( } } - override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) { + override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: RBuffer, fieldType: Class<*>) { when (fieldType) { DiffTracked::class.java -> { val tracker = field.get(owner) as DiffTracked<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) - if (readBuffer.readHeader()) + val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType) ?: return + if (readBuffer.readBit()) serializer.deserializeExplicit(trackedField, flags, tracker, readBuffer, tracker.valueType) tracker.clearChangeState() } DiffTrackedArray::class.java -> { val tracker = field.get(owner) as DiffTrackedArray<*> - val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) + val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType) ?: return - if(readBuffer.readHeader()) { + if(readBuffer.readBit()) { val holder = Holder(null) val array = tracker.values as Array for (index in tracker.changeMap.indices) { - if (readBuffer.readHeader()) { + if (readBuffer.readBit()) { serializer.deserializeExplicit(Holder.valueField, flags, holder, readBuffer, tracker.elementType) array[index] = holder.value } @@ -101,4 +118,25 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf( else -> throwInvalidType(fieldType) } } + + override fun canSerialize(obj: Any?, flags: Array, type: Class<*>): Boolean { + if (obj == null) + return false + + if (DiffTracked::class.java.isAssignableFrom(type)){ + obj as DiffTracked<*> + + val handler = SyncHandler.getCompatibleSerializer(obj.valueType) ?: return false + + return handler.canSerialize(obj.value, flags, obj.valueType) + }else if (DiffTrackedArray::class.java.isAssignableFrom(type)){ + obj as DiffTrackedArray<*> + + val handler = SyncHandler.getCompatibleSerializer(obj.elementType) ?: return false + + return handler.canSerialize(obj.values, flags, obj.elementType) + } + + return false + } } \ No newline at end of file diff --git a/src/net/tofvesson/serializers/MathSerializer.kt b/src/net/tofvesson/serializers/MathSerializer.kt index 541e1a0..b9652c4 100644 --- a/src/net/tofvesson/serializers/MathSerializer.kt +++ b/src/net/tofvesson/serializers/MathSerializer.kt @@ -32,6 +32,8 @@ class MathSerializer: Serializer(arrayOf( override fun computeSizeExplicit(field: Field, flags: Array, owner: Any?, state: WriteState, fieldType: Class<*>) { when (fieldType) { Vector3::class.java -> { + val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) ?: return + val vector = field.access().get(owner) as Vector3 val xDiff = xField.get(vector) as DiffTracked val yDiff = yField.get(vector) as DiffTracked @@ -41,7 +43,7 @@ class MathSerializer: Serializer(arrayOf( val c3 = flags.contains(compressedRotationVector3) if ((c1 and c2) or (c2 and c3) or (c1 and c3)) throw MismatchedFlagException("Cannot have more than one rotation compression flag!") - state.registerHeader(3) + state.registerBits(3) when { c1 || c2 || c3 -> { val bytes = @@ -56,7 +58,6 @@ class MathSerializer: Serializer(arrayOf( .registerBytes(if (zDiff.hasChanged()) varIntSize(zDiff.value.encodeRotation(bytes).toLong()) else 0) } else -> { - val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) if (xDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, xDiff, state, Float::class.java) if (yDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, yDiff, state, Float::class.java) if (zDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, zDiff, state, Float::class.java) @@ -67,9 +68,12 @@ class MathSerializer: Serializer(arrayOf( } } - override fun serializeExplicit(field: Field, _flags: Array, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) { + override fun serializeExplicit(field: Field, _flags: Array, owner: Any?, writeBuffer: WBuffer, fieldType: Class<*>) { when (fieldType) { Vector3::class.java -> { + val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) ?: return + val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) ?: return + var flags = _flags val vector = field.access().get(owner) as Vector3 val c1 = flags.contains(compressedRotationVector1) @@ -81,13 +85,12 @@ class MathSerializer: Serializer(arrayOf( if ((c1 and c2) or (c2 and c3) or (c1 and c3)) throw MismatchedFlagException("Cannot have more than one rotation compression flag!") - writeBuffer.writeHeader(xDiff.hasChanged()) - writeBuffer.writeHeader(yDiff.hasChanged()) - writeBuffer.writeHeader(zDiff.hasChanged()) + writeBuffer.writeBit(xDiff.hasChanged()) + writeBuffer.writeBit(yDiff.hasChanged()) + writeBuffer.writeBit(zDiff.hasChanged()) when { c1 || c2 || c3 -> { - val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) val bytes = when { c1 -> 1 @@ -123,7 +126,6 @@ class MathSerializer: Serializer(arrayOf( if (zDiff.hasChanged()) intSerializer.serializeExplicit(Holder.valueField, flags, zHolder, writeBuffer, Int::class.java) } else -> { - val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) if (xDiff.hasChanged()) floatSerializer.serializeExplicit(diffValue, _flags, xDiff, writeBuffer, Float::class.java) if (yDiff.hasChanged()) floatSerializer.serializeExplicit(diffValue, _flags, yDiff, writeBuffer, Float::class.java) @@ -135,9 +137,12 @@ class MathSerializer: Serializer(arrayOf( } } - override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) { + override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: RBuffer, fieldType: Class<*>) { when (fieldType) { Vector3::class.java -> { + val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) ?: return + val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) ?: return + val vector = field.access().get(owner) as Vector3 val c1 = flags.contains(compressedRotationVector1) val c2 = flags.contains(compressedRotationVector2) @@ -153,37 +158,35 @@ class MathSerializer: Serializer(arrayOf( when { c1 || c2 || c3 -> { - val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) val bytes = if (c1) 1 else if (c2) 2 else 3 - if (readBuffer.readHeader()) { + if (readBuffer.readBit()) { intSerializer.deserializeExplicit(Holder.valueField, flags, xHolder, readBuffer, Int::class.java) xDiff.value = (xHolder.value as Int).decodeRotation(bytes) xDiff.clearChangeState() } - if (readBuffer.readHeader()) { + if (readBuffer.readBit()) { intSerializer.deserializeExplicit(Holder.valueField, flags, yHolder, readBuffer, Int::class.java) yDiff.value = (yHolder.value as Int).decodeRotation(bytes) yDiff.clearChangeState() } - if (readBuffer.readHeader()) { + if (readBuffer.readBit()) { intSerializer.deserializeExplicit(Holder.valueField, flags, zHolder, readBuffer, Int::class.java) zDiff.value = (zHolder.value as Int).decodeRotation(bytes) zDiff.clearChangeState() } } else -> { - val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) - if (readBuffer.readHeader()){ + if (readBuffer.readBit()){ floatSerializer.deserializeExplicit(diffValue, flags, xField.get(vector), readBuffer, Float::class.java) xDiff.clearChangeState() } - if (readBuffer.readHeader()){ + if (readBuffer.readBit()){ floatSerializer.deserializeExplicit(diffValue, flags, yField.get(vector), readBuffer, Float::class.java) yDiff.clearChangeState() } - if (readBuffer.readHeader()){ + if (readBuffer.readBit()){ floatSerializer.deserializeExplicit(diffValue, flags, zField.get(vector), readBuffer, Float::class.java) zDiff.clearChangeState() } @@ -193,4 +196,10 @@ class MathSerializer: Serializer(arrayOf( else -> throwInvalidType(fieldType) } } + + override fun canSerialize(obj: Any?, flags: Array, type: Class<*>): Boolean { + SyncHandler.getCompatibleSerializer(Float::class.java) ?: return false + SyncHandler.getCompatibleSerializer(Int::class.java) ?: return false + return Vector3::class.java.isAssignableFrom(type) + } } \ No newline at end of file diff --git a/src/net/tofvesson/serializers/PrimitiveArraySerializer.kt b/src/net/tofvesson/serializers/PrimitiveArraySerializer.kt index b786b81..518946d 100644 --- a/src/net/tofvesson/serializers/PrimitiveArraySerializer.kt +++ b/src/net/tofvesson/serializers/PrimitiveArraySerializer.kt @@ -2,6 +2,7 @@ package net.tofvesson.serializers import net.tofvesson.annotation.SyncFlag import net.tofvesson.data.* +import net.tofvesson.math.varIntSize import java.lang.reflect.Field @Suppress("MemberVisibilityCanBePrivate") @@ -23,13 +24,14 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( override fun computeSizeExplicit(field: Field, flags: Array, owner: Any?, state: WriteState, fieldType: Class<*>) { val arrayLength = java.lang.reflect.Array.getLength(field.get(owner)) val holder = Holder(null) + when (fieldType) { BooleanArray::class.java -> state.registerBits(arrayLength) ByteArray::class.java -> state.registerBytes(arrayLength) ShortArray::class.java -> if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 2) else { - val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java) + val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java) ?: return for (value in field.get(owner) as ShortArray) { holder.value = value shortSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Short::class.java) @@ -38,7 +40,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( IntArray::class.java -> if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 4) else { - val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) + val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) ?: return for (value in field.get(owner) as IntArray) { holder.value = value intSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Int::class.java) @@ -47,7 +49,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( LongArray::class.java -> if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 8) else { - val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java) + val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java) ?: return for (value in field.get(owner) as LongArray) { holder.value = value longSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Long::class.java) @@ -56,7 +58,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( FloatArray::class.java -> if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 4) else { - val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) + val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) ?: return for (value in field.get(owner) as FloatArray) { holder.value = value floatSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Float::class.java) @@ -65,7 +67,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( DoubleArray::class.java -> if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 8) else { - val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java) + val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java) ?: return for (value in field.get(owner) as DoubleArray) { holder.value = value doubleSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Double::class.java) @@ -73,12 +75,16 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( } else -> throwInvalidType(fieldType) } + + if(!flags.contains(knownSize)) + state.registerBytes(varIntSize(arrayLength.toLong())) } - override fun serializeExplicit(field: Field, flags: Array, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) { + override fun serializeExplicit(field: Field, flags: Array, owner: Any?, writeBuffer: WBuffer, fieldType: Class<*>) { val arrayLength = java.lang.reflect.Array.getLength(field.get(owner)) val holder = Holder(null) - if(!flags.contains(knownSize)) writeBuffer.writePackedInt(arrayLength, true) + if(!flags.contains(knownSize)) + writeBuffer.writePackedInt(arrayLength, true) when (fieldType) { BooleanArray::class.java -> for(value in field.get(owner) as BooleanArray) @@ -91,7 +97,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( for(value in field.get(owner) as ShortArray) writeBuffer.writeShort(value) else { - val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java) + val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java) ?: return for (value in field.get(owner) as ShortArray) { holder.value = value shortSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType) @@ -102,7 +108,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( for(value in field.get(owner) as IntArray) writeBuffer.writeInt(value) else { - val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) + val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java) ?: return for (value in field.get(owner) as IntArray) { holder.value = value intSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType) @@ -113,7 +119,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( for(value in field.get(owner) as LongArray) writeBuffer.writeLong(value) else { - val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java) + val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java) ?: return for (value in field.get(owner) as LongArray) { holder.value = value longSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType) @@ -124,7 +130,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( for(value in field.get(owner) as FloatArray) writeBuffer.writeFloat(value) else { - val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) + val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java) ?: return for (value in field.get(owner) as FloatArray) { holder.value = value floatSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType) @@ -135,7 +141,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( for(value in field.get(owner) as DoubleArray) writeBuffer.writeDouble(value) else { - val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java) + val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java) ?: return for (value in field.get(owner) as DoubleArray) { holder.value = value doubleSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType) @@ -145,7 +151,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( } } - override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) { + override fun deserializeExplicit(field: Field, flags: Array, owner: Any?, readBuffer: RBuffer, fieldType: Class<*>) { val localLength = java.lang.reflect.Array.getLength(field.get(owner)) val arrayLength = if(flags.contains(knownSize)) localLength @@ -215,4 +221,7 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf( } if(arrayLength!=localLength) field.set(owner, target) } + + override fun canSerialize(obj: Any?, flags: Array, type: Class<*>) = + getRegisteredTypes().firstOrNull { it == type } != null } \ No newline at end of file diff --git a/src/net/tofvesson/serializers/PrimitiveSerializers.kt b/src/net/tofvesson/serializers/PrimitiveSerializers.kt index 87587c7..6029f5e 100644 --- a/src/net/tofvesson/serializers/PrimitiveSerializers.kt +++ b/src/net/tofvesson/serializers/PrimitiveSerializers.kt @@ -1,9 +1,9 @@ package net.tofvesson.serializers import net.tofvesson.annotation.SyncFlag -import net.tofvesson.data.ReadBuffer +import net.tofvesson.data.RBuffer import net.tofvesson.data.Serializer -import net.tofvesson.data.WriteBuffer +import net.tofvesson.data.WBuffer import net.tofvesson.data.WriteState import net.tofvesson.math.* import net.tofvesson.reflect.* @@ -75,7 +75,7 @@ class PrimitiveSerializer private constructor() : Serializer(arrayOf( field: Field, flags: Array, owner: Any?, - writeBuffer: WriteBuffer, + writeBuffer: WBuffer, fieldType: Class<*> ){ when (fieldType) { @@ -104,7 +104,7 @@ class PrimitiveSerializer private constructor() : Serializer(arrayOf( field: Field, flags: Array, owner: Any?, - readBuffer: ReadBuffer, + readBuffer: RBuffer, fieldType: Class<*> ) = when(fieldType){ @@ -127,4 +127,7 @@ class PrimitiveSerializer private constructor() : Serializer(arrayOf( else field.setDoubleAdaptive(owner, readBuffer.readPackedDouble(flags.contains(SyncFlag.FloatEndianSwap))) else -> throwInvalidType(fieldType) } + + override fun canSerialize(obj: Any?, flags: Array, type: Class<*>) = + getRegisteredTypes().firstOrNull { it == type } != null } \ No newline at end of file diff --git a/src/Main.java b/test/Main.java similarity index 94% rename from src/Main.java rename to test/Main.java index 1c41ace..96a1b58 100644 --- a/src/Main.java +++ b/test/Main.java @@ -2,9 +2,12 @@ import net.tofvesson.annotation.SyncedVar; import net.tofvesson.data.DiffTracked; import net.tofvesson.data.DiffTrackedArray; import net.tofvesson.data.SyncHandler; +import net.tofvesson.math.ArithmeticKt; import net.tofvesson.serializers.MathSerializer; import net.tofvesson.math.Vector3; +import java.nio.ByteBuffer; + public class Main { @SyncedVar("NonNegative") public int syncTest = 5; @@ -28,12 +31,16 @@ public class Main { public static DiffTracked tracker = new DiffTracked<>(5, Integer.class); @SyncedVar - public static DiffTrackedArray tracker2 = new DiffTrackedArray<>(Long.class, 8, i -> (long)i); + public static DiffTrackedArray tracker2 = new DiffTrackedArray<>(Long.class, 8, i -> (long)i); //14,11 @SyncedVar({MathSerializer.flagLowCompressionRotation, MathSerializer.flagPassiveCompress}) public static Vector3 lookDirection = new Vector3(60, 90, 80); public static void main(String[] args){ + ByteBuffer buf = ByteBuffer.allocate(256); + ArithmeticKt.writeVarInt(buf, 0, -260); + + Main testObject = new Main(); SyncHandler sync = new SyncHandler();