Add tests of kmath-memory and make all the memory operations on all the platforms use little-endian byte order #163

Closed
CommanderTvis wants to merge 4 commits from unify-endianness into dev
5 changed files with 109 additions and 19 deletions
Showing only changes of commit 491e2ac7a6 - Show all commits

View File

@ -57,6 +57,7 @@ public interface MemoryReader {
/** /**
* Reads [Float] at certain [offset]. * Reads [Float] at certain [offset].
*/ */
@Deprecated("readFloat and writeFloat functions work unexpectedly on JS platform, since because their results are widened when used. Consider using readDouble and writeDouble.")
public fun readFloat(offset: Int): Float public fun readFloat(offset: Int): Float
/** /**
@ -113,6 +114,7 @@ public interface MemoryWriter {
/** /**
* Writes [Float] at certain [offset]. * Writes [Float] at certain [offset].
*/ */
@Deprecated("readFloat and writeFloat functions work unexpectedly on JS platform, since because their results are widened when used. Consider using readDouble and writeDouble.")
public fun writeFloat(offset: Int, value: Float) public fun writeFloat(offset: Int, value: Float)
/** /**

View File

@ -0,0 +1,88 @@
package kscience.kmath.memory
import kotlin.test.Test
import kotlin.test.assertEquals
internal class MemoryTest {
@Test
fun allocateReturnsMemoryWithValidSize() {
val mem = Memory.allocate(64)
assertEquals(64, mem.size)
}
@Test
fun rwByte() {
val mem = Memory.allocate(64)
mem.write { writeByte(0, 42.toByte()) }
assertEquals(42.toByte(), mem.read { readByte(0) })
mem.write { writeByte(1, (-4).toByte()) }
assertEquals((-4).toByte(), mem.read { readByte(1) })
}
@Test
fun rwShort() {
val mem = Memory.allocate(64)
mem.write { writeShort(0, 44555.toShort()) }
assertEquals(44555.toShort(), mem.read { readShort(0) })
mem.write { writeShort(2, (-33333).toShort()) }
assertEquals((-33333).toShort(), mem.read { readShort(2) })
}
@Test
fun rwInt() {
val mem = Memory.allocate(64)
mem.write { writeInt(0, 1234444444) }
assertEquals(1234444444, mem.read { readInt(0) })
mem.write { writeInt(4, -5595959) }
assertEquals(-5595959, mem.read { readInt(4) })
}
@Test
fun rwLong() {
val mem = Memory.allocate(64)
mem.write { writeLong(0, 1234444444L) }
assertEquals(1234444444L, mem.read { readLong(0) })
mem.write { writeLong(4, -5595959L) }
assertEquals(-5595959L, mem.read { readLong(4) })
mem.write { writeLong(8, 1234444444444149L) }
assertEquals(1234444444444149L, mem.read { readLong(8) })
mem.write { writeLong(16, -50000333595959L) }
assertEquals(-50000333595959L, mem.read { readLong(16) })
}
@Suppress("DEPRECATION")
@Test
fun rwFloat() {
val mem = Memory.allocate(64)
mem.write { writeFloat(0, 12.12345f) }
println(1)
assertEquals(12.12345027923584, mem.read { readFloat(0) }.toDouble())
mem.write { writeFloat(4, -313.13f) }
println(2)
assertEquals(-313.1300048828125, mem.read { readFloat(4) }.toDouble())
mem.write { writeFloat(8, Float.NaN) }
println(3)
assertEquals(Float.NaN, mem.read { readFloat(8) })
mem.write { writeFloat(12, Float.POSITIVE_INFINITY) }
println(4)
assertEquals(Float.POSITIVE_INFINITY, mem.read { readFloat(12) })
mem.write { writeFloat(12, Float.NEGATIVE_INFINITY) }
println(5)
assertEquals(Float.NEGATIVE_INFINITY, mem.read { readFloat(12) })
}
@Test
fun rwDouble() {
val mem = Memory.allocate(64)
mem.write { writeDouble(0, 12.12345) }
assertEquals(12.12345, mem.read { readDouble(0) })
mem.write { writeDouble(8, -313.13) }
assertEquals(-313.13, mem.read { readDouble(8) })
mem.write { writeDouble(16, Double.NaN) }
assertEquals(Double.NaN, mem.read { readDouble(16) })
mem.write { writeDouble(24, Double.POSITIVE_INFINITY) }
assertEquals(Double.POSITIVE_INFINITY, mem.read { readDouble(24) })
mem.write { writeDouble(32, Double.NEGATIVE_INFINITY) }
assertEquals(Double.NEGATIVE_INFINITY, mem.read { readDouble(32) })
}
}

View File

@ -28,18 +28,18 @@ private class DataViewMemory(val view: DataView) : Memory {
private val reader: MemoryReader = object : MemoryReader { private val reader: MemoryReader = object : MemoryReader {
override val memory: Memory get() = this@DataViewMemory override val memory: Memory get() = this@DataViewMemory
override fun readDouble(offset: Int): Double = view.getFloat64(offset, false) override fun readDouble(offset: Int): Double = view.getFloat64(offset, true)
override fun readFloat(offset: Int): Float = view.getFloat32(offset, false) override fun readFloat(offset: Int): Float = view.getFloat32(offset, true)
override fun readByte(offset: Int): Byte = view.getInt8(offset) override fun readByte(offset: Int): Byte = view.getInt8(offset)
override fun readShort(offset: Int): Short = view.getInt16(offset, false) override fun readShort(offset: Int): Short = view.getInt16(offset, true)
override fun readInt(offset: Int): Int = view.getInt32(offset, false) override fun readInt(offset: Int): Int = view.getInt32(offset, true)
override fun readLong(offset: Int): Long = override fun readLong(offset: Int): Long =
view.getInt32(offset, false).toLong() shl 32 or view.getInt32(offset + 4, false).toLong() view.getInt32(offset, true).toLong() shl 32 or view.getInt32(offset + 4, true).toLong()
override fun release() { override fun release() {
// does nothing on JS // does nothing on JS
@ -52,11 +52,11 @@ private class DataViewMemory(val view: DataView) : Memory {
override val memory: Memory get() = this@DataViewMemory override val memory: Memory get() = this@DataViewMemory
override fun writeDouble(offset: Int, value: Double) { override fun writeDouble(offset: Int, value: Double) {
view.setFloat64(offset, value, false) view.setFloat64(offset, value, true)
} }
override fun writeFloat(offset: Int, value: Float) { override fun writeFloat(offset: Int, value: Float) {
view.setFloat32(offset, value, false) view.setFloat32(offset, value, true)
} }
override fun writeByte(offset: Int, value: Byte) { override fun writeByte(offset: Int, value: Byte) {
@ -64,16 +64,16 @@ private class DataViewMemory(val view: DataView) : Memory {
} }
override fun writeShort(offset: Int, value: Short) { override fun writeShort(offset: Int, value: Short) {
view.setUint16(offset, value, false) view.setUint16(offset, value, true)
} }
override fun writeInt(offset: Int, value: Int) { override fun writeInt(offset: Int, value: Int) {
view.setInt32(offset, value, false) view.setInt32(offset, value, true)
} }
override fun writeLong(offset: Int, value: Long) { override fun writeLong(offset: Int, value: Long) {
view.setInt32(offset, (value shr 32).toInt(), littleEndian = false) view.setInt32(offset, (value shr 32).toInt(), littleEndian = true)
view.setInt32(offset + 4, (value and 0xffffffffL).toInt(), littleEndian = false) view.setInt32(offset + 4, (value and 0xffffffffL).toInt(), littleEndian = true)
} }
override fun release() { override fun release() {
@ -82,7 +82,6 @@ private class DataViewMemory(val view: DataView) : Memory {
} }
override fun writer(): MemoryWriter = writer override fun writer(): MemoryWriter = writer
} }
/** /**
@ -98,6 +97,6 @@ public actual fun Memory.Companion.allocate(length: Int): Memory {
* and could be mutated independently from the resulting [Memory]. * and could be mutated independently from the resulting [Memory].
*/ */
public actual fun Memory.Companion.wrap(array: ByteArray): Memory { public actual fun Memory.Companion.wrap(array: ByteArray): Memory {
@Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array val int8Array = array.unsafeCast<Int8Array>()
return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length))
} }

View File

@ -7,6 +7,7 @@ package space.kscience.kmath.memory
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.FileChannel import java.nio.channels.FileChannel
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -31,7 +32,7 @@ internal class ByteBufferMemory(
} }
override fun copy(): Memory { override fun copy(): Memory {
val copy = ByteBuffer.allocate(buffer.capacity()) val copy = ByteBuffer.allocate(buffer.capacity()).order(ByteOrder.LITTLE_ENDIAN)
buffer.rewind() buffer.rewind()
copy.put(buffer) copy.put(buffer)
copy.flip() copy.flip()
@ -99,7 +100,7 @@ internal class ByteBufferMemory(
* Allocates memory based on a [ByteBuffer]. * Allocates memory based on a [ByteBuffer].
*/ */
public actual fun Memory.Companion.allocate(length: Int): Memory = public actual fun Memory.Companion.allocate(length: Int): Memory =
ByteBufferMemory(checkNotNull(ByteBuffer.allocate(length))) ByteBufferMemory(checkNotNull(ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN)))
/** /**
* Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied

View File

@ -42,7 +42,7 @@ internal class NativeMemory(
override fun readLong(offset: Int) = array.getLongAt(position(offset)) override fun readLong(offset: Int) = array.getLongAt(position(offset))
override fun release() { override fun release() {
// does nothing on JVM // does nothing on Native
} }
} }
@ -60,7 +60,7 @@ internal class NativeMemory(
} }
override fun writeByte(offset: Int, value: Byte) { override fun writeByte(offset: Int, value: Byte) {
array.set(position(offset), value) array[position(offset)] = value
} }
override fun writeShort(offset: Int, value: Short) { override fun writeShort(offset: Int, value: Short) {
@ -76,7 +76,7 @@ internal class NativeMemory(
} }
override fun release() { override fun release() {
// does nothing on JVM // does nothing on Native
} }
} }