Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Serialization.kt with auto functionality #88

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 129 additions & 12 deletions ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import java.nio.Buffer
import java.nio.ByteBuffer
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor

const val SERIALIZED_USHORT_SIZE = 2
const val SERIALIZED_UINT_SIZE = 4
const val SERIALIZED_INT_SIZE = 4
const val SERIALIZED_ULONG_SIZE = 8
const val SERIALIZED_LONG_SIZE = 4
const val SERIALIZED_LONG_SIZE = 8
const val SERIALIZED_UBYTE_SIZE = 1

const val SERIALIZED_PUBLIC_KEY_SIZE = 74
Expand All @@ -20,6 +24,81 @@

interface Deserializable<T> {
fun deserialize(buffer: ByteArray, offset: Int = 0): Pair<T, Int>

}

/**
* Serializes the object and returns the buffer.
* Alternative to manually defining the serialize function.
*/
interface AutoSerializable : Serializable {
override fun serialize(): ByteArray {
return this::class.primaryConstructor!!.parameters.map { param ->
val value =
this.javaClass.kotlin.memberProperties.find { it.name == param.name }!!.get(this)
simpleSerialize(value)
}.reduce { acc, bytes -> acc + bytes }
}
}

///**
// * Deserializes the object from the buffer and returns the object and the new offset.
// * Alternative to manually defining the deserialize function.
// */
//inline fun <reified T> Deserializable<T>.autoDeserialize(buffer: ByteArray, offset: Int = 0): Pair<T, Int> {
// TODO()
//}

fun <U> simpleSerialize(data: U): ByteArray {
return when (data) {
is Int -> serializeInt(data)
is Long -> serializeLong(data)

Check warning on line 55 in ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt

View check run for this annotation

Codecov / codecov/patch

ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt#L55

Added line #L55 was not covered by tests
is UByte -> serializeUChar(data)
is UInt -> serializeUInt(data)
is UShort -> serializeUShort(data.toInt())
is ULong -> serializeULong(data)
is String -> serializeVarLen(data.toByteArray())
is ByteArray -> serializeVarLen(data)
is Boolean -> serializeBool(data)
is Enum<*> -> serializeUInt(data.ordinal.toUInt())
is Serializable -> data.serialize()
else -> throw IllegalArgumentException("Unsupported serialization type")
}
}

inline fun <reified U> simpleDeserialize(buffer: ByteArray, offset: Int = 0): Pair<U, Int> {
val (value, off) = when (U::class) {
Int::class -> Pair(deserializeInt(buffer, offset) as U, SERIALIZED_INT_SIZE)
Long::class -> Pair(deserializeLong(buffer, offset) as U, SERIALIZED_LONG_SIZE)

Check warning on line 72 in ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt

View check run for this annotation

Codecov / codecov/patch

ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt#L72

Added line #L72 was not covered by tests
UByte::class -> Pair(deserializeUChar(buffer, offset) as U, SERIALIZED_UBYTE_SIZE)
UShort::class -> Pair(
deserializeUShort(buffer, offset).toUShort() as U,
SERIALIZED_USHORT_SIZE
)

UInt::class -> Pair(deserializeUInt(buffer, offset) as U, SERIALIZED_UINT_SIZE)
ULong::class -> Pair(deserializeULong(buffer, offset) as U, SERIALIZED_ULONG_SIZE)
String::class -> {
val (data, len) = deserializeVarLen(buffer, offset)
Pair(data.decodeToString() as U, len)
}

ByteArray::class -> {
val (data, len) = deserializeVarLen(buffer, offset)

Check warning on line 87 in ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt

View check run for this annotation

Codecov / codecov/patch

ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt#L87

Added line #L87 was not covered by tests
Pair(data as U, len)
}

Boolean::class -> Pair(deserializeBool(buffer, offset) as U, 1)
else -> {
println("Enum: ${U::class.qualifiedName}")
if (U::class.isSubclassOf(Enum::class)) {
val ordinal = deserializeUInt(buffer, offset).toInt()
val values = U::class.java.enumConstants

Check warning on line 96 in ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt

View check run for this annotation

Codecov / codecov/patch

ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt#L96

Added line #L96 was not covered by tests
Pair(values[ordinal] as U, SERIALIZED_UINT_SIZE)
} else throw IllegalArgumentException("Unsupported deserialization type")
}
}
return (value to (offset + off))
}

fun serializeBool(data: Boolean): ByteArray {
Expand All @@ -40,10 +119,21 @@
return bytes
}

fun serializeUShort(value: UShort): ByteArray {
val bytes = ByteBuffer.allocate(SERIALIZED_USHORT_SIZE)

Check warning on line 123 in ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt

View check run for this annotation

Codecov / codecov/patch

ipv8/src/main/java/nl/tudelft/ipv8/messaging/Serialization.kt#L122-L123

Added lines #L122 - L123 were not covered by tests
bytes.putShort(value.toShort())
return bytes.array()
}

fun deserializeUShort(buffer: ByteArray, offset: Int = 0): Int {
return (((buffer[offset].toInt() and 0xFF) shl 8) or (buffer[offset + 1].toInt() and 0xFF))
}

fun deserializeRealUShort(buffer: ByteArray, offset: Int = 0): UShort {
val buf = ByteBuffer.wrap(buffer, offset, SERIALIZED_USHORT_SIZE)
return buf.short.toUShort()
}

fun serializeUInt(value: UInt): ByteArray {
val bytes = UByteArray(SERIALIZED_UINT_SIZE)
for (i in 0 until SERIALIZED_UINT_SIZE) {
Expand Down Expand Up @@ -80,7 +170,7 @@

fun serializeLong(value: Long): ByteArray {
val buffer = ByteBuffer.allocate(SERIALIZED_LONG_SIZE)
buffer.putInt(value.toInt())
buffer.putLong(value)
return buffer.array()
}

Expand All @@ -89,7 +179,20 @@
buffer.put(bytes.copyOfRange(offset, offset + SERIALIZED_LONG_SIZE))
// In JDK 8 this returns a Buffer.
(buffer as Buffer).flip()
return buffer.int.toLong()
return buffer.long
}

fun serializeInt(value: Int): ByteArray {
val buffer = ByteBuffer.allocate(SERIALIZED_INT_SIZE)
buffer.putInt(value)
return buffer.array()
}

fun deserializeInt(bytes: ByteArray, offset: Int = 0): Int {
val buffer = ByteBuffer.allocate(SERIALIZED_INT_SIZE)
buffer.put(bytes.copyOfRange(offset, offset + SERIALIZED_INT_SIZE))
buffer.flip()
return buffer.int
}

fun serializeUChar(char: UByte): ByteArray {
Expand All @@ -107,8 +210,10 @@

fun deserializeVarLen(buffer: ByteArray, offset: Int = 0): Pair<ByteArray, Int> {
val len = deserializeUInt(buffer, offset).toInt()
val payload = buffer.copyOfRange(offset + SERIALIZED_UINT_SIZE,
offset + SERIALIZED_UINT_SIZE + len)
val payload = buffer.copyOfRange(
offset + SERIALIZED_UINT_SIZE,
offset + SERIALIZED_UINT_SIZE + len
)
return Pair(payload, SERIALIZED_UINT_SIZE + len)
}

Expand All @@ -117,19 +222,31 @@
return arrayOf()
}
val len = deserializeUInt(buffer, offset).toInt()
val payload = buffer.copyOfRange(offset + SERIALIZED_UINT_SIZE,
offset + SERIALIZED_UINT_SIZE + len)
return arrayOf(payload) + deserializeRecursively(buffer.copyOfRange(offset + SERIALIZED_UINT_SIZE + len,
buffer.size), offset)
val payload = buffer.copyOfRange(
offset + SERIALIZED_UINT_SIZE,
offset + SERIALIZED_UINT_SIZE + len
)
return arrayOf(payload) + deserializeRecursively(
buffer.copyOfRange(
offset + SERIALIZED_UINT_SIZE + len,
buffer.size
), offset
)
}

fun deserializeAmount(buffer: ByteArray, amount: Int, offset: Int = 0): Pair<Array<ByteArray>, ByteArray> {
fun deserializeAmount(
buffer: ByteArray,
amount: Int,
offset: Int = 0
): Pair<Array<ByteArray>, ByteArray> {
val returnValues = arrayListOf<ByteArray>()
var localOffset = offset
for (i in 0 until amount) {
val len = deserializeUInt(buffer, localOffset).toInt()
val payload = buffer.copyOfRange(localOffset + SERIALIZED_UINT_SIZE,
localOffset + SERIALIZED_UINT_SIZE + len)
val payload = buffer.copyOfRange(
localOffset + SERIALIZED_UINT_SIZE,
localOffset + SERIALIZED_UINT_SIZE + len
)
localOffset += SERIALIZED_UINT_SIZE + len
returnValues.add(payload)
}
Expand Down
Loading
Loading