Skip to content

Commit

Permalink
Added API for reading and writing of floating point numbers
Browse files Browse the repository at this point in the history
Implemented extension functions for reading and writing  values with types Float and Double

Resolves #167
  • Loading branch information
shanshin committed Aug 1, 2023
1 parent cbeb279 commit fcd1943
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 0 deletions.
64 changes: 64 additions & 0 deletions core/common/src/Sinks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,70 @@ public fun Sink.writeULongLe(long: ULong) {
writeLongLe(long.toLong())
}

/**
* Writes four bytes of a bit representation of [float], in the big-endian order, to this sink.
* Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout.
*
* To obtain a bit representation, the [Float.toBits] function is used.
*
* @param float the floating point number to be written.
*
* @throws IllegalStateException when the sink is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloat
*/
public fun Sink.writeFloat(float: Float) {
writeInt(float.toBits())
}

/**
* Writes eight bytes of a bit representation of [double], in the big-endian order, to this sink.
* Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout.
*
* To obtain a bit representation, the [Double.toBits] function is used.
*
* @param double the floating point number to be written.
*
* @throws IllegalStateException when the sink is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDouble
*/
public fun Sink.writeDouble(double: Double) {
writeLong(double.toBits())
}

/**
* Writes four bytes of a bit representation of [float], in the little-endian order, to this sink.
* Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout.
*
* To obtain a bit representation, the [Float.toBits] function is used.
*
* @param float the floating point number to be written.
*
* @throws IllegalStateException when the sink is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloatLe
*/
public fun Sink.writeFloatLe(float: Float) {
writeIntLe(float.toBits())
}

/**
* Writes eight bytes of a bit representation of [double], in the little-endian order, to this sink.
* Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout.
*
* To obtain a bit representation, the [Double.toBits] function is used.
*
* @param double the floating point number to be written.
*
* @throws IllegalStateException when the sink is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDoubleLe
*/
public fun Sink.writeDoubleLe(double: Double) {
writeLongLe(double.toBits())
}

/**
* Provides direct access to the sink's internal buffer and hints its emit before exit.
*
Expand Down
52 changes: 52 additions & 0 deletions core/common/src/Sources.kt
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,58 @@ public fun Source.readUIntLe(): UInt = readIntLe().toUInt()
*/
public fun Source.readULongLe(): ULong = readLongLe().toULong()

/**
* Removes four bytes from this source and returns a floating point number with type [Float] composed of it
* according to the big-endian order.
*
* The [Float.Companion.fromBits] function is used for decoding bytes into [Float].
*
* @throws EOFException when there are not enough data to read an unsigned int value.
* @throws IllegalStateException when the source is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloat
*/
public fun Source.readFloat(): Float = Float.fromBits(readInt())

/**
* Removes eight bytes from this source and returns a floating point number with type [Double] composed of it
* according to the big-endian order.
*
* The [Double.Companion.fromBits] function is used for decoding bytes into [Double].
*
* @throws EOFException when there are not enough data to read an unsigned int value.
* @throws IllegalStateException when the source is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDouble
*/
public fun Source.readDouble(): Double = Double.fromBits(readLong())

/**
* Removes four bytes from this source and returns a floating point number with type [Float] composed of it
* according to the little-endian order.
*
* The [Float.Companion.fromBits] function is used for decoding bytes into [Float].
*
* @throws EOFException when there are not enough data to read an unsigned int value.
* @throws IllegalStateException when the source is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloatLe
*/
public fun Source.readFloatLe(): Float = Float.fromBits(readIntLe())

/**
* Removes eight bytes from this source and returns a floating point number with type [Double] composed of it
* according to the little-endian order.
*
* The [Double.Companion.fromBits] function is used for decoding bytes into [Double].
*
* @throws EOFException when there are not enough data to read an unsigned int value.
* @throws IllegalStateException when the source is closed.
*
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDoubleLe
*/
public fun Source.readDoubleLe(): Double = Double.fromBits(readLongLe())

