diff --git a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayDefinition.kt b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayDefinition.kt index 818c0fb..9db6dbb 100644 --- a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayDefinition.kt +++ b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayDefinition.kt @@ -1,27 +1,34 @@ package nl.astraeus.tba -enum class DataType(val size: Int) { +enum class DataType( + val size: Int, + val bytesUsedInternally: Int = 0 +) { BYTE(1), SHORT(2), INT(4), LONG(8), FLOAT(4), DOUBLE(8), - STRING(-1), // max length 65535 - CLOB(-1), // max length 2^32-1 - BLOB(-1), // max length 2^32-1 + STRING(-1, 2), // max length 65535 + CLOB(-1, 4), // max length 2^32-1 + BLOB(-1, 4), // max length 2^32-1 ; } class Type( val name: String, val type: DataType, - val size: Int = if (type.size == -1) { + size: Int = 0, +) { + val size: Int = if (size > 0) { + size + type.bytesUsedInternally + } else if (type.size == -1) { throw IllegalArgumentException("Size must be defined for type ${type.name}") } else { - type.size - }, -) + type.size + type.bytesUsedInternally + } +} open class ByteArrayDefinition( vararg types: Type, @@ -31,4 +38,4 @@ open class ByteArrayDefinition( val size: Int by lazy { this.types.sumOf { it.size } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayExtentions.kt b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayExtentions.kt deleted file mode 100644 index 6a6bc3d..0000000 --- a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayExtentions.kt +++ /dev/null @@ -1,69 +0,0 @@ -package nl.astraeus.tba - -fun ByteArray.setShort(index: Int, value: Short) { - this[index] = (value.toInt() shr 8).toByte() - this[index + 1] = value.toByte() -} - -fun ByteArray.getShort(index: Int): Short { - return ((this[index].toInt() shl 8) or (this[index + 1].toInt() and 0xff)).toShort() -} - -fun ByteArray.setInt(index: Int, value: Int) { - this[index] = ((value shr 24) and 0xff).toByte() - this[index + 1] = ((value shr 16) and 0xff).toByte() - this[index + 2] = ((value shr 8) and 0xff).toByte() - this[index + 3] = (value and 0xff).toByte() -} - -fun ByteArray.getInt(index: Int): Int { - return ( - (this[index].toInt() shl 24) or - ((this[index+1].toInt() shl 16) and 0xff0000) or - ((this[index+2].toInt() shl 8) and 0xff00) or - (this[index+3].toInt() and 0xff) - ) -} - -fun ByteArray.setLong(index: Int, value: Long) { - this[index] = ((value shr 56) and 0xff).toByte() - this[index + 1] = ((value shr 48) and 0xff).toByte() - this[index + 2] = ((value shr 40) and 0xff).toByte() - this[index + 3] = ((value shr 32) and 0xff).toByte() - this[index + 4] = ((value shr 24) and 0xff).toByte() - this[index + 5] = ((value shr 16) and 0xff).toByte() - this[index + 6] = ((value shr 8) and 0xff).toByte() - this[index + 7] = (value and 0xff).toByte() -} - -fun ByteArray.getLong(index: Int): Long { - return ( - (this[index].toLong() shl 56) or - ((this[index+1].toLong() and 0xff) shl 48) or - ((this[index+2].toLong() and 0xff) shl 40) or - ((this[index+3].toLong() and 0xff) shl 32) or - ((this[index+4].toLong() and 0xff) shl 24) or - ((this[index+5].toLong() and 0xff) shl 16) or - ((this[index+6].toLong() and 0xff) shl 8) or - (this[index+7].toLong() and 0xff) - ) -} - -fun ByteArray.setDouble(index: Int, value: Double) = this.setLong(index, value.toRawBits()) -fun ByteArray.getDouble(index: Int): Double = Double.fromBits(this.getLong(index)) - -fun ByteArray.setFloat(index: Int, value: Float) = this.setInt(index, value.toRawBits()) -fun ByteArray.getFloat(index: Int): Float = Float.fromBits(this.getInt(index)) - -fun ByteArray.getString(index: Int): Pair { - val length = this.getInt(index) - val str = this.copyOfRange(index + 4, index + 4 + length).decodeToString() - return Pair(str, index + 4 + length) -} - -fun ByteArray.setString(index: Int, value: String): Int { - val bytes = value.encodeToByteArray() - this.setInt(index, bytes.size) - bytes.copyInto(this, index + 4) - return index + bytes.size + 4 -} diff --git a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayHandler.kt b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayHandler.kt index bbf1d87..a9f047d 100644 --- a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayHandler.kt +++ b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayHandler.kt @@ -2,9 +2,13 @@ package nl.astraeus.tba class MutableByteArrayHandler( initialSize: Int = 1024, - buffer: ByteArray = ByteArray(initialSize) + buffer: SlicedByteArray = SlicedByteArray.wrap(ByteArray(initialSize), 0, initialSize), ) : ByteArrayHandler(initialSize, buffer) { + constructor(buffer: ByteArray, range: IntRange = buffer.indices) : this( + buffer = SlicedByteArray.wrap(buffer, range.first, range.last - range.first) + ) + operator fun set(index: Int, value: Byte) { buffer[index] = value } @@ -32,8 +36,8 @@ class MutableByteArrayHandler( buffer[index + 7] = (value and 0xff).toByte() } - fun setFloat(index: Int, value: Float) = buffer.setInt(index, value.toRawBits()) - fun setDouble(index: Int, value: Double) = buffer.setLong(index, value.toRawBits()) + fun setFloat(index: Int, value: Float) = setInt(index, value.toRawBits()) + fun setDouble(index: Int, value: Double) = setLong(index, value.toRawBits()) fun setString(index: Int, value: String, maxLength: Int): Int { val bytes = value.encodeToByteArray() @@ -41,7 +45,7 @@ class MutableByteArrayHandler( throw IllegalArgumentException("String is too long") } this.setShort(index, bytes.size.toShort()) - bytes.copyInto(buffer, index + 2) + bytes.copyInto(buffer.data, index + 2) return index + bytes.size + 2 } @@ -51,7 +55,7 @@ class MutableByteArrayHandler( throw IllegalArgumentException("String is too long") } this.setInt(index, bytes.size) - bytes.copyInto(buffer, index + 4) + bytes.copyInto(buffer.data, index + 4) return index + bytes.size + 4 } @@ -60,7 +64,7 @@ class MutableByteArrayHandler( throw IllegalArgumentException("String is too long") } this.setInt(index, bytes.size) - bytes.copyInto(buffer, index + 4) + bytes.copyInto(buffer.data, index + 4) return index + bytes.size + 4 } @@ -68,15 +72,11 @@ class MutableByteArrayHandler( open class ByteArrayHandler( size: Int = 1024, - var buffer: ByteArray = ByteArray(size), - val firstIndex: Int = 0, - val lastIndex: Int = buffer.size + var buffer: SlicedByteArray = SlicedByteArray.wrap(ByteArray(size), 0, size), ) { - constructor(buffer: ByteArray, range: IntRange) : this( - buffer = buffer, - firstIndex = range.first, - lastIndex = range.last + constructor(buffer: ByteArray, range: IntRange = buffer.indices) : this( + buffer = SlicedByteArray.wrap(buffer, range.first, range.last - range.first) ) operator fun get(index: Int): Byte { @@ -109,35 +109,34 @@ open class ByteArrayHandler( ) } - fun getFloat(index: Int): Float = Float.fromBits(buffer.getInt(index)) - fun getDouble(index: Int): Double = Double.fromBits(buffer.getLong(index)) + fun getFloat(index: Int): Float = Float.fromBits(getInt(index)) + fun getDouble(index: Int): Double = Double.fromBits(getLong(index)) fun getString(index: Int): String { val length = getShort(index) - val str = buffer.copyOfRange(index + 2, index + 2 + length).decodeToString() + val str = buffer.data.copyOfRange(index + 2, index + 2 + length).decodeToString() return str } fun getClob(index: Int): String { val length = getInt(index) - val str = buffer.copyOfRange(index + 4, index + 4 + length).decodeToString() + val str = buffer.data.copyOfRange(index + 4, index + 4 + length).decodeToString() return str } fun getBlob(index: Int): ByteArray { val length = getInt(index) - val str = buffer.copyOfRange(index + 4, index + 4 + length) + val str = buffer.data.copyOfRange(index + 4, index + 4 + length) return str } fun slice(range: IntRange): ByteArrayHandler { return ByteArrayHandler( - buffer, - range + buffer = SlicedByteArray(this.buffer.data, range.first, range.last - range.first) ) } fun asMutable(): MutableByteArrayHandler { - return MutableByteArrayHandler(buffer = buffer.copyOfRange(firstIndex, lastIndex)) + return MutableByteArrayHandler(buffer = SlicedByteArray(buffer.data, 0, buffer.data.size)) } } diff --git a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayProperties.kt b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayProperties.kt index 9215dc6..0f26c76 100644 --- a/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayProperties.kt +++ b/src/commonMain/kotlin/nl/astraeus/tba/ByteArrayProperties.kt @@ -23,7 +23,10 @@ abstract class ByteArrayPropertyWithLength( protected fun getMaxLength(thisRef: TypedByteArray): Int { if (maxLength == null) { - maxLength = thisRef.typeMap[name]!!.size - 2 + val type = thisRef.typeMap[name] ?: throw IllegalArgumentException( + "Type $name not found in typemap in $thisRef" + ) + maxLength = type.size } return maxLength!! } diff --git a/src/commonMain/kotlin/nl/astraeus/tba/SlicedByteArray.kt b/src/commonMain/kotlin/nl/astraeus/tba/SlicedByteArray.kt new file mode 100644 index 0000000..8896962 --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/tba/SlicedByteArray.kt @@ -0,0 +1,32 @@ +package nl.astraeus.tba + +class SlicedByteArray( + val data: ByteArray, + val offset: Int, + val length: Int +) { + val size: Int + get() = length + + operator fun get(index: Int): Byte { + return data[offset + index] + } + + operator fun set(index: Int, value: Byte) { + data[offset + index] = value + } + + fun copyInto(dest: ByteArray, destOffset: Int, length: Int) { + data.copyInto(dest, destOffset, offset, offset + length) + } + + companion object { + fun wrap( + data: ByteArray, + offset: Int, + length: Int + ): SlicedByteArray { + return SlicedByteArray(data, offset,length) + } + } +} diff --git a/src/commonMain/kotlin/nl/astraeus/tba/TypedByteArray.kt b/src/commonMain/kotlin/nl/astraeus/tba/TypedByteArray.kt index 291c50c..ff50c92 100644 --- a/src/commonMain/kotlin/nl/astraeus/tba/TypedByteArray.kt +++ b/src/commonMain/kotlin/nl/astraeus/tba/TypedByteArray.kt @@ -19,6 +19,8 @@ open class TypedByteArray( } } + constructor(vararg types: Type): this(ByteArrayDefinition(*types)) + constructor(data: ByteArray): this(ByteArrayDefinition()) { this.data = MutableByteArrayHandler(buffer = data) } diff --git a/src/commonTest/kotlin/nl/astraeus/tba/ExtendedByteArrayDefinition.kt b/src/commonTest/kotlin/nl/astraeus/tba/ExtendedByteArrayDefinition.kt index ac9d305..80354c6 100644 --- a/src/commonTest/kotlin/nl/astraeus/tba/ExtendedByteArrayDefinition.kt +++ b/src/commonTest/kotlin/nl/astraeus/tba/ExtendedByteArrayDefinition.kt @@ -9,12 +9,10 @@ class ExtendedByteArrayDefinition { open class MessageByteArray( vararg types: Type ) : TypedByteArray( - ByteArrayDefinition( Type("messageType", DataType.BYTE), Type("messageLength", DataType.SHORT), Type("message", DataType.STRING, 1024), *types, - ) ) { var messageType by byte("messageType") var messageLength by short("messageLength") diff --git a/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArraySize.kt b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArraySize.kt new file mode 100644 index 0000000..2a7f541 --- /dev/null +++ b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArraySize.kt @@ -0,0 +1,36 @@ +package nl.astraeus.tba + +import kotlin.test.Test +import kotlin.test.assertEquals + +class TypedByteArraySize { + + class Person() : TypedByteArray( + Type("name", DataType.STRING, 100), + ) { + var name by string("name") + + constructor(name: String): this() { + this.name = name + } + + constructor(data: ByteArray): this() { + this.data = MutableByteArrayHandler(buffer = data) + } + } + + @Test + fun testPersonSize() { + val type = Type("name", DataType.STRING, 10) + + assertEquals(12, type.size) + + val person = Person("Test") + + assertEquals(102, person.data.buffer.size) + + val clobType = Type("long-name", DataType.CLOB, 100) + + assertEquals(104, clobType.size) + } +} diff --git a/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayTest.kt b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayTest.kt index 8cd2c2b..40f746d 100644 --- a/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayTest.kt +++ b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayTest.kt @@ -6,11 +6,9 @@ import kotlin.test.assertEquals class TypedByteArrayTest { class Person() : TypedByteArray( - ByteArrayDefinition( - Type("name", DataType.STRING, 102), + Type("name", DataType.STRING, 100), Type("age", DataType.BYTE), Type("length", DataType.SHORT), - ) ) { var name by string("name") var age by byte("age") @@ -33,6 +31,7 @@ class TypedByteArrayTest { assertEquals(4, person.data.buffer[1]) assertEquals(42, person.data.buffer[102]) + assertEquals(180.toByte(), person.data.buffer[104]) assertEquals("Test", person.name) assertEquals(42.toByte(), person.age) @@ -42,7 +41,7 @@ class TypedByteArrayTest { @Test fun testToByteArrayAndBack() { val person = Person("Test", 42, 180) - val bytes: ByteArray = person.data.buffer + val bytes: ByteArray = person.data.buffer.data val person2 = Person(bytes) diff --git a/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayWithByteArrayTest.kt b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayWithByteArrayTest.kt new file mode 100644 index 0000000..429f0aa --- /dev/null +++ b/src/commonTest/kotlin/nl/astraeus/tba/TypedByteArrayWithByteArrayTest.kt @@ -0,0 +1,54 @@ +package nl.astraeus.tba + +import kotlin.test.Test +import kotlin.test.assertEquals + +class TypedByteArrayWithByteArrayTest { + + class Person1() : TypedByteArray( + Type("name", DataType.STRING, 10), + ) { + var name by string("name") + + constructor(name: String): this() { + this.name = name + } + + constructor(data: ByteArray): this() { + this.data = MutableByteArrayHandler(buffer = data) + } + } + + class Company() : TypedByteArray( + Type("name", DataType.STRING, 10), + Type("person", DataType.BLOB, 12) + ) { + var name by string("name") + var personData by blob("person") + val person by lazy { Person1(personData) } + + constructor(name: String, person: Person1): this() { + this.name = name + this.personData = person.data.buffer.data + } + + constructor(data: ByteArray): this() { + this.data = MutableByteArrayHandler(buffer = data) + } + } + + @Test + fun testPersonSize() { + val person = Person1("A Name") + + assertEquals(12, person.data.buffer.size) + + val company = Company("A Company", person) + + assertEquals("A Name", company.person.name) + + company.person.name = "2nd Name" + + assertEquals("2nd Name", company.person.name) + } +}