Slices and string cache
This commit is contained in:
@@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "nl.astraeus"
|
||||
version = "0.1.0-SNAPSHOT"
|
||||
version = "0.1.1-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -137,6 +137,32 @@ tasks.named<Task>("publishKotlinMultiplatformPublicationToMavenLocal") {
|
||||
dependsOn(tasks.named<Task>("signJsPublication"))
|
||||
}
|
||||
|
||||
|
||||
tasks.named<Task>("publishJsPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signJvmPublication"))
|
||||
}
|
||||
|
||||
tasks.named<Task>("publishJvmPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signJsPublication"))
|
||||
}
|
||||
|
||||
tasks.named<Task>("publishJvmPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signKotlinMultiplatformPublication"))
|
||||
}
|
||||
|
||||
tasks.named<Task>("publishJsPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signKotlinMultiplatformPublication"))
|
||||
}
|
||||
|
||||
tasks.named<Task>("publishKotlinMultiplatformPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signJvmPublication"))
|
||||
}
|
||||
|
||||
tasks.named<Task>("publishKotlinMultiplatformPublicationToMavenLocalRepository") {
|
||||
dependsOn(tasks.named<Task>("signJsPublication"))
|
||||
}
|
||||
|
||||
|
||||
tasks.named<Task>("publishJsPublicationToGiteaRepository") {
|
||||
dependsOn(tasks.named<Task>("signJvmPublication"))
|
||||
}
|
||||
@@ -159,4 +185,4 @@ tasks.named<Task>("publishKotlinMultiplatformPublicationToGiteaRepository") {
|
||||
|
||||
tasks.named<Task>("publishKotlinMultiplatformPublicationToGiteaRepository") {
|
||||
dependsOn(tasks.named<Task>("signJsPublication"))
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@ enum class DataType(
|
||||
DOUBLE(8),
|
||||
STRING(-1, 2), // max length 65535
|
||||
CLOB(-1, 4), // max length 2^32-1
|
||||
BLOB(-1, 4), // max length 2^32-1
|
||||
BLOB(-1, 4),
|
||||
//SLICE(-1, 4) // max length 2^32-1
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,61 +13,20 @@ class MutableByteArrayHandler(
|
||||
buffer[index] = value
|
||||
}
|
||||
|
||||
fun setShort(index: Int, value: Short) {
|
||||
buffer[index] = (value.toInt() shr 8).toByte()
|
||||
buffer[index + 1] = value.toByte()
|
||||
}
|
||||
fun setShort(index: Int, value: Short) = buffer.setShort(index, value)
|
||||
|
||||
fun setInt(index: Int, value: Int) {
|
||||
buffer[index] = ((value shr 24) and 0xff).toByte()
|
||||
buffer[index + 1] = ((value shr 16) and 0xff).toByte()
|
||||
buffer[index + 2] = ((value shr 8) and 0xff).toByte()
|
||||
buffer[index + 3] = (value and 0xff).toByte()
|
||||
}
|
||||
fun setInt(index: Int, value: Int) = buffer.setInt(index, value)
|
||||
|
||||
fun setLong(index: Int, value: Long) {
|
||||
buffer[index] = ((value shr 56) and 0xff).toByte()
|
||||
buffer[index + 1] = ((value shr 48) and 0xff).toByte()
|
||||
buffer[index + 2] = ((value shr 40) and 0xff).toByte()
|
||||
buffer[index + 3] = ((value shr 32) and 0xff).toByte()
|
||||
buffer[index + 4] = ((value shr 24) and 0xff).toByte()
|
||||
buffer[index + 5] = ((value shr 16) and 0xff).toByte()
|
||||
buffer[index + 6] = ((value shr 8) and 0xff).toByte()
|
||||
buffer[index + 7] = (value and 0xff).toByte()
|
||||
}
|
||||
fun setLong(index: Int, value: Long) = buffer.setLong(index, value)
|
||||
|
||||
fun setFloat(index: Int, value: Float) = setInt(index, value.toRawBits())
|
||||
fun setDouble(index: Int, value: Double) = setLong(index, value.toRawBits())
|
||||
fun setFloat(index: Int, value: Float) = buffer.setFloat(index, value)
|
||||
fun setDouble(index: Int, value: Double) = buffer.setDouble(index, value)
|
||||
|
||||
fun setString(index: Int, value: String, maxLength: Int): Int {
|
||||
val bytes = value.encodeToByteArray()
|
||||
if (bytes.size > maxLength) {
|
||||
throw IllegalArgumentException("String is too long")
|
||||
}
|
||||
this.setShort(index, bytes.size.toShort())
|
||||
bytes.copyInto(buffer.data, index + 2)
|
||||
return index + bytes.size + 2
|
||||
}
|
||||
fun setString(index: Int, value: String, maxLength: Int) = buffer.setString(index, value, maxLength)
|
||||
|
||||
fun setClob(index: Int, value: String, maxLength: Int): Int {
|
||||
val bytes = value.encodeToByteArray()
|
||||
if (bytes.size > maxLength) {
|
||||
throw IllegalArgumentException("String is too long")
|
||||
}
|
||||
this.setInt(index, bytes.size)
|
||||
bytes.copyInto(buffer.data, index + 4)
|
||||
return index + bytes.size + 4
|
||||
}
|
||||
|
||||
fun setBlob(index: Int, bytes: ByteArray, maxLength: Int): Int {
|
||||
if (bytes.size > maxLength) {
|
||||
throw IllegalArgumentException("String is too long")
|
||||
}
|
||||
this.setInt(index, bytes.size)
|
||||
bytes.copyInto(buffer.data, index + 4)
|
||||
return index + bytes.size + 4
|
||||
}
|
||||
fun setClob(index: Int, value: String, maxLength: Int) = buffer.setClob(index, value, maxLength)
|
||||
|
||||
fun setBlob(index: Int, bytes: SlicedByteArray, maxLength: Int) = buffer.setBlob(index, bytes, maxLength)
|
||||
}
|
||||
|
||||
open class ByteArrayHandler(
|
||||
@@ -83,18 +42,9 @@ open class ByteArrayHandler(
|
||||
return buffer[index]
|
||||
}
|
||||
|
||||
fun getShort(index: Int): Short {
|
||||
return ((buffer[index].toInt() shl 8) or (buffer[index + 1].toInt() and 0xff)).toShort()
|
||||
}
|
||||
fun getShort(index: Int) = buffer.getShort(index)
|
||||
|
||||
fun getInt(index: Int): Int {
|
||||
return (
|
||||
(buffer[index].toInt() shl 24) or
|
||||
((buffer[index+1].toInt() shl 16) and 0xff0000) or
|
||||
((buffer[index+2].toInt() shl 8) and 0xff00) or
|
||||
(buffer[index+3].toInt() and 0xff)
|
||||
)
|
||||
}
|
||||
fun getInt(index: Int) = buffer.getInt(index)
|
||||
|
||||
fun getLong(index: Int): Long {
|
||||
return (
|
||||
@@ -109,26 +59,14 @@ open class ByteArrayHandler(
|
||||
)
|
||||
}
|
||||
|
||||
fun getFloat(index: Int): Float = Float.fromBits(getInt(index))
|
||||
fun getDouble(index: Int): Double = Double.fromBits(getLong(index))
|
||||
fun getFloat(index: Int): Float = buffer.getFloat(index)
|
||||
fun getDouble(index: Int): Double = buffer.getDouble(index)
|
||||
|
||||
fun getString(index: Int): String {
|
||||
val length = getShort(index)
|
||||
val str = buffer.data.copyOfRange(index + 2, index + 2 + length).decodeToString()
|
||||
return str
|
||||
}
|
||||
fun getString(index: Int) = buffer.getString(index)
|
||||
|
||||
fun getClob(index: Int): String {
|
||||
val length = getInt(index)
|
||||
val str = buffer.data.copyOfRange(index + 4, index + 4 + length).decodeToString()
|
||||
return str
|
||||
}
|
||||
fun getClob(index: Int): String = buffer.getClob(index)
|
||||
|
||||
fun getBlob(index: Int): ByteArray {
|
||||
val length = getInt(index)
|
||||
val str = buffer.data.copyOfRange(index + 4, index + 4 + length)
|
||||
return str
|
||||
}
|
||||
fun getBlob(index: Int): SlicedByteArray = buffer.getBlob(index)
|
||||
|
||||
fun slice(range: IntRange): ByteArrayHandler {
|
||||
return ByteArrayHandler(
|
||||
|
||||
@@ -117,8 +117,9 @@ class DoubleProperty(
|
||||
}
|
||||
|
||||
class StringProperty(
|
||||
name: String
|
||||
name: String,
|
||||
) : ByteArrayPropertyWithLength<String>(name) {
|
||||
|
||||
override fun getValue(thisRef: TypedByteArray, property: KProperty<*>): String {
|
||||
return thisRef.data.getString(getIndex(thisRef))
|
||||
}
|
||||
@@ -128,6 +129,24 @@ class StringProperty(
|
||||
}
|
||||
}
|
||||
|
||||
class CachedStringProperty(
|
||||
name: String,
|
||||
) : ByteArrayPropertyWithLength<String>(name) {
|
||||
var cachedValue: String? = null
|
||||
|
||||
override fun getValue(thisRef: TypedByteArray, property: KProperty<*>): String {
|
||||
if (cachedValue == null) {
|
||||
cachedValue = thisRef.data.getString(getIndex(thisRef))
|
||||
}
|
||||
return cachedValue!!
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: TypedByteArray, property: KProperty<*>, value: String) {
|
||||
thisRef.data.setString(getIndex(thisRef), value, getMaxLength(thisRef))
|
||||
cachedValue = value
|
||||
}
|
||||
}
|
||||
|
||||
class ClobProperty(
|
||||
name: String
|
||||
) : ByteArrayPropertyWithLength<String>(name) {
|
||||
@@ -142,12 +161,12 @@ class ClobProperty(
|
||||
|
||||
class BlobProperty(
|
||||
name: String
|
||||
) : ByteArrayPropertyWithLength<ByteArray>(name) {
|
||||
override fun getValue(thisRef: TypedByteArray, property: KProperty<*>): ByteArray {
|
||||
) : ByteArrayPropertyWithLength<SlicedByteArray>(name) {
|
||||
override fun getValue(thisRef: TypedByteArray, property: KProperty<*>): SlicedByteArray {
|
||||
return thisRef.data.getBlob(getIndex(thisRef))
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: TypedByteArray, property: KProperty<*>, value: ByteArray) {
|
||||
override fun setValue(thisRef: TypedByteArray, property: KProperty<*>, value: SlicedByteArray) {
|
||||
thisRef.data.setBlob(getIndex(thisRef), value, getMaxLength(thisRef))
|
||||
}
|
||||
}
|
||||
@@ -160,5 +179,6 @@ fun long(propertyName: String) = LongProperty(propertyName)
|
||||
fun float(propertyName: String) = FloatProperty(propertyName)
|
||||
fun double(propertyName: String) = DoubleProperty(propertyName)
|
||||
fun string(propertyName: String) = StringProperty(propertyName)
|
||||
fun cachedString(propertyName: String) = CachedStringProperty(propertyName)
|
||||
fun clob(propertyName: String) = ClobProperty(propertyName)
|
||||
fun blob(propertyName: String) = BlobProperty(propertyName)
|
||||
|
||||
@@ -16,15 +16,141 @@ class SlicedByteArray(
|
||||
data[offset + index] = value
|
||||
}
|
||||
|
||||
fun setShort(index: Int, value: Short) {
|
||||
check(offset + index + 1 < offset + length) { "Index out of bounds" }
|
||||
|
||||
data[offset + index] = (value.toInt() shr 8).toByte()
|
||||
data[offset + index + 1] = value.toByte()
|
||||
}
|
||||
|
||||
fun getShort(index: Int): Short {
|
||||
check(offset + index + 1 < offset + length) { "Index out of bounds" }
|
||||
|
||||
return ((data[offset + index].toInt() shl 8) or (data[offset + index + 1].toInt() and 0xff)).toShort()
|
||||
}
|
||||
|
||||
fun setInt(index: Int, value: Int) {
|
||||
check(offset + index + 3 < offset + length) { "Index out of bounds" }
|
||||
|
||||
data[offset + index] = ((value shr 24) and 0xff).toByte()
|
||||
data[offset + index + 1] = ((value shr 16) and 0xff).toByte()
|
||||
data[offset + index + 2] = ((value shr 8) and 0xff).toByte()
|
||||
data[offset + index + 3] = (value and 0xff).toByte()
|
||||
}
|
||||
|
||||
fun getInt(index: Int): Int {
|
||||
check(offset + index + 3 < offset + length) { "Index out of bounds" }
|
||||
return (
|
||||
(data[offset + index].toInt() shl 24) or
|
||||
((data[offset + index+1].toInt() shl 16) and 0xff0000) or
|
||||
((data[offset + index+2].toInt() shl 8) and 0xff00) or
|
||||
(data[offset + index+3].toInt() and 0xff)
|
||||
)
|
||||
}
|
||||
|
||||
fun setLong(index: Int, value: Long) {
|
||||
check(offset + index + 7 < offset + length) { "Index out of bounds" }
|
||||
data[offset + index] = ((value shr 56) and 0xff).toByte()
|
||||
data[offset + index + 1] = ((value shr 48) and 0xff).toByte()
|
||||
data[offset + index + 2] = ((value shr 40) and 0xff).toByte()
|
||||
data[offset + index + 3] = ((value shr 32) and 0xff).toByte()
|
||||
data[offset + index + 4] = ((value shr 24) and 0xff).toByte()
|
||||
data[offset + index + 5] = ((value shr 16) and 0xff).toByte()
|
||||
data[offset + index + 6] = ((value shr 8) and 0xff).toByte()
|
||||
data[offset + index + 7] = (value and 0xff).toByte()
|
||||
}
|
||||
|
||||
fun getLong(index: Int): Long {
|
||||
check(offset + index + 7 < offset + length) { "Index out of bounds" }
|
||||
return (
|
||||
(data[offset + index].toLong() shl 56) or
|
||||
((data[offset + index+1].toLong() and 0xff) shl 48) or
|
||||
((data[offset + index+2].toLong() and 0xff) shl 40) or
|
||||
((data[offset + index+3].toLong() and 0xff) shl 32) or
|
||||
((data[offset + index+4].toLong() and 0xff) shl 24) or
|
||||
((data[offset + index+5].toLong() and 0xff) shl 16) or
|
||||
((data[offset + index+6].toLong() and 0xff) shl 8) or
|
||||
(data[offset + index+7].toLong() and 0xff)
|
||||
)
|
||||
}
|
||||
|
||||
fun setFloat(index: Int, value: Float) = setInt(index, value.toRawBits())
|
||||
fun getFloat(index: Int): Float = Float.fromBits(getInt(index))
|
||||
|
||||
fun setDouble(index: Int, value: Double) = setLong(index, value.toRawBits())
|
||||
fun getDouble(index: Int): Double = Double.fromBits(getLong(index))
|
||||
|
||||
fun getClob(index: Int): String {
|
||||
val length = getInt(index)
|
||||
check(offset + index + length < offset + this.length) { "Index out of bounds" }
|
||||
val str = data.copyOfRange(offset + index + 4, offset + index + 4 + length).decodeToString()
|
||||
return str
|
||||
}
|
||||
|
||||
fun getBlob(index: Int): SlicedByteArray {
|
||||
val length = getInt(index)
|
||||
check(offset + index + length < offset + this.length) { "Index out of bounds" }
|
||||
val str = SlicedByteArray(data, offset + index + 4, length)
|
||||
return str
|
||||
}
|
||||
|
||||
fun copyInto(dest: ByteArray, destOffset: Int, length: Int) {
|
||||
data.copyInto(dest, destOffset, offset, offset + length)
|
||||
}
|
||||
|
||||
fun iterator(): Iterator<Byte> = object : Iterator<Byte> {
|
||||
var index = offset
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return index < offset + length
|
||||
}
|
||||
|
||||
override fun next(): Byte {
|
||||
return data[offset + index++]
|
||||
}
|
||||
}
|
||||
|
||||
fun toByteArray(): ByteArray = data.copyOfRange(offset, offset + length)
|
||||
|
||||
fun getString(index: Int): String {
|
||||
val length = getShort(offset + index)
|
||||
val str = data.decodeToString(offset + index + 2, offset + index + 2 + length.toInt())
|
||||
return str
|
||||
}
|
||||
|
||||
fun getString(start: Int, length: Int): String = data.decodeToString(offset + start, offset + start + length)
|
||||
|
||||
fun setString(index: Int, value: String, maxLength: Int) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
if (bytes.size > maxLength) {
|
||||
throw IllegalArgumentException("String is too long")
|
||||
}
|
||||
this.setShort(offset + index, bytes.size.toShort())
|
||||
bytes.copyInto(data, offset + index + 2)
|
||||
}
|
||||
|
||||
fun setClob(index: Int, value: String, maxLength: Int) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
if (offset + index + bytes.size + 4 > maxLength) {
|
||||
throw IllegalArgumentException("String is too long")
|
||||
}
|
||||
this.setInt(offset + index, bytes.size)
|
||||
bytes.copyInto(data, offset + index + 4)
|
||||
}
|
||||
|
||||
fun setBlob(index: Int, bytes: SlicedByteArray, maxLength: Int) {
|
||||
if (bytes.size > maxLength) {
|
||||
throw IllegalArgumentException("Blob is too long")
|
||||
}
|
||||
this.setInt(offset + index, bytes.size)
|
||||
bytes.copyInto(data, offset + index + 4, maxLength - 4)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun wrap(
|
||||
data: ByteArray,
|
||||
offset: Int,
|
||||
length: Int
|
||||
offset: Int = 0,
|
||||
length: Int = data.size
|
||||
): SlicedByteArray {
|
||||
return SlicedByteArray(data, offset,length)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class TypedByteArrayWithByteArrayTest {
|
||||
this.name = name
|
||||
}
|
||||
|
||||
constructor(data: ByteArray): this() {
|
||||
constructor(data: SlicedByteArray): this() {
|
||||
this.data = MutableByteArrayHandler(buffer = data)
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class TypedByteArrayWithByteArrayTest {
|
||||
|
||||
constructor(name: String, person: Person1): this() {
|
||||
this.name = name
|
||||
this.personData = person.data.buffer.data
|
||||
this.personData = person.data.buffer
|
||||
}
|
||||
|
||||
constructor(data: ByteArray): this() {
|
||||
@@ -47,8 +47,40 @@ class TypedByteArrayWithByteArrayTest {
|
||||
|
||||
assertEquals("A Name", company.person.name)
|
||||
|
||||
company.person.name = "2nd Name"
|
||||
//company.person.name = "2nd Name"
|
||||
|
||||
assertEquals("2nd Name", company.person.name)
|
||||
//assertEquals("2nd Name", company.person.name)
|
||||
}
|
||||
|
||||
class Data() : TypedByteArray(
|
||||
Type("name", DataType.STRING, 10),
|
||||
Type("binaryData", DataType.BLOB, 10)
|
||||
) {
|
||||
var name by string("name")
|
||||
var binaryData by blob("binaryData")
|
||||
|
||||
constructor(name: String, data: SlicedByteArray): this() {
|
||||
this.name = name
|
||||
this.binaryData = data
|
||||
}
|
||||
|
||||
constructor(data: ByteArray): this() {
|
||||
this.data = MutableByteArrayHandler(buffer = data)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBlob() {
|
||||
val binaryData = ByteArray(10)
|
||||
for(index in 0..<10) {
|
||||
binaryData[index] = index.toByte()
|
||||
}
|
||||
val data = Data("A Name", SlicedByteArray.wrap(binaryData))
|
||||
|
||||
assertEquals(1, data.data[17])
|
||||
|
||||
data.binaryData[0] = 100
|
||||
|
||||
assertEquals(100, data.data[16])
|
||||
}
|
||||
}
|
||||
|
||||
52
src/jvmTest/kotlin/nl/astraeus/tba/TestCachedString.kt
Normal file
52
src/jvmTest/kotlin/nl/astraeus/tba/TestCachedString.kt
Normal file
@@ -0,0 +1,52 @@
|
||||
package nl.astraeus.tba
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
class TestCachedString {
|
||||
|
||||
class PersonCached : TypedByteArray(
|
||||
Type("name", DataType.STRING, 100),
|
||||
) {
|
||||
var name by cachedString("name")
|
||||
}
|
||||
|
||||
class PersonNotCached : TypedByteArray(
|
||||
Type("name", DataType.STRING, 100),
|
||||
) {
|
||||
var name by string("name")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCachedString() {
|
||||
val cachedPerson = PersonCached()
|
||||
val notCachedPerson = PersonNotCached()
|
||||
|
||||
cachedPerson.name = "A Longer Longer Longer Longer Longer Test"
|
||||
notCachedPerson.name = "A Longer Longer Longer Longer Longer Test"
|
||||
|
||||
var total = 0
|
||||
repeat(1000000) {
|
||||
total += cachedPerson.name.hashCode()
|
||||
total += notCachedPerson.name.hashCode()
|
||||
}
|
||||
|
||||
println("Total warmup: $total")
|
||||
|
||||
var start1 = System.nanoTime()
|
||||
var total1 = 0
|
||||
repeat(1000000) {
|
||||
total1 += cachedPerson.name.hashCode()
|
||||
}
|
||||
val stop1 = System.nanoTime()
|
||||
|
||||
var start2 = System.nanoTime()
|
||||
var total2 = 0
|
||||
repeat(1000000) {
|
||||
total2 += notCachedPerson.name.hashCode()
|
||||
}
|
||||
val stop2 = System.nanoTime()
|
||||
|
||||
println("Total1 ($total1) time: ${(stop1 - start1) / 1000000f}ms")
|
||||
println("Total2 ($total2) time: ${(stop2 - start2) / 1000000f}ms")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user