Almost completely rewrite serialization system

This commit is contained in:
Gabriel Tofvesson 2020-03-25 21:21:43 +01:00
parent dda62a1a02
commit 224f85323f
19 changed files with 558 additions and 447 deletions

View File

@ -4,6 +4,7 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@ -3,7 +3,7 @@ package net.tofvesson.data
class DiffTrackedArray<T>(val elementType: Class<T>, val size: Int, gen: (Int) -> T) {
val values: Array<T> = java.lang.reflect.Array.newInstance(elementType, size) as Array<T>
val changeMap = Array(size) {false}
val changeMap = BooleanArray(size) {false}
init{
for(index in 0 until size)

View File

@ -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)
}

View File

@ -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!")
}
}

View File

@ -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<Class<*>>) {
flags: Array<out SyncFlag>,
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<Class<*>>) {
field: Field,
flags: Array<out SyncFlag>,
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<out SyncFlag>,
owner: Any?,
writeBuffer: WriteBuffer,
writeBuffer: WBuffer,
fieldType: Class<*>
)
@ -53,20 +58,25 @@ abstract class Serializer(registeredTypes: Array<Class<*>>) {
field: Field,
flags: Array<out SyncFlag>,
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<out SyncFlag>,
owner: Any?,
readBuffer: ReadBuffer,
readBuffer: RBuffer,
fieldType: Class<*>
)
abstract fun canSerialize(obj: Any?, flags: Array<out SyncFlag>, type: Class<*>): Boolean
open fun canDeserialize(obj: Any?, flags: Array<out SyncFlag>, type: Class<*>) = canSerialize(obj, flags, type)
fun getRegisteredTypes(): Array<Class<*>> = 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")
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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!")
}
}

View File

@ -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()
}

View File

@ -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()

View File

@ -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
(this ushr 1) or
this) and 1

View File

@ -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(){
}
}

View File

@ -19,6 +19,6 @@ fun <T> 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
}

View File

@ -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")

View File

@ -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<out SyncFlag>, 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<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, 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<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, 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<Any?>
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<out SyncFlag>, 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
}
}

View File

@ -32,6 +32,8 @@ class MathSerializer: Serializer(arrayOf(
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, 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<Float>
val yDiff = yField.get(vector) as DiffTracked<Float>
@ -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<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
override fun serializeExplicit(field: Field, _flags: Array<out SyncFlag>, 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<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, 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<out SyncFlag>, type: Class<*>): Boolean {
SyncHandler.getCompatibleSerializer(Float::class.java) ?: return false
SyncHandler.getCompatibleSerializer(Int::class.java) ?: return false
return Vector3::class.java.isAssignableFrom(type)
}
}

View File

@ -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<out SyncFlag>, 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<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, 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<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, 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<out SyncFlag>, type: Class<*>) =
getRegisteredTypes().firstOrNull { it == type } != null
}

View File

@ -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<out SyncFlag>,
owner: Any?,
writeBuffer: WriteBuffer,
writeBuffer: WBuffer,
fieldType: Class<*>
){
when (fieldType) {
@ -104,7 +104,7 @@ class PrimitiveSerializer private constructor() : Serializer(arrayOf(
field: Field,
flags: Array<out SyncFlag>,
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<out SyncFlag>, type: Class<*>) =
getRegisteredTypes().firstOrNull { it == type } != null
}

View File

@ -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<Integer> tracker = new DiffTracked<>(5, Integer.class);
@SyncedVar
public static DiffTrackedArray<Long> tracker2 = new DiffTrackedArray<>(Long.class, 8, i -> (long)i);
public static DiffTrackedArray<Long> 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();