/**
* Return `true` if the next byte to be consumed from this source is equal to [byte].
* Otherwise, return `false` as well as when the source is exhausted.
Expand Down
28 changes: 28 additions & 0 deletions core/common/test/AbstractSinkTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,34 @@ abstract class AbstractSinkTest internal constructor(
assertEquals("Buffer(size=8 hex=efcdab9078563412)", data.toString())
}

@Test
fun writeFloat() {
sink.writeFloat(12345.678F)
sink.flush()
assertEquals(12345.678F.toBits(), data.readInt())
}

@Test
fun writeFloatLe() {
sink.writeFloatLe(12345.678F)
sink.flush()
assertEquals(12345.678F.toBits(), data.readIntLe())
}

@Test
fun writeDouble() {
sink.writeDouble(123456.78901)
sink.flush()
assertEquals(123456.78901.toBits(), data.readLong())
}

@Test
fun writeDoubleLe() {
sink.writeDoubleLe(123456.78901)
sink.flush()
assertEquals(123456.78901.toBits(), data.readLongLe())
}

@Test
fun writeByteString() {
sink.write("təˈranəˌsôr".encodeToByteString())
Expand Down
64 changes: 64 additions & 0 deletions core/common/test/AbstractSourceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,70 @@ abstract class AbstractBufferedSourceTest internal constructor(
assertEquals(0x78563412u, source.readUIntLe())
}

@Test
fun readFloat() {
sink.write(byteArrayOf(70, 64, -26, -74))
sink.flush()
assertEquals(12345.678F.toBits(), source.readFloat().toBits())
}

@Test
fun readDouble() {
sink.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35))
sink.flush()
assertEquals(123456.78901, source.readDouble())
}

@Test
fun readFloatLe() {
sink.write(byteArrayOf(-74, -26, 64, 70))
sink.flush()
assertEquals(12345.678F.toBits(), source.readFloatLe().toBits())
}

@Test
fun readDoubleLe() {
sink.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64))
sink.flush()
assertEquals(123456.78901, source.readDoubleLe())
}

@Test
fun readTooShortFloatThrows() {
assertFailsWith<EOFException> { source.readFloat() }
sink.writeByte(0)
sink.flush()
assertFailsWith<EOFException> { source.readFloat() }
assertTrue(source.request(1))
}

@Test
fun readTooShortDoubleThrows() {
assertFailsWith<EOFException> { source.readDouble() }
sink.writeByte(0)
sink.flush()
assertFailsWith<EOFException> { source.readDouble() }
assertTrue(source.request(1))
}

@Test
fun readTooShortFloatLeThrows() {
assertFailsWith<EOFException> { source.readFloatLe() }
sink.writeByte(0)
sink.flush()
assertFailsWith<EOFException> { source.readFloatLe() }
assertTrue(source.request(1))
}

@Test
fun readTooShortDoubleLeThrows() {
assertFailsWith<EOFException> { source.readDoubleLe() }
sink.writeByte(0)
sink.flush()
assertFailsWith<EOFException> { source.readDoubleLe() }
assertTrue(source.request(1))
}

@Test
fun readTooShortUnsignedIntThrows() {
assertFailsWith<EOFException> { source.readUInt() }
Expand Down
61 changes: 61 additions & 0 deletions core/common/test/samples/samples.kt
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,21 @@ class KotlinxIoCoreCommonSamples {
assertEquals(18446744073709551615UL, buffer.readULong())
}

@Test
fun readFloat() {
val buffer = Buffer()
buffer.write(byteArrayOf(70, 64, -26, -74))
assertEquals(12345.678F.toBits(), buffer.readFloat().toBits())
}

@Test
fun readDouble() {
val buffer = Buffer()
buffer.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35))

assertEquals(123456.78901, buffer.readDouble())
}

@Test
fun writeUByte() {
val buffer = Buffer()
Expand Down Expand Up @@ -520,6 +535,22 @@ class KotlinxIoCoreCommonSamples {
assertContentEquals(byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1), buffer.readByteArray())
}

@Test
fun writeFloat() {
val buffer = Buffer()
buffer.writeFloat(12345.678F)

assertContentEquals(byteArrayOf(70, 64, -26, -74), buffer.readByteArray())
}

@Test
fun writeDouble() {
val buffer = Buffer()
buffer.writeDouble(123456.78901)

assertContentEquals(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35), buffer.readByteArray())
}

@Test
fun flush() {
val rawSink = object : RawSink {
Expand Down Expand Up @@ -650,6 +681,20 @@ class KotlinxIoCoreCommonSamples {
assertEquals(0xF0DEBC9A78563412U, buffer.readULongLe())
}

@Test
fun readFloatLe() {
val buffer = Buffer()
buffer.write(byteArrayOf(-74, -26, 64, 70))
assertEquals(12345.678F.toBits(), buffer.readFloatLe().toBits())
}

@Test
fun readDoubleLe() {
val buffer = Buffer()
buffer.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64))
assertEquals(123456.78901, buffer.readDoubleLe())
}

@Test
fun writeUShortLe() {
val buffer = Buffer()
Expand All @@ -670,4 +715,20 @@ class KotlinxIoCoreCommonSamples {
buffer.writeULongLe(0x123456789ABCDEF0U)
assertEquals(0xF0DEBC9A78563412U, buffer.readULong())
}

@Test
fun writeFloatLe() {
val buffer = Buffer()
buffer.writeFloatLe(12345.678F)

assertContentEquals(byteArrayOf(-74, -26, 64, 70), buffer.readByteArray())
}

@Test
fun writeDoubleLe() {
val buffer = Buffer()
buffer.writeDoubleLe(123456.78901)

assertContentEquals(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64), buffer.readByteArray())
}
}

0 comments on commit fcd1943

Please sign in to comment